// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chromeos/dbus/fake_bluetooth_device_client.h" #include #include #include #include #include #include #include #include "base/location.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/rand_util.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/thread_task_runner_handle.h" #include "base/threading/worker_pool.h" #include "base/time/time.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/fake_bluetooth_adapter_client.h" #include "chromeos/dbus/fake_bluetooth_agent_manager_client.h" #include "chromeos/dbus/fake_bluetooth_agent_service_provider.h" #include "chromeos/dbus/fake_bluetooth_gatt_service_client.h" #include "chromeos/dbus/fake_bluetooth_input_client.h" #include "chromeos/dbus/fake_bluetooth_profile_manager_client.h" #include "chromeos/dbus/fake_bluetooth_profile_service_provider.h" #include "dbus/file_descriptor.h" #include "third_party/cros_system_api/dbus/service_constants.h" namespace { // Default interval between simulated events. const int kSimulationIntervalMs = 750; // Minimum and maximum bounds for randomly generated RSSI values. const int kMinRSSI = -90; const int kMaxRSSI = -30; // The default value of connection info properties from GetConnInfo(). const int kUnkownPower = 127; // This is meant to delay the removal of a pre defined device until the // developer has time to see it. const int kVanishingDevicePairTimeMultiplier = 4; // Meant to delay a pair request for an observable amount of time. const int kIncomingSimulationPairTimeMultiplier = 45; // Meant to delay a request that asks for pair requests for an observable // amount of time. const int kIncomingSimulationStartPairTimeMultiplier = 30; // This allows the PIN code dialog to be shown for a long enough time to see // the PIN code UI in detail. const int kPinCodeDevicePairTimeMultiplier = 7; // This allows the pairing dialog to be shown for a long enough time to see // its UI in detail. const int kSimulateNormalPairTimeMultiplier = 3; void SimulatedProfileSocket(int fd) { // Simulate a server-side socket of a profile; read data from the socket, // write it back, and then close. char buf[1024]; ssize_t len; ssize_t count; len = read(fd, buf, sizeof buf); if (len < 0) { close(fd); return; } count = len; len = write(fd, buf, count); if (len < 0) { close(fd); return; } close(fd); } void SimpleErrorCallback(const std::string& error_name, const std::string& error_message) { VLOG(1) << "Bluetooth Error: " << error_name << ": " << error_message; } } // namespace namespace chromeos { const char FakeBluetoothDeviceClient::kTestPinCode[] = "123456"; const int FakeBluetoothDeviceClient::kTestPassKey = 123456; const char FakeBluetoothDeviceClient::kPairingMethodNone[] = "None"; const char FakeBluetoothDeviceClient::kPairingMethodPinCode[] = "PIN Code"; const char FakeBluetoothDeviceClient::kPairingMethodPassKey[] = "PassKey"; const char FakeBluetoothDeviceClient::kPairingActionConfirmation[] = "Confirmation"; const char FakeBluetoothDeviceClient::kPairingActionDisplay[] = "Display"; const char FakeBluetoothDeviceClient::kPairingActionFail[] = "Fail"; const char FakeBluetoothDeviceClient::kPairingActionRequest[] = "Request"; const char FakeBluetoothDeviceClient::kPairedDevicePath[] = "/fake/hci0/dev0"; const char FakeBluetoothDeviceClient::kPairedDeviceAddress[] = "00:11:22:33:44:55"; const char FakeBluetoothDeviceClient::kPairedDeviceName[] = "Fake Device"; const uint32 FakeBluetoothDeviceClient::kPairedDeviceClass = 0x000104; const char FakeBluetoothDeviceClient::kLegacyAutopairPath[] = "/fake/hci0/dev1"; const char FakeBluetoothDeviceClient::kLegacyAutopairAddress[] = "28:CF:DA:00:00:00"; const char FakeBluetoothDeviceClient::kLegacyAutopairName[] = "Bluetooth 2.0 Mouse"; const uint32 FakeBluetoothDeviceClient::kLegacyAutopairClass = 0x002580; const char FakeBluetoothDeviceClient::kDisplayPinCodePath[] = "/fake/hci0/dev2"; const char FakeBluetoothDeviceClient::kDisplayPinCodeAddress[] = "28:37:37:00:00:00"; const char FakeBluetoothDeviceClient::kDisplayPinCodeName[] = "Bluetooth 2.0 Keyboard"; const uint32 FakeBluetoothDeviceClient::kDisplayPinCodeClass = 0x002540; const char FakeBluetoothDeviceClient::kVanishingDevicePath[] = "/fake/hci0/dev3"; const char FakeBluetoothDeviceClient::kVanishingDeviceAddress[] = "01:02:03:04:05:06"; const char FakeBluetoothDeviceClient::kVanishingDeviceName[] = "Vanishing Device"; const uint32 FakeBluetoothDeviceClient::kVanishingDeviceClass = 0x000104; const char FakeBluetoothDeviceClient::kConnectUnpairablePath[] = "/fake/hci0/dev4"; const char FakeBluetoothDeviceClient::kConnectUnpairableAddress[] = "7C:ED:8D:00:00:00"; const char FakeBluetoothDeviceClient::kConnectUnpairableName[] = "Unpairable Device"; const uint32 FakeBluetoothDeviceClient::kConnectUnpairableClass = 0x002580; const char FakeBluetoothDeviceClient::kDisplayPasskeyPath[] = "/fake/hci0/dev5"; const char FakeBluetoothDeviceClient::kDisplayPasskeyAddress[] = "00:0F:F6:00:00:00"; const char FakeBluetoothDeviceClient::kDisplayPasskeyName[] = "Bluetooth 2.1+ Keyboard"; const uint32 FakeBluetoothDeviceClient::kDisplayPasskeyClass = 0x002540; const char FakeBluetoothDeviceClient::kRequestPinCodePath[] = "/fake/hci0/dev6"; const char FakeBluetoothDeviceClient::kRequestPinCodeAddress[] = "00:24:BE:00:00:00"; const char FakeBluetoothDeviceClient::kRequestPinCodeName[] = "PIN Device"; const uint32 FakeBluetoothDeviceClient::kRequestPinCodeClass = 0x240408; const char FakeBluetoothDeviceClient::kConfirmPasskeyPath[] = "/fake/hci0/dev7"; const char FakeBluetoothDeviceClient::kConfirmPasskeyAddress[] = "20:7D:74:00:00:00"; const char FakeBluetoothDeviceClient::kConfirmPasskeyName[] = "Phone"; const uint32 FakeBluetoothDeviceClient::kConfirmPasskeyClass = 0x7a020c; const char FakeBluetoothDeviceClient::kRequestPasskeyPath[] = "/fake/hci0/dev8"; const char FakeBluetoothDeviceClient::kRequestPasskeyAddress[] = "20:7D:74:00:00:01"; const char FakeBluetoothDeviceClient::kRequestPasskeyName[] = "Passkey Device"; const uint32 FakeBluetoothDeviceClient::kRequestPasskeyClass = 0x7a020c; const char FakeBluetoothDeviceClient::kUnconnectableDevicePath[] = "/fake/hci0/dev9"; const char FakeBluetoothDeviceClient::kUnconnectableDeviceAddress[] = "20:7D:74:00:00:02"; const char FakeBluetoothDeviceClient::kUnconnectableDeviceName[] = "Unconnectable Device"; const uint32 FakeBluetoothDeviceClient::kUnconnectableDeviceClass = 0x7a020c; const char FakeBluetoothDeviceClient::kUnpairableDevicePath[] = "/fake/hci0/devA"; const char FakeBluetoothDeviceClient::kUnpairableDeviceAddress[] = "20:7D:74:00:00:03"; const char FakeBluetoothDeviceClient::kUnpairableDeviceName[] = "Unpairable Device"; const uint32 FakeBluetoothDeviceClient::kUnpairableDeviceClass = 0x002540; const char FakeBluetoothDeviceClient::kJustWorksPath[] = "/fake/hci0/devB"; const char FakeBluetoothDeviceClient::kJustWorksAddress[] = "00:0C:8A:00:00:00"; const char FakeBluetoothDeviceClient::kJustWorksName[] = "Just-Works Device"; const uint32 FakeBluetoothDeviceClient::kJustWorksClass = 0x240428; const char FakeBluetoothDeviceClient::kLowEnergyPath[] = "/fake/hci0/devC"; const char FakeBluetoothDeviceClient::kLowEnergyAddress[] = "00:1A:11:00:15:30"; const char FakeBluetoothDeviceClient::kLowEnergyName[] = "Bluetooth 4.0 Heart Rate Monitor"; const uint32 FakeBluetoothDeviceClient::kLowEnergyClass = 0x000918; // Major class "Health", Minor class "Heart/Pulse Rate Monitor." const char FakeBluetoothDeviceClient::kPairedUnconnectableDevicePath[] = "/fake/hci0/devD"; const char FakeBluetoothDeviceClient::kPairedUnconnectableDeviceAddress[] = "20:7D:74:00:00:04"; const char FakeBluetoothDeviceClient::kPairedUnconnectableDeviceName[] = "Paired Unconnectable Device"; const uint32 FakeBluetoothDeviceClient::kPairedUnconnectableDeviceClass = 0x000104; const char FakeBluetoothDeviceClient::kConnectedTrustedNotPairedDevicePath[] = "/fake/hci0/devE"; const char FakeBluetoothDeviceClient::kConnectedTrustedNotPairedDeviceAddress[] = "11:22:33:44:55:66"; const char FakeBluetoothDeviceClient::kConnectedTrustedNotPairedDeviceName[] = "Connected Pairable Device"; const uint32 FakeBluetoothDeviceClient::kConnectedTrustedNotPairedDeviceClass = 0x7a020c; FakeBluetoothDeviceClient::Properties::Properties( const PropertyChangedCallback& callback) : BluetoothDeviceClient::Properties( NULL, bluetooth_device::kBluetoothDeviceInterface, callback) {} FakeBluetoothDeviceClient::Properties::~Properties() {} void FakeBluetoothDeviceClient::Properties::Get( dbus::PropertyBase* property, dbus::PropertySet::GetCallback callback) { VLOG(1) << "Get " << property->name(); callback.Run(false); } void FakeBluetoothDeviceClient::Properties::GetAll() { VLOG(1) << "GetAll"; } void FakeBluetoothDeviceClient::Properties::Set( dbus::PropertyBase* property, dbus::PropertySet::SetCallback callback) { VLOG(1) << "Set " << property->name(); if (property->name() == trusted.name()) { callback.Run(true); property->ReplaceValueWithSetValue(); } else { callback.Run(false); } } FakeBluetoothDeviceClient::SimulatedPairingOptions::SimulatedPairingOptions() {} FakeBluetoothDeviceClient::SimulatedPairingOptions::~SimulatedPairingOptions() { } FakeBluetoothDeviceClient::IncomingDeviceProperties:: IncomingDeviceProperties() {} FakeBluetoothDeviceClient::IncomingDeviceProperties:: ~IncomingDeviceProperties() {} FakeBluetoothDeviceClient::FakeBluetoothDeviceClient() : simulation_interval_ms_(kSimulationIntervalMs), discovery_simulation_step_(0), incoming_pairing_simulation_step_(0), pairing_cancelled_(false), connection_rssi_(kUnkownPower), transmit_power_(kUnkownPower), max_transmit_power_(kUnkownPower) { scoped_ptr properties(new Properties( base::Bind(&FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), dbus::ObjectPath(kPairedDevicePath)))); properties->address.ReplaceValue(kPairedDeviceAddress); properties->bluetooth_class.ReplaceValue(kPairedDeviceClass); properties->name.ReplaceValue("Fake Device (Name)"); properties->alias.ReplaceValue(kPairedDeviceName); properties->paired.ReplaceValue(true); properties->trusted.ReplaceValue(true); properties->adapter.ReplaceValue( dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); std::vector uuids; uuids.push_back("00001800-0000-1000-8000-00805f9b34fb"); uuids.push_back("00001801-0000-1000-8000-00805f9b34fb"); properties->uuids.ReplaceValue(uuids); properties->modalias.ReplaceValue("usb:v05ACp030Dd0306"); properties_map_.insert(dbus::ObjectPath(kPairedDevicePath), properties.Pass()); device_list_.push_back(dbus::ObjectPath(kPairedDevicePath)); properties.reset(new Properties(base::Bind( &FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), dbus::ObjectPath(kPairedUnconnectableDevicePath)))); properties->address.ReplaceValue(kPairedUnconnectableDeviceAddress); properties->bluetooth_class.ReplaceValue(kPairedUnconnectableDeviceClass); properties->name.ReplaceValue("Fake Device 2 (Unconnectable)"); properties->alias.ReplaceValue(kPairedUnconnectableDeviceName); properties->paired.ReplaceValue(true); properties->trusted.ReplaceValue(true); properties->adapter.ReplaceValue( dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); properties->uuids.ReplaceValue(uuids); properties->modalias.ReplaceValue("usb:v05ACp030Dd0306"); properties_map_.insert(dbus::ObjectPath(kPairedUnconnectableDevicePath), properties.Pass()); device_list_.push_back(dbus::ObjectPath(kPairedUnconnectableDevicePath)); } FakeBluetoothDeviceClient::~FakeBluetoothDeviceClient() {} void FakeBluetoothDeviceClient::Init(dbus::Bus* bus) {} void FakeBluetoothDeviceClient::AddObserver(Observer* observer) { observers_.AddObserver(observer); } void FakeBluetoothDeviceClient::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } std::vector FakeBluetoothDeviceClient::GetDevicesForAdapter( const dbus::ObjectPath& adapter_path) { if (adapter_path == dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)) return device_list_; else return std::vector(); } FakeBluetoothDeviceClient::Properties* FakeBluetoothDeviceClient::GetProperties( const dbus::ObjectPath& object_path) { PropertiesMap::const_iterator iter = properties_map_.find(object_path); if (iter != properties_map_.end()) return iter->second; return NULL; } FakeBluetoothDeviceClient::SimulatedPairingOptions* FakeBluetoothDeviceClient::GetPairingOptions( const dbus::ObjectPath& object_path) { PairingOptionsMap::const_iterator iter = pairing_options_map_.find(object_path); if (iter != pairing_options_map_.end()) return iter->second; return iter != pairing_options_map_.end() ? iter->second : nullptr; } void FakeBluetoothDeviceClient::Connect(const dbus::ObjectPath& object_path, const base::Closure& callback, const ErrorCallback& error_callback) { VLOG(1) << "Connect: " << object_path.value(); Properties* properties = GetProperties(object_path); if (properties->connected.value() == true) { // Already connected. callback.Run(); return; } if (properties->paired.value() != true && object_path != dbus::ObjectPath(kConnectUnpairablePath) && object_path != dbus::ObjectPath(kLowEnergyPath)) { // Must be paired. error_callback.Run(bluetooth_device::kErrorFailed, "Not paired"); return; } else if (properties->paired.value() == true && (object_path == dbus::ObjectPath(kUnconnectableDevicePath) || object_path == dbus::ObjectPath(kPairedUnconnectableDevicePath))) { // Must not be paired error_callback.Run(bluetooth_device::kErrorFailed, "Connection fails while paired"); return; } // The device can be connected. properties->connected.ReplaceValue(true); callback.Run(); // Expose GATT services if connected to LE device. if (object_path == dbus::ObjectPath(kLowEnergyPath)) { FakeBluetoothGattServiceClient* gatt_service_client = static_cast( DBusThreadManager::Get()->GetBluetoothGattServiceClient()); gatt_service_client->ExposeHeartRateService( dbus::ObjectPath(kLowEnergyPath)); } AddInputDeviceIfNeeded(object_path, properties); } void FakeBluetoothDeviceClient::Disconnect( const dbus::ObjectPath& object_path, const base::Closure& callback, const ErrorCallback& error_callback) { VLOG(1) << "Disconnect: " << object_path.value(); Properties* properties = GetProperties(object_path); if (!properties->connected.value()) { error_callback.Run("org.bluez.Error.NotConnected", "Not Connected"); return; } // Hide the Heart Rate Service if disconnected from LE device. if (object_path == dbus::ObjectPath(kLowEnergyPath)) { FakeBluetoothGattServiceClient* gatt_service_client = static_cast( DBusThreadManager::Get()->GetBluetoothGattServiceClient()); gatt_service_client->HideHeartRateService(); } callback.Run(); properties->connected.ReplaceValue(false); } void FakeBluetoothDeviceClient::ConnectProfile( const dbus::ObjectPath& object_path, const std::string& uuid, const base::Closure& callback, const ErrorCallback& error_callback) { VLOG(1) << "ConnectProfile: " << object_path.value() << " " << uuid; FakeBluetoothProfileManagerClient* fake_bluetooth_profile_manager_client = static_cast( DBusThreadManager::Get()->GetBluetoothProfileManagerClient()); FakeBluetoothProfileServiceProvider* profile_service_provider = fake_bluetooth_profile_manager_client->GetProfileServiceProvider(uuid); if (profile_service_provider == NULL) { error_callback.Run(kNoResponseError, "Missing profile"); return; } if (object_path == dbus::ObjectPath(kPairedUnconnectableDevicePath)) { error_callback.Run(bluetooth_device::kErrorFailed, "unconnectable"); return; } // Make a socket pair of a compatible type with the type used by Bluetooth; // spin up a thread to simulate the server side and wrap the client side in // a D-Bus file descriptor object. int socket_type = SOCK_STREAM; if (uuid == FakeBluetoothProfileManagerClient::kL2capUuid) socket_type = SOCK_SEQPACKET; int fds[2]; if (socketpair(AF_UNIX, socket_type, 0, fds) < 0) { error_callback.Run(kNoResponseError, "socketpair call failed"); return; } int args; args = fcntl(fds[1], F_GETFL, NULL); if (args < 0) { error_callback.Run(kNoResponseError, "failed to get socket flags"); return; } args |= O_NONBLOCK; if (fcntl(fds[1], F_SETFL, args) < 0) { error_callback.Run(kNoResponseError, "failed to set socket non-blocking"); return; } base::WorkerPool::GetTaskRunner(false) ->PostTask(FROM_HERE, base::Bind(&SimulatedProfileSocket, fds[0])); scoped_ptr fd(new dbus::FileDescriptor(fds[1])); // Post the new connection to the service provider. BluetoothProfileServiceProvider::Delegate::Options options; profile_service_provider->NewConnection( object_path, fd.Pass(), options, base::Bind(&FakeBluetoothDeviceClient::ConnectionCallback, base::Unretained(this), object_path, callback, error_callback)); } void FakeBluetoothDeviceClient::DisconnectProfile( const dbus::ObjectPath& object_path, const std::string& uuid, const base::Closure& callback, const ErrorCallback& error_callback) { VLOG(1) << "DisconnectProfile: " << object_path.value() << " " << uuid; FakeBluetoothProfileManagerClient* fake_bluetooth_profile_manager_client = static_cast( DBusThreadManager::Get()->GetBluetoothProfileManagerClient()); FakeBluetoothProfileServiceProvider* profile_service_provider = fake_bluetooth_profile_manager_client->GetProfileServiceProvider(uuid); if (profile_service_provider == NULL) { error_callback.Run(kNoResponseError, "Missing profile"); return; } profile_service_provider->RequestDisconnection( object_path, base::Bind(&FakeBluetoothDeviceClient::DisconnectionCallback, base::Unretained(this), object_path, callback, error_callback)); } void FakeBluetoothDeviceClient::Pair(const dbus::ObjectPath& object_path, const base::Closure& callback, const ErrorCallback& error_callback) { VLOG(1) << "Pair: " << object_path.value(); Properties* properties = GetProperties(object_path); if (properties->paired.value() == true) { // Already paired. callback.Run(); return; } SimulatePairing(object_path, false, callback, error_callback); } void FakeBluetoothDeviceClient::CancelPairing( const dbus::ObjectPath& object_path, const base::Closure& callback, const ErrorCallback& error_callback) { VLOG(1) << "CancelPairing: " << object_path.value(); pairing_cancelled_ = true; callback.Run(); } void FakeBluetoothDeviceClient::GetConnInfo( const dbus::ObjectPath& object_path, const ConnInfoCallback& callback, const ErrorCallback& error_callback) { Properties* properties = GetProperties(object_path); if (!properties->connected.value()) { error_callback.Run("org.bluez.Error.NotConnected", "Not Connected"); return; } callback.Run(connection_rssi_, transmit_power_, max_transmit_power_); } void FakeBluetoothDeviceClient::BeginDiscoverySimulation( const dbus::ObjectPath& adapter_path) { VLOG(1) << "starting discovery simulation"; discovery_simulation_step_ = 1; base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::DiscoverySimulationTimer, base::Unretained(this)), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } void FakeBluetoothDeviceClient::EndDiscoverySimulation( const dbus::ObjectPath& adapter_path) { VLOG(1) << "stopping discovery simulation"; discovery_simulation_step_ = 0; } void FakeBluetoothDeviceClient::BeginIncomingPairingSimulation( const dbus::ObjectPath& adapter_path) { VLOG(1) << "starting incoming pairing simulation"; incoming_pairing_simulation_step_ = 1; base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::IncomingPairingSimulationTimer, base::Unretained(this)), base::TimeDelta::FromMilliseconds( kIncomingSimulationStartPairTimeMultiplier * simulation_interval_ms_)); } void FakeBluetoothDeviceClient::EndIncomingPairingSimulation( const dbus::ObjectPath& adapter_path) { VLOG(1) << "stopping incoming pairing simulation"; incoming_pairing_simulation_step_ = 0; } void FakeBluetoothDeviceClient::SetSimulationIntervalMs(int interval_ms) { simulation_interval_ms_ = interval_ms; } void FakeBluetoothDeviceClient::CreateDevice( const dbus::ObjectPath& adapter_path, const dbus::ObjectPath& device_path) { if (std::find(device_list_.begin(), device_list_.end(), device_path) != device_list_.end()) return; scoped_ptr properties( new Properties(base::Bind(&FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), device_path))); properties->adapter.ReplaceValue(adapter_path); if (device_path == dbus::ObjectPath(kLegacyAutopairPath)) { properties->address.ReplaceValue(kLegacyAutopairAddress); properties->bluetooth_class.ReplaceValue(kLegacyAutopairClass); properties->name.ReplaceValue("LegacyAutopair"); properties->alias.ReplaceValue(kLegacyAutopairName); std::vector uuids; uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); properties->uuids.ReplaceValue(uuids); } else if (device_path == dbus::ObjectPath(kDisplayPinCodePath)) { properties->address.ReplaceValue(kDisplayPinCodeAddress); properties->bluetooth_class.ReplaceValue(kDisplayPinCodeClass); properties->name.ReplaceValue("DisplayPinCode"); properties->alias.ReplaceValue(kDisplayPinCodeName); std::vector uuids; uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); properties->uuids.ReplaceValue(uuids); } else if (device_path == dbus::ObjectPath(kVanishingDevicePath)) { properties->address.ReplaceValue(kVanishingDeviceAddress); properties->bluetooth_class.ReplaceValue(kVanishingDeviceClass); properties->name.ReplaceValue("VanishingDevice"); properties->alias.ReplaceValue(kVanishingDeviceName); } else if (device_path == dbus::ObjectPath(kConnectUnpairablePath)) { properties->address.ReplaceValue(kConnectUnpairableAddress); properties->bluetooth_class.ReplaceValue(kConnectUnpairableClass); properties->name.ReplaceValue("ConnectUnpairable"); properties->alias.ReplaceValue(kConnectUnpairableName); std::vector uuids; uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); properties->uuids.ReplaceValue(uuids); } else if (device_path == dbus::ObjectPath(kDisplayPasskeyPath)) { properties->address.ReplaceValue(kDisplayPasskeyAddress); properties->bluetooth_class.ReplaceValue(kDisplayPasskeyClass); properties->name.ReplaceValue("DisplayPasskey"); properties->alias.ReplaceValue(kDisplayPasskeyName); std::vector uuids; uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); properties->uuids.ReplaceValue(uuids); } else if (device_path == dbus::ObjectPath(kRequestPinCodePath)) { properties->address.ReplaceValue(kRequestPinCodeAddress); properties->bluetooth_class.ReplaceValue(kRequestPinCodeClass); properties->name.ReplaceValue("RequestPinCode"); properties->alias.ReplaceValue(kRequestPinCodeName); } else if (device_path == dbus::ObjectPath(kConfirmPasskeyPath)) { properties->address.ReplaceValue(kConfirmPasskeyAddress); properties->bluetooth_class.ReplaceValue(kConfirmPasskeyClass); properties->name.ReplaceValue("ConfirmPasskey"); properties->alias.ReplaceValue(kConfirmPasskeyName); } else if (device_path == dbus::ObjectPath(kRequestPasskeyPath)) { properties->address.ReplaceValue(kRequestPasskeyAddress); properties->bluetooth_class.ReplaceValue(kRequestPasskeyClass); properties->name.ReplaceValue("RequestPasskey"); properties->alias.ReplaceValue(kRequestPasskeyName); } else if (device_path == dbus::ObjectPath(kUnconnectableDevicePath)) { properties->address.ReplaceValue(kUnconnectableDeviceAddress); properties->bluetooth_class.ReplaceValue(kUnconnectableDeviceClass); properties->name.ReplaceValue("UnconnectableDevice"); properties->alias.ReplaceValue(kUnconnectableDeviceName); } else if (device_path == dbus::ObjectPath(kUnpairableDevicePath)) { properties->address.ReplaceValue(kUnpairableDeviceAddress); properties->bluetooth_class.ReplaceValue(kUnpairableDeviceClass); properties->name.ReplaceValue("Fake Unpairable Device"); properties->alias.ReplaceValue(kUnpairableDeviceName); } else if (device_path == dbus::ObjectPath(kJustWorksPath)) { properties->address.ReplaceValue(kJustWorksAddress); properties->bluetooth_class.ReplaceValue(kJustWorksClass); properties->name.ReplaceValue("JustWorks"); properties->alias.ReplaceValue(kJustWorksName); } else if (device_path == dbus::ObjectPath(kLowEnergyPath)) { properties->address.ReplaceValue(kLowEnergyAddress); properties->bluetooth_class.ReplaceValue(kLowEnergyClass); properties->name.ReplaceValue("Heart Rate Monitor"); properties->alias.ReplaceValue(kLowEnergyName); std::vector uuids; uuids.push_back(FakeBluetoothGattServiceClient::kHeartRateServiceUUID); properties->uuids.ReplaceValue(uuids); } else if (device_path == dbus::ObjectPath(kConnectedTrustedNotPairedDevicePath)) { properties->address.ReplaceValue(kConnectedTrustedNotPairedDeviceAddress); properties->bluetooth_class.ReplaceValue( kConnectedTrustedNotPairedDeviceClass); properties->trusted.ReplaceValue(true); properties->connected.ReplaceValue(true); properties->paired.ReplaceValue(false); properties->name.ReplaceValue("Connected Pairable Device"); properties->alias.ReplaceValue(kConnectedTrustedNotPairedDeviceName); } else { NOTREACHED(); } properties_map_.insert(device_path, properties.Pass()); device_list_.push_back(device_path); FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, DeviceAdded(device_path)); } void FakeBluetoothDeviceClient::CreateDeviceWithProperties( const dbus::ObjectPath& adapter_path, const IncomingDeviceProperties& props) { dbus::ObjectPath device_path(props.device_path); if (std::find(device_list_.begin(), device_list_.end(), device_path) != device_list_.end()) return; scoped_ptr properties( new Properties(base::Bind(&FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), device_path))); properties->adapter.ReplaceValue(adapter_path); properties->name.ReplaceValue(props.device_name); properties->alias.ReplaceValue(props.device_alias); properties->address.ReplaceValue(props.device_address); properties->bluetooth_class.ReplaceValue(props.device_class); properties->trusted.ReplaceValue(props.is_trusted); if (props.is_trusted) properties->paired.ReplaceValue(true); scoped_ptr options(new SimulatedPairingOptions); options->pairing_method = props.pairing_method; options->pairing_auth_token = props.pairing_auth_token; options->pairing_action = props.pairing_action; options->incoming = props.incoming; properties_map_.insert(device_path, properties.Pass()); device_list_.push_back(device_path); pairing_options_map_.insert(device_path, options.Pass()); FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, DeviceAdded(device_path)); } scoped_ptr FakeBluetoothDeviceClient::GetBluetoothDevicesAsDictionaries() const { scoped_ptr predefined_devices(new base::ListValue); scoped_ptr pairedDevice(new base::DictionaryValue); pairedDevice->SetString("path", kPairedDevicePath); pairedDevice->SetString("address", kPairedDeviceAddress); pairedDevice->SetString("name", kPairedDeviceName); pairedDevice->SetString("alias", kPairedDeviceName); pairedDevice->SetString("pairingMethod", ""); pairedDevice->SetString("pairingAuthToken", ""); pairedDevice->SetString("pairingAction", ""); pairedDevice->SetInteger("classValue", kPairedDeviceClass); pairedDevice->SetBoolean("discoverable", true); pairedDevice->SetBoolean("isTrusted", true); pairedDevice->SetBoolean("paired", true); pairedDevice->SetBoolean("incoming", false); predefined_devices->Append(pairedDevice.Pass()); scoped_ptr legacyDevice(new base::DictionaryValue); legacyDevice->SetString("path", kLegacyAutopairPath); legacyDevice->SetString("address", kLegacyAutopairAddress); legacyDevice->SetString("name", kLegacyAutopairName); legacyDevice->SetString("alias", kLegacyAutopairName); legacyDevice->SetString("pairingMethod", ""); legacyDevice->SetString("pairingAuthToken", ""); legacyDevice->SetString("pairingAction", ""); legacyDevice->SetInteger("classValue", kLegacyAutopairClass); legacyDevice->SetBoolean("isTrusted", true); legacyDevice->SetBoolean("discoverable", false); legacyDevice->SetBoolean("paired", false); legacyDevice->SetBoolean("incoming", false); predefined_devices->Append(legacyDevice.Pass()); scoped_ptr pin(new base::DictionaryValue); pin->SetString("path", kDisplayPinCodePath); pin->SetString("address", kDisplayPinCodeAddress); pin->SetString("name", kDisplayPinCodeName); pin->SetString("alias", kDisplayPinCodeName); pin->SetString("pairingMethod", kPairingMethodPinCode); pin->SetString("pairingAuthToken", kTestPinCode); pin->SetString("pairingAction", kPairingActionDisplay); pin->SetInteger("classValue", kDisplayPinCodeClass); pin->SetBoolean("isTrusted", false); pin->SetBoolean("discoverable", false); pin->SetBoolean("paired", false); pin->SetBoolean("incoming", false); predefined_devices->Append(pin.Pass()); scoped_ptr vanishing(new base::DictionaryValue); vanishing->SetString("path", kVanishingDevicePath); vanishing->SetString("address", kVanishingDeviceAddress); vanishing->SetString("name", kVanishingDeviceName); vanishing->SetString("alias", kVanishingDeviceName); vanishing->SetString("pairingMethod", ""); vanishing->SetString("pairingAuthToken", ""); vanishing->SetString("pairingAction", ""); vanishing->SetInteger("classValue", kVanishingDeviceClass); vanishing->SetBoolean("isTrusted", false); vanishing->SetBoolean("discoverable", false); vanishing->SetBoolean("paired", false); vanishing->SetBoolean("incoming", false); predefined_devices->Append(vanishing.Pass()); scoped_ptr connect_unpairable( new base::DictionaryValue); connect_unpairable->SetString("path", kConnectUnpairablePath); connect_unpairable->SetString("address", kConnectUnpairableAddress); connect_unpairable->SetString("name", kConnectUnpairableName); connect_unpairable->SetString("pairingMethod", ""); connect_unpairable->SetString("pairingAuthToken", ""); connect_unpairable->SetString("pairingAction", ""); connect_unpairable->SetString("alias", kConnectUnpairableName); connect_unpairable->SetInteger("classValue", kConnectUnpairableClass); connect_unpairable->SetBoolean("isTrusted", false); connect_unpairable->SetBoolean("discoverable", false); connect_unpairable->SetBoolean("paired", false); connect_unpairable->SetBoolean("incoming", false); predefined_devices->Append(connect_unpairable.Pass()); scoped_ptr passkey(new base::DictionaryValue); passkey->SetString("path", kDisplayPasskeyPath); passkey->SetString("address", kDisplayPasskeyAddress); passkey->SetString("name", kDisplayPasskeyName); passkey->SetString("alias", kDisplayPasskeyName); passkey->SetString("pairingMethod", kPairingMethodPassKey); passkey->SetInteger("pairingAuthToken", kTestPassKey); passkey->SetString("pairingAction", kPairingActionDisplay); passkey->SetInteger("classValue", kDisplayPasskeyClass); passkey->SetBoolean("isTrusted", false); passkey->SetBoolean("discoverable", false); passkey->SetBoolean("paired", false); passkey->SetBoolean("incoming", false); predefined_devices->Append(passkey.Pass()); scoped_ptr request_pin(new base::DictionaryValue); request_pin->SetString("path", kRequestPinCodePath); request_pin->SetString("address", kRequestPinCodeAddress); request_pin->SetString("name", kRequestPinCodeName); request_pin->SetString("alias", kRequestPinCodeName); request_pin->SetString("pairingMethod", ""); request_pin->SetString("pairingAuthToken", ""); request_pin->SetString("pairingAction", kPairingActionRequest); request_pin->SetInteger("classValue", kRequestPinCodeClass); request_pin->SetBoolean("isTrusted", false); request_pin->SetBoolean("discoverable", false); request_pin->SetBoolean("paired", false); request_pin->SetBoolean("incoming", false); predefined_devices->Append(request_pin.Pass()); scoped_ptr confirm(new base::DictionaryValue); confirm->SetString("path", kConfirmPasskeyPath); confirm->SetString("address", kConfirmPasskeyAddress); confirm->SetString("name", kConfirmPasskeyName); confirm->SetString("alias", kConfirmPasskeyName); confirm->SetString("pairingMethod", ""); confirm->SetInteger("pairingAuthToken", kTestPassKey); confirm->SetString("pairingAction", kPairingActionConfirmation); confirm->SetInteger("classValue", kConfirmPasskeyClass); confirm->SetBoolean("isTrusted", false); confirm->SetBoolean("discoverable", false); confirm->SetBoolean("paired", false); confirm->SetBoolean("incoming", false); predefined_devices->Append(confirm.Pass()); scoped_ptr request_passkey(new base::DictionaryValue); request_passkey->SetString("path", kRequestPasskeyPath); request_passkey->SetString("address", kRequestPasskeyAddress); request_passkey->SetString("name", kRequestPasskeyName); request_passkey->SetString("alias", kRequestPasskeyName); request_passkey->SetString("pairingMethod", kPairingMethodPassKey); request_passkey->SetString("pairingAction", kPairingActionRequest); request_passkey->SetInteger("pairingAuthToken", kTestPassKey); request_passkey->SetInteger("classValue", kRequestPasskeyClass); request_passkey->SetBoolean("isTrusted", false); request_passkey->SetBoolean("discoverable", false); request_passkey->SetBoolean("paired", false); request_passkey->SetBoolean("incoming", false); predefined_devices->Append(request_passkey.Pass()); scoped_ptr unconnectable(new base::DictionaryValue); unconnectable->SetString("path", kUnconnectableDevicePath); unconnectable->SetString("address", kUnconnectableDeviceAddress); unconnectable->SetString("name", kUnconnectableDeviceName); unconnectable->SetString("alias", kUnconnectableDeviceName); unconnectable->SetString("pairingMethod", ""); unconnectable->SetString("pairingAuthToken", ""); unconnectable->SetString("pairingAction", ""); unconnectable->SetInteger("classValue", kUnconnectableDeviceClass); unconnectable->SetBoolean("isTrusted", true); unconnectable->SetBoolean("discoverable", false); unconnectable->SetBoolean("paired", false); unconnectable->SetBoolean("incoming", false); predefined_devices->Append(unconnectable.Pass()); scoped_ptr unpairable(new base::DictionaryValue); unpairable->SetString("path", kUnpairableDevicePath); unpairable->SetString("address", kUnpairableDeviceAddress); unpairable->SetString("name", kUnpairableDeviceName); unpairable->SetString("alias", kUnpairableDeviceName); unpairable->SetString("pairingMethod", ""); unpairable->SetString("pairingAuthToken", ""); unpairable->SetString("pairingAction", kPairingActionFail); unpairable->SetInteger("classValue", kUnpairableDeviceClass); unpairable->SetBoolean("isTrusted", false); unpairable->SetBoolean("discoverable", false); unpairable->SetBoolean("paired", false); unpairable->SetBoolean("incoming", false); predefined_devices->Append(unpairable.Pass()); scoped_ptr just_works(new base::DictionaryValue); just_works->SetString("path", kJustWorksPath); just_works->SetString("address", kJustWorksAddress); just_works->SetString("name", kJustWorksName); just_works->SetString("alias", kJustWorksName); just_works->SetString("pairingMethod", ""); just_works->SetString("pairingAuthToken", ""); just_works->SetString("pairingAction", ""); just_works->SetInteger("classValue", kJustWorksClass); just_works->SetBoolean("isTrusted", false); just_works->SetBoolean("discoverable", false); just_works->SetBoolean("paired", false); just_works->SetBoolean("incoming", false); predefined_devices->Append(just_works.Pass()); scoped_ptr low_energy(new base::DictionaryValue); low_energy->SetString("path", kLowEnergyPath); low_energy->SetString("address", kLowEnergyAddress); low_energy->SetString("name", kLowEnergyName); low_energy->SetString("alias", kLowEnergyName); low_energy->SetString("pairingMethod", ""); low_energy->SetString("pairingAuthToken", ""); low_energy->SetString("pairingAction", ""); low_energy->SetInteger("classValue", kLowEnergyClass); low_energy->SetBoolean("isTrusted", false); low_energy->SetBoolean("discoverable", false); low_energy->SetBoolean("paireed", false); low_energy->SetBoolean("incoming", false); predefined_devices->Append(low_energy.Pass()); scoped_ptr paired_unconnectable( new base::DictionaryValue); paired_unconnectable->SetString("path", kPairedUnconnectableDevicePath); paired_unconnectable->SetString("address", kPairedUnconnectableDeviceAddress); paired_unconnectable->SetString("name", kPairedUnconnectableDeviceName); paired_unconnectable->SetString("pairingMethod", ""); paired_unconnectable->SetString("pairingAuthToken", ""); paired_unconnectable->SetString("pairingAction", ""); paired_unconnectable->SetString("alias", kPairedUnconnectableDeviceName); paired_unconnectable->SetInteger("classValue", kPairedUnconnectableDeviceClass); paired_unconnectable->SetBoolean("isTrusted", false); paired_unconnectable->SetBoolean("discoverable", true); paired_unconnectable->SetBoolean("paired", true); paired_unconnectable->SetBoolean("incoming", false); predefined_devices->Append(paired_unconnectable.Pass()); scoped_ptr connected_trusted_not_paired( new base::DictionaryValue); connected_trusted_not_paired->SetString("path", kConnectedTrustedNotPairedDevicePath); connected_trusted_not_paired->SetString( "address", kConnectedTrustedNotPairedDeviceAddress); connected_trusted_not_paired->SetString("name", kConnectedTrustedNotPairedDeviceName); connected_trusted_not_paired->SetString("pairingMethod", ""); connected_trusted_not_paired->SetString("pairingAuthToken", ""); connected_trusted_not_paired->SetString("pairingAction", ""); connected_trusted_not_paired->SetString("alias", kConnectedTrustedNotPairedDeviceName); connected_trusted_not_paired->SetInteger( "classValue", kConnectedTrustedNotPairedDeviceClass); connected_trusted_not_paired->SetBoolean("isTrusted", true); connected_trusted_not_paired->SetBoolean("discoverable", true); connected_trusted_not_paired->SetBoolean("paired", false); connected_trusted_not_paired->SetBoolean("incoming", false); predefined_devices->Append(connected_trusted_not_paired.Pass()); return predefined_devices.Pass(); } void FakeBluetoothDeviceClient::RemoveDevice( const dbus::ObjectPath& adapter_path, const dbus::ObjectPath& device_path) { std::vector::iterator listiter = std::find(device_list_.begin(), device_list_.end(), device_path); if (listiter == device_list_.end()) return; PropertiesMap::const_iterator iter = properties_map_.find(device_path); Properties* properties = iter->second; VLOG(1) << "removing device: " << properties->alias.value(); device_list_.erase(listiter); // Remove the Input interface if it exists. This should be called before the // BluetoothDeviceClient::Observer::DeviceRemoved because it deletes the // BluetoothDeviceChromeOS object, including the device_path referenced here. FakeBluetoothInputClient* fake_bluetooth_input_client = static_cast( DBusThreadManager::Get()->GetBluetoothInputClient()); fake_bluetooth_input_client->RemoveInputDevice(device_path); if (device_path == dbus::ObjectPath(kLowEnergyPath)) { FakeBluetoothGattServiceClient* gatt_service_client = static_cast( DBusThreadManager::Get()->GetBluetoothGattServiceClient()); gatt_service_client->HideHeartRateService(); } FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, DeviceRemoved(device_path)); properties_map_.erase(iter); PairingOptionsMap::const_iterator options_iter = pairing_options_map_.find(device_path); if (options_iter != pairing_options_map_.end()) { pairing_options_map_.erase(options_iter); } } void FakeBluetoothDeviceClient::OnPropertyChanged( const dbus::ObjectPath& object_path, const std::string& property_name) { VLOG(2) << "Fake Bluetooth device property changed: " << object_path.value() << ": " << property_name; FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, DevicePropertyChanged(object_path, property_name)); } void FakeBluetoothDeviceClient::DiscoverySimulationTimer() { if (!discovery_simulation_step_) return; // Timer fires every .75s, the numbers below are arbitrary to give a feel // for a discovery process. VLOG(1) << "discovery simulation, step " << discovery_simulation_step_; if (discovery_simulation_step_ == 2) { CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kLegacyAutopairPath)); CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kLowEnergyPath)); } else if (discovery_simulation_step_ == 4) { UpdateDeviceRSSI(dbus::ObjectPath(kLowEnergyPath), base::RandInt(kMinRSSI, kMaxRSSI)); CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kDisplayPinCodePath)); CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kVanishingDevicePath)); } else if (discovery_simulation_step_ == 7) { CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kConnectUnpairablePath)); UpdateDeviceRSSI(dbus::ObjectPath(kLowEnergyPath), base::RandInt(kMinRSSI, kMaxRSSI)); } else if (discovery_simulation_step_ == 8) { CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kDisplayPasskeyPath)); CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kRequestPinCodePath)); UpdateDeviceRSSI(dbus::ObjectPath(kLowEnergyPath), base::RandInt(kMinRSSI, kMaxRSSI)); } else if (discovery_simulation_step_ == 10) { CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kConfirmPasskeyPath)); CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kRequestPasskeyPath)); CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kUnconnectableDevicePath)); CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kUnpairableDevicePath)); CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kJustWorksPath)); UpdateDeviceRSSI(dbus::ObjectPath(kLowEnergyPath), base::RandInt(kMinRSSI, kMaxRSSI)); } else if (discovery_simulation_step_ == 13) { UpdateDeviceRSSI(dbus::ObjectPath(kLowEnergyPath), base::RandInt(kMinRSSI, kMaxRSSI)); RemoveDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kVanishingDevicePath)); } else if (discovery_simulation_step_ == 14) { UpdateDeviceRSSI(dbus::ObjectPath(kLowEnergyPath), base::RandInt(kMinRSSI, kMaxRSSI)); return; } ++discovery_simulation_step_; base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::DiscoverySimulationTimer, base::Unretained(this)), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } void FakeBluetoothDeviceClient::IncomingPairingSimulationTimer() { if (!incoming_pairing_simulation_step_) return; VLOG(1) << "incoming pairing simulation, step " << incoming_pairing_simulation_step_; switch (incoming_pairing_simulation_step_) { case 1: CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kConfirmPasskeyPath)); SimulatePairing(dbus::ObjectPath(kConfirmPasskeyPath), true, base::Bind(&base::DoNothing), base::Bind(&SimpleErrorCallback)); break; case 2: CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kJustWorksPath)); SimulatePairing(dbus::ObjectPath(kJustWorksPath), true, base::Bind(&base::DoNothing), base::Bind(&SimpleErrorCallback)); break; case 3: CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kDisplayPinCodePath)); SimulatePairing(dbus::ObjectPath(kDisplayPinCodePath), true, base::Bind(&base::DoNothing), base::Bind(&SimpleErrorCallback)); break; case 4: CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kDisplayPasskeyPath)); SimulatePairing(dbus::ObjectPath(kDisplayPasskeyPath), true, base::Bind(&base::DoNothing), base::Bind(&SimpleErrorCallback)); break; case 5: CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kRequestPinCodePath)); SimulatePairing(dbus::ObjectPath(kRequestPinCodePath), true, base::Bind(&base::DoNothing), base::Bind(&SimpleErrorCallback)); break; case 6: CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kRequestPasskeyPath)); SimulatePairing(dbus::ObjectPath(kRequestPasskeyPath), true, base::Bind(&base::DoNothing), base::Bind(&SimpleErrorCallback)); break; default: return; } ++incoming_pairing_simulation_step_; base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::IncomingPairingSimulationTimer, base::Unretained(this)), base::TimeDelta::FromMilliseconds(kIncomingSimulationPairTimeMultiplier * simulation_interval_ms_)); } void FakeBluetoothDeviceClient::SimulatePairing( const dbus::ObjectPath& object_path, bool incoming_request, const base::Closure& callback, const ErrorCallback& error_callback) { pairing_cancelled_ = false; FakeBluetoothAgentManagerClient* fake_bluetooth_agent_manager_client = static_cast( DBusThreadManager::Get()->GetBluetoothAgentManagerClient()); FakeBluetoothAgentServiceProvider* agent_service_provider = fake_bluetooth_agent_manager_client->GetAgentServiceProvider(); CHECK(agent_service_provider != NULL); // Grab the device's pairing properties. PairingOptionsMap::const_iterator iter = pairing_options_map_.find(object_path); // If the device with path |object_path| has simulated pairing properties // defined, then pair it based on its |pairing_method|. if (iter != pairing_options_map_.end()) { if (iter->second->pairing_action == kPairingActionFail) { // Fails the pairing with an org.bluez.Error.Failed error. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::FailSimulatedPairing, base::Unretained(this), object_path, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } else if (iter->second->pairing_method == kPairingMethodNone || iter->second->pairing_method.empty()) { if (!iter->second->incoming) { // Simply pair and connect the device. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, base::Unretained(this), object_path, callback, error_callback), base::TimeDelta::FromMilliseconds( kSimulateNormalPairTimeMultiplier * simulation_interval_ms_)); } else { agent_service_provider->RequestAuthorization( object_path, base::Bind(&FakeBluetoothDeviceClient::ConfirmationCallback, base::Unretained(this), object_path, callback, error_callback)); } } else if (iter->second->pairing_method == kPairingMethodPinCode) { if (iter->second->pairing_action == kPairingActionDisplay) { // Display a Pincode, and wait before acting as if the other end // accepted it. agent_service_provider->DisplayPinCode( object_path, iter->second->pairing_auth_token); base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, base::Unretained(this), object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(kPinCodeDevicePairTimeMultiplier * simulation_interval_ms_)); } else if (iter->second->pairing_action == kPairingActionRequest) { // Request a pin code. agent_service_provider->RequestPinCode( object_path, base::Bind(&FakeBluetoothDeviceClient::PinCodeCallback, base::Unretained(this), object_path, callback, error_callback)); } else if (iter->second->pairing_action == kPairingActionConfirmation) { error_callback.Run(kNoResponseError, "No confirm for pincode pairing."); } } else if (iter->second->pairing_method == kPairingMethodPassKey) { // Display a passkey, and each interval act as if another key was entered // for it. if (iter->second->pairing_action == kPairingActionDisplay) { agent_service_provider->DisplayPasskey( object_path, std::stoi(iter->second->pairing_auth_token), 0); base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::SimulateKeypress, base::Unretained(this), 1, object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } else if (iter->second->pairing_action == kPairingActionRequest) { agent_service_provider->RequestPasskey( object_path, base::Bind(&FakeBluetoothDeviceClient::PasskeyCallback, base::Unretained(this), object_path, callback, error_callback)); } else if (iter->second->pairing_action == kPairingActionConfirmation) { agent_service_provider->RequestConfirmation( object_path, std::stoi(iter->second->pairing_auth_token), base::Bind(&FakeBluetoothDeviceClient::ConfirmationCallback, base::Unretained(this), object_path, callback, error_callback)); } } } else { if (object_path == dbus::ObjectPath(kLegacyAutopairPath) || object_path == dbus::ObjectPath(kConnectUnpairablePath) || object_path == dbus::ObjectPath(kUnconnectableDevicePath) || object_path == dbus::ObjectPath(kLowEnergyPath)) { // No need to call anything on the pairing delegate, just wait 3 times // the interval before acting as if the other end accepted it. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, base::Unretained(this), object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(kSimulateNormalPairTimeMultiplier * simulation_interval_ms_)); } else if (object_path == dbus::ObjectPath(kDisplayPinCodePath)) { // Display a Pincode, and wait before acting as if the other end accepted // it. agent_service_provider->DisplayPinCode(object_path, kTestPinCode); base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, base::Unretained(this), object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(kPinCodeDevicePairTimeMultiplier * simulation_interval_ms_)); } else if (object_path == dbus::ObjectPath(kVanishingDevicePath)) { // The vanishing device simulates being too far away, and thus times out. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::TimeoutSimulatedPairing, base::Unretained(this), object_path, error_callback), base::TimeDelta::FromMilliseconds(kVanishingDevicePairTimeMultiplier * simulation_interval_ms_)); } else if (object_path == dbus::ObjectPath(kDisplayPasskeyPath)) { // Display a passkey, and each interval act as if another key was entered // for it. agent_service_provider->DisplayPasskey(object_path, kTestPassKey, 0); base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::SimulateKeypress, base::Unretained(this), 1, object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } else if (object_path == dbus::ObjectPath(kRequestPinCodePath)) { // Request a Pincode. agent_service_provider->RequestPinCode( object_path, base::Bind(&FakeBluetoothDeviceClient::PinCodeCallback, base::Unretained(this), object_path, callback, error_callback)); } else if (object_path == dbus::ObjectPath(kConfirmPasskeyPath) || object_path == dbus::ObjectPath(kConnectedTrustedNotPairedDevicePath)) { // Request confirmation of a Passkey. agent_service_provider->RequestConfirmation( object_path, kTestPassKey, base::Bind(&FakeBluetoothDeviceClient::ConfirmationCallback, base::Unretained(this), object_path, callback, error_callback)); } else if (object_path == dbus::ObjectPath(kRequestPasskeyPath)) { // Request a Passkey from the user. agent_service_provider->RequestPasskey( object_path, base::Bind(&FakeBluetoothDeviceClient::PasskeyCallback, base::Unretained(this), object_path, callback, error_callback)); } else if (object_path == dbus::ObjectPath(kUnpairableDevicePath)) { // Fails the pairing with an org.bluez.Error.Failed error. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::FailSimulatedPairing, base::Unretained(this), object_path, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } else if (object_path == dbus::ObjectPath(kJustWorksPath)) { if (incoming_request) { agent_service_provider->RequestAuthorization( object_path, base::Bind(&FakeBluetoothDeviceClient::ConfirmationCallback, base::Unretained(this), object_path, callback, error_callback)); } else { // No need to call anything on the pairing delegate, just wait before // acting as if the other end accepted it. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, base::Unretained(this), object_path, callback, error_callback), base::TimeDelta::FromMilliseconds( kSimulateNormalPairTimeMultiplier * simulation_interval_ms_)); } } else { error_callback.Run(kNoResponseError, "No pairing fake"); } } } void FakeBluetoothDeviceClient::CompleteSimulatedPairing( const dbus::ObjectPath& object_path, const base::Closure& callback, const ErrorCallback& error_callback) { VLOG(1) << "CompleteSimulatedPairing: " << object_path.value(); if (pairing_cancelled_) { pairing_cancelled_ = false; error_callback.Run(bluetooth_device::kErrorAuthenticationCanceled, "Cancelled"); } else { Properties* properties = GetProperties(object_path); properties->paired.ReplaceValue(true); callback.Run(); AddInputDeviceIfNeeded(object_path, properties); } } void FakeBluetoothDeviceClient::TimeoutSimulatedPairing( const dbus::ObjectPath& object_path, const ErrorCallback& error_callback) { VLOG(1) << "TimeoutSimulatedPairing: " << object_path.value(); error_callback.Run(bluetooth_device::kErrorAuthenticationTimeout, "Timed out"); } void FakeBluetoothDeviceClient::CancelSimulatedPairing( const dbus::ObjectPath& object_path, const ErrorCallback& error_callback) { VLOG(1) << "CancelSimulatedPairing: " << object_path.value(); error_callback.Run(bluetooth_device::kErrorAuthenticationCanceled, "Canceled"); } void FakeBluetoothDeviceClient::RejectSimulatedPairing( const dbus::ObjectPath& object_path, const ErrorCallback& error_callback) { VLOG(1) << "RejectSimulatedPairing: " << object_path.value(); error_callback.Run(bluetooth_device::kErrorAuthenticationRejected, "Rejected"); } void FakeBluetoothDeviceClient::FailSimulatedPairing( const dbus::ObjectPath& object_path, const ErrorCallback& error_callback) { VLOG(1) << "FailSimulatedPairing: " << object_path.value(); error_callback.Run(bluetooth_device::kErrorFailed, "Failed"); } void FakeBluetoothDeviceClient::AddInputDeviceIfNeeded( const dbus::ObjectPath& object_path, Properties* properties) { // If the paired device is a HID device based on it's bluetooth class, // simulate the Input interface. FakeBluetoothInputClient* fake_bluetooth_input_client = static_cast( DBusThreadManager::Get()->GetBluetoothInputClient()); if ((properties->bluetooth_class.value() & 0x001f03) == 0x000500) fake_bluetooth_input_client->AddInputDevice(object_path); } void FakeBluetoothDeviceClient::UpdateDeviceRSSI( const dbus::ObjectPath& object_path, int16 rssi) { PropertiesMap::const_iterator iter = properties_map_.find(object_path); if (iter == properties_map_.end()) { VLOG(2) << "Fake device does not exist: " << object_path.value(); return; } Properties* properties = iter->second; DCHECK(properties); properties->rssi.ReplaceValue(rssi); } void FakeBluetoothDeviceClient::UpdateConnectionInfo( uint16 connection_rssi, uint16 transmit_power, uint16 max_transmit_power) { connection_rssi_ = connection_rssi; transmit_power_ = transmit_power; max_transmit_power_ = max_transmit_power; } void FakeBluetoothDeviceClient::PinCodeCallback( const dbus::ObjectPath& object_path, const base::Closure& callback, const ErrorCallback& error_callback, BluetoothAgentServiceProvider::Delegate::Status status, const std::string& pincode) { VLOG(1) << "PinCodeCallback: " << object_path.value(); if (status == BluetoothAgentServiceProvider::Delegate::SUCCESS) { PairingOptionsMap::const_iterator iter = pairing_options_map_.find(object_path); bool success = true; // If the device has pairing options defined if (iter != pairing_options_map_.end()) { success = iter->second->pairing_auth_token == pincode; } if (success) { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, base::Unretained(this), object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(kSimulateNormalPairTimeMultiplier * simulation_interval_ms_)); } else { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::RejectSimulatedPairing, base::Unretained(this), object_path, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } } else if (status == BluetoothAgentServiceProvider::Delegate::CANCELLED) { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CancelSimulatedPairing, base::Unretained(this), object_path, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } else if (status == BluetoothAgentServiceProvider::Delegate::REJECTED) { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::RejectSimulatedPairing, base::Unretained(this), object_path, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } } void FakeBluetoothDeviceClient::PasskeyCallback( const dbus::ObjectPath& object_path, const base::Closure& callback, const ErrorCallback& error_callback, BluetoothAgentServiceProvider::Delegate::Status status, uint32 passkey) { VLOG(1) << "PasskeyCallback: " << object_path.value(); if (status == BluetoothAgentServiceProvider::Delegate::SUCCESS) { PairingOptionsMap::const_iterator iter = pairing_options_map_.find(object_path); bool success = true; if (iter != pairing_options_map_.end()) { success = static_cast( std::stoi(iter->second->pairing_auth_token)) == passkey; } if (success) { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, base::Unretained(this), object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(kSimulateNormalPairTimeMultiplier * simulation_interval_ms_)); } else { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::RejectSimulatedPairing, base::Unretained(this), object_path, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } } else if (status == BluetoothAgentServiceProvider::Delegate::CANCELLED) { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CancelSimulatedPairing, base::Unretained(this), object_path, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } else if (status == BluetoothAgentServiceProvider::Delegate::REJECTED) { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::RejectSimulatedPairing, base::Unretained(this), object_path, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } } void FakeBluetoothDeviceClient::ConfirmationCallback( const dbus::ObjectPath& object_path, const base::Closure& callback, const ErrorCallback& error_callback, BluetoothAgentServiceProvider::Delegate::Status status) { VLOG(1) << "ConfirmationCallback: " << object_path.value(); if (status == BluetoothAgentServiceProvider::Delegate::SUCCESS) { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, base::Unretained(this), object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(kSimulateNormalPairTimeMultiplier * simulation_interval_ms_)); } else if (status == BluetoothAgentServiceProvider::Delegate::CANCELLED) { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CancelSimulatedPairing, base::Unretained(this), object_path, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } else if (status == BluetoothAgentServiceProvider::Delegate::REJECTED) { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::RejectSimulatedPairing, base::Unretained(this), object_path, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } } void FakeBluetoothDeviceClient::SimulateKeypress( uint16 entered, const dbus::ObjectPath& object_path, const base::Closure& callback, const ErrorCallback& error_callback) { VLOG(1) << "SimulateKeypress " << entered << ": " << object_path.value(); FakeBluetoothAgentManagerClient* fake_bluetooth_agent_manager_client = static_cast( DBusThreadManager::Get()->GetBluetoothAgentManagerClient()); FakeBluetoothAgentServiceProvider* agent_service_provider = fake_bluetooth_agent_manager_client->GetAgentServiceProvider(); // The agent service provider object could have been destroyed after the // pairing is canceled. if (!agent_service_provider) return; agent_service_provider->DisplayPasskey(object_path, kTestPassKey, entered); if (entered < 7) { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::SimulateKeypress, base::Unretained(this), entered + 1, object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } else { base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, base::Unretained(this), object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } } void FakeBluetoothDeviceClient::ConnectionCallback( const dbus::ObjectPath& object_path, const base::Closure& callback, const ErrorCallback& error_callback, BluetoothProfileServiceProvider::Delegate::Status status) { VLOG(1) << "ConnectionCallback: " << object_path.value(); if (status == BluetoothProfileServiceProvider::Delegate::SUCCESS) { callback.Run(); } else if (status == BluetoothProfileServiceProvider::Delegate::CANCELLED) { // TODO(keybuk): tear down this side of the connection error_callback.Run(bluetooth_device::kErrorFailed, "Canceled"); } else if (status == BluetoothProfileServiceProvider::Delegate::REJECTED) { // TODO(keybuk): tear down this side of the connection error_callback.Run(bluetooth_device::kErrorFailed, "Rejected"); } } void FakeBluetoothDeviceClient::DisconnectionCallback( const dbus::ObjectPath& object_path, const base::Closure& callback, const ErrorCallback& error_callback, BluetoothProfileServiceProvider::Delegate::Status status) { VLOG(1) << "DisconnectionCallback: " << object_path.value(); if (status == BluetoothProfileServiceProvider::Delegate::SUCCESS) { // TODO(keybuk): tear down this side of the connection callback.Run(); } else if (status == BluetoothProfileServiceProvider::Delegate::CANCELLED) { error_callback.Run(bluetooth_device::kErrorFailed, "Canceled"); } else if (status == BluetoothProfileServiceProvider::Delegate::REJECTED) { error_callback.Run(bluetooth_device::kErrorFailed, "Rejected"); } } } // namespace chromeos