// 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 <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <algorithm> #include <map> #include <string> #include <utility> #include <vector> #include "base/bind.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/stl_util.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_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 "dbus/object_path.h" #include "third_party/cros_system_api/dbus/service_constants.h" namespace { // Default interval between simulated events. const int kSimulationIntervalMs = 750; 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); } } // namespace namespace chromeos { 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::kAppleMousePath[] = "/fake/hci0/dev1"; const char FakeBluetoothDeviceClient::kAppleMouseAddress[] = "28:CF:DA:00:00:00"; const char FakeBluetoothDeviceClient::kAppleMouseName[] = "Apple Magic Mouse"; const uint32 FakeBluetoothDeviceClient::kAppleMouseClass = 0x002580; const char FakeBluetoothDeviceClient::kAppleKeyboardPath[] = "/fake/hci0/dev2"; const char FakeBluetoothDeviceClient::kAppleKeyboardAddress[] = "28:37:37:00:00:00"; const char FakeBluetoothDeviceClient::kAppleKeyboardName[] = "Apple Wireless Keyboard"; const uint32 FakeBluetoothDeviceClient::kAppleKeyboardClass = 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::kMicrosoftMousePath[] = "/fake/hci0/dev4"; const char FakeBluetoothDeviceClient::kMicrosoftMouseAddress[] = "7C:ED:8D:00:00:00"; const char FakeBluetoothDeviceClient::kMicrosoftMouseName[] = "Microsoft Mouse"; const uint32 FakeBluetoothDeviceClient::kMicrosoftMouseClass = 0x002580; const char FakeBluetoothDeviceClient::kMotorolaKeyboardPath[] = "/fake/hci0/dev5"; const char FakeBluetoothDeviceClient::kMotorolaKeyboardAddress[] = "00:0F:F6:00:00:00"; const char FakeBluetoothDeviceClient::kMotorolaKeyboardName[] = "Motorola Keyboard"; const uint32 FakeBluetoothDeviceClient::kMotorolaKeyboardClass = 0x002540; const char FakeBluetoothDeviceClient::kSonyHeadphonesPath[] = "/fake/hci0/dev6"; const char FakeBluetoothDeviceClient::kSonyHeadphonesAddress[] = "00:24:BE:00:00:00"; const char FakeBluetoothDeviceClient::kSonyHeadphonesName[] = "Sony BT-00"; const uint32 FakeBluetoothDeviceClient::kSonyHeadphonesClass = 0x240408; const char FakeBluetoothDeviceClient::kPhonePath[] = "/fake/hci0/dev7"; const char FakeBluetoothDeviceClient::kPhoneAddress[] = "20:7D:74:00:00:00"; const char FakeBluetoothDeviceClient::kPhoneName[] = "Phone"; const uint32 FakeBluetoothDeviceClient::kPhoneClass = 0x7a020c; const char FakeBluetoothDeviceClient::kWeirdDevicePath[] = "/fake/hci0/dev8"; const char FakeBluetoothDeviceClient::kWeirdDeviceAddress[] = "20:7D:74:00:00:01"; const char FakeBluetoothDeviceClient::kWeirdDeviceName[] = "Weird Device"; const uint32 FakeBluetoothDeviceClient::kWeirdDeviceClass = 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; 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::FakeBluetoothDeviceClient() : simulation_interval_ms_(kSimulationIntervalMs), discovery_simulation_step_(0), pairing_cancelled_(false) { Properties* 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<std::string> 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_[dbus::ObjectPath(kPairedDevicePath)] = properties; device_list_.push_back(dbus::ObjectPath(kPairedDevicePath)); } FakeBluetoothDeviceClient::~FakeBluetoothDeviceClient() { // Clean up Properties structures STLDeleteValues(&properties_map_); } void FakeBluetoothDeviceClient::Init(dbus::Bus* bus) { } void FakeBluetoothDeviceClient::AddObserver(Observer* observer) { observers_.AddObserver(observer); } void FakeBluetoothDeviceClient::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } std::vector<dbus::ObjectPath> FakeBluetoothDeviceClient::GetDevicesForAdapter( const dbus::ObjectPath& adapter_path) { if (adapter_path == dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)) return device_list_; else return std::vector<dbus::ObjectPath>(); } FakeBluetoothDeviceClient::Properties* FakeBluetoothDeviceClient::GetProperties(const dbus::ObjectPath& object_path) { PropertiesMap::iterator iter = properties_map_.find(object_path); if (iter != properties_map_.end()) return iter->second; return NULL; } 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(kMicrosoftMousePath)) { // Must be paired. error_callback.Run(bluetooth_device::kErrorFailed, "Not paired"); return; } else if (properties->paired.value() == true && object_path == dbus::ObjectPath(kUnconnectableDevicePath)) { // 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(); 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() == true) { callback.Run(); properties->connected.ReplaceValue(false); } else { error_callback.Run("org.bluez.Error.NotConnected", "Not Connected"); } } 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<FakeBluetoothProfileManagerClient*>( 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; } // 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<dbus::FileDescriptor> 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<FakeBluetoothProfileManagerClient*>( 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; } pairing_cancelled_ = false; FakeBluetoothAgentManagerClient* fake_bluetooth_agent_manager_client = static_cast<FakeBluetoothAgentManagerClient*>( DBusThreadManager::Get()->GetBluetoothAgentManagerClient()); FakeBluetoothAgentServiceProvider* agent_service_provider = fake_bluetooth_agent_manager_client->GetAgentServiceProvider(); if (agent_service_provider == NULL) { error_callback.Run(kNoResponseError, "Missing agent"); return; } if (object_path == dbus::ObjectPath(kAppleMousePath) || object_path == dbus::ObjectPath(kMicrosoftMousePath) || object_path == dbus::ObjectPath(kUnconnectableDevicePath)) { // 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::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, base::Unretained(this), object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(3 * simulation_interval_ms_)); } else if (object_path == dbus::ObjectPath(kAppleKeyboardPath)) { // Display a Pincode, and wait 7 times the interval before acting as // if the other end accepted it. agent_service_provider->DisplayPinCode(object_path, "123456"); base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, base::Unretained(this), object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(7 * simulation_interval_ms_)); } else if (object_path == dbus::ObjectPath(kVanishingDevicePath)) { // The vanishing device simulates being too far away, and thus times out. base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::TimeoutSimulatedPairing, base::Unretained(this), object_path, error_callback), base::TimeDelta::FromMilliseconds(4 * simulation_interval_ms_)); } else if (object_path == dbus::ObjectPath(kMotorolaKeyboardPath)) { // Display a passkey, and each interval act as if another key was entered // for it. agent_service_provider->DisplayPasskey(object_path, 123456, 0); base::MessageLoop::current()->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(kSonyHeadphonesPath)) { // 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(kPhonePath)) { // Request confirmation of a Passkey. agent_service_provider->RequestConfirmation( object_path, 123456, base::Bind(&FakeBluetoothDeviceClient::ConfirmationCallback, base::Unretained(this), object_path, callback, error_callback)); } else if (object_path == dbus::ObjectPath(kWeirdDevicePath)) { // 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::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::FailSimulatedPairing, base::Unretained(this), object_path, error_callback), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } else { error_callback.Run(kNoResponseError, "No pairing fake"); } } 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::BeginDiscoverySimulation( const dbus::ObjectPath& adapter_path) { VLOG(1) << "starting discovery simulation"; discovery_simulation_step_ = 1; base::MessageLoop::current()->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::SetSimulationIntervalMs(int interval_ms) { simulation_interval_ms_ = interval_ms; } void FakeBluetoothDeviceClient::RemoveDevice( const dbus::ObjectPath& adapter_path, const dbus::ObjectPath& device_path) { std::vector<dbus::ObjectPath>::iterator listiter = std::find(device_list_.begin(), device_list_.end(), device_path); if (listiter == device_list_.end()) return; PropertiesMap::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<FakeBluetoothInputClient*>( DBusThreadManager::Get()->GetBluetoothInputClient()); fake_bluetooth_input_client->RemoveInputDevice(device_path); FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, DeviceRemoved(device_path)); delete properties; properties_map_.erase(iter); } void FakeBluetoothDeviceClient::OnPropertyChanged( const dbus::ObjectPath& object_path, const std::string& 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) { if (std::find(device_list_.begin(), device_list_.end(), dbus::ObjectPath(kAppleMousePath)) == device_list_.end()) { Properties* properties = new Properties(base::Bind( &FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), dbus::ObjectPath(kAppleMousePath))); properties->address.ReplaceValue(kAppleMouseAddress); properties->bluetooth_class.ReplaceValue(kAppleMouseClass); properties->name.ReplaceValue("Fake Apple Magic Mouse"); properties->alias.ReplaceValue(kAppleMouseName); properties->adapter.ReplaceValue( dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); std::vector<std::string> uuids; uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); properties->uuids.ReplaceValue(uuids); properties_map_[dbus::ObjectPath(kAppleMousePath)] = properties; device_list_.push_back(dbus::ObjectPath(kAppleMousePath)); FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, DeviceAdded(dbus::ObjectPath(kAppleMousePath))); } } else if (discovery_simulation_step_ == 4) { if (std::find(device_list_.begin(), device_list_.end(), dbus::ObjectPath(kAppleKeyboardPath)) == device_list_.end()) { Properties *properties = new Properties(base::Bind( &FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), dbus::ObjectPath(kAppleKeyboardPath))); properties->address.ReplaceValue(kAppleKeyboardAddress); properties->bluetooth_class.ReplaceValue(kAppleKeyboardClass); properties->name.ReplaceValue("Fake Apple Wireless Keyboard"); properties->alias.ReplaceValue(kAppleKeyboardName); properties->adapter.ReplaceValue( dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); std::vector<std::string> uuids; uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); properties->uuids.ReplaceValue(uuids); properties_map_[dbus::ObjectPath(kAppleKeyboardPath)] = properties; device_list_.push_back(dbus::ObjectPath(kAppleKeyboardPath)); FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, DeviceAdded(dbus::ObjectPath(kAppleKeyboardPath))); } if (std::find(device_list_.begin(), device_list_.end(), dbus::ObjectPath(kVanishingDevicePath)) == device_list_.end()) { Properties* properties = new Properties(base::Bind( &FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), dbus::ObjectPath(kVanishingDevicePath))); properties->address.ReplaceValue(kVanishingDeviceAddress); properties->bluetooth_class.ReplaceValue(kVanishingDeviceClass); properties->name.ReplaceValue("Fake Vanishing Device"); properties->alias.ReplaceValue(kVanishingDeviceName); properties->adapter.ReplaceValue( dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); properties_map_[dbus::ObjectPath(kVanishingDevicePath)] = properties; device_list_.push_back(dbus::ObjectPath(kVanishingDevicePath)); FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, DeviceAdded(dbus::ObjectPath(kVanishingDevicePath))); } } else if (discovery_simulation_step_ == 7) { if (std::find(device_list_.begin(), device_list_.end(), dbus::ObjectPath(kMicrosoftMousePath)) == device_list_.end()) { Properties* properties = new Properties(base::Bind( &FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), dbus::ObjectPath(kMicrosoftMousePath))); properties->address.ReplaceValue(kMicrosoftMouseAddress); properties->bluetooth_class.ReplaceValue(kMicrosoftMouseClass); properties->name.ReplaceValue("Fake Microsoft Mouse"); properties->alias.ReplaceValue(kMicrosoftMouseName); properties->adapter.ReplaceValue( dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); std::vector<std::string> uuids; uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); properties->uuids.ReplaceValue(uuids); properties_map_[dbus::ObjectPath(kMicrosoftMousePath)] = properties; device_list_.push_back(dbus::ObjectPath(kMicrosoftMousePath)); FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, DeviceAdded(dbus::ObjectPath(kMicrosoftMousePath))); } } else if (discovery_simulation_step_ == 8) { if (std::find(device_list_.begin(), device_list_.end(), dbus::ObjectPath(kMotorolaKeyboardPath)) == device_list_.end()) { Properties* properties = new Properties(base::Bind( &FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), dbus::ObjectPath(kMotorolaKeyboardPath))); properties->address.ReplaceValue(kMotorolaKeyboardAddress); properties->bluetooth_class.ReplaceValue(kMotorolaKeyboardClass); properties->name.ReplaceValue("Fake Motorola Keyboard"); properties->alias.ReplaceValue(kMotorolaKeyboardName); properties->adapter.ReplaceValue( dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); std::vector<std::string> uuids; uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); properties->uuids.ReplaceValue(uuids); properties_map_[dbus::ObjectPath(kMotorolaKeyboardPath)] = properties; device_list_.push_back(dbus::ObjectPath(kMotorolaKeyboardPath)); FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, DeviceAdded(dbus::ObjectPath(kMotorolaKeyboardPath))); } if (std::find(device_list_.begin(), device_list_.end(), dbus::ObjectPath(kSonyHeadphonesPath)) == device_list_.end()) { Properties* properties = new Properties(base::Bind( &FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), dbus::ObjectPath(kSonyHeadphonesPath))); properties->address.ReplaceValue(kSonyHeadphonesAddress); properties->bluetooth_class.ReplaceValue(kSonyHeadphonesClass); properties->name.ReplaceValue("Fake Sony Headphones"); properties->alias.ReplaceValue(kSonyHeadphonesName); properties->adapter.ReplaceValue( dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); properties_map_[dbus::ObjectPath(kSonyHeadphonesPath)] = properties; device_list_.push_back(dbus::ObjectPath(kSonyHeadphonesPath)); FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, DeviceAdded(dbus::ObjectPath(kSonyHeadphonesPath))); } } else if (discovery_simulation_step_ == 10) { if (std::find(device_list_.begin(), device_list_.end(), dbus::ObjectPath(kPhonePath)) == device_list_.end()) { Properties* properties = new Properties(base::Bind( &FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), dbus::ObjectPath(kPhonePath))); properties->address.ReplaceValue(kPhoneAddress); properties->bluetooth_class.ReplaceValue(kPhoneClass); properties->name.ReplaceValue("Fake Phone"); properties->alias.ReplaceValue(kPhoneName); properties->adapter.ReplaceValue( dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); properties_map_[dbus::ObjectPath(kPhonePath)] = properties; device_list_.push_back(dbus::ObjectPath(kPhonePath)); FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, DeviceAdded(dbus::ObjectPath(kPhonePath))); } if (std::find(device_list_.begin(), device_list_.end(), dbus::ObjectPath(kWeirdDevicePath)) == device_list_.end()) { Properties* properties = new Properties(base::Bind( &FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), dbus::ObjectPath(kWeirdDevicePath))); properties->address.ReplaceValue(kWeirdDeviceAddress); properties->bluetooth_class.ReplaceValue(kWeirdDeviceClass); properties->name.ReplaceValue("Fake Weird Device"); properties->alias.ReplaceValue(kWeirdDeviceName); properties->adapter.ReplaceValue( dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); properties_map_[dbus::ObjectPath(kWeirdDevicePath)] = properties; device_list_.push_back(dbus::ObjectPath(kWeirdDevicePath)); FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, DeviceAdded(dbus::ObjectPath(kWeirdDevicePath))); } if (std::find(device_list_.begin(), device_list_.end(), dbus::ObjectPath(kUnconnectableDevicePath)) == device_list_.end()) { Properties* properties = new Properties(base::Bind( &FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), dbus::ObjectPath(kUnconnectableDevicePath))); properties->address.ReplaceValue(kUnconnectableDeviceAddress); properties->bluetooth_class.ReplaceValue(kUnconnectableDeviceClass); properties->name.ReplaceValue("Fake Unconnectable Device"); properties->alias.ReplaceValue(kUnconnectableDeviceName); properties->adapter.ReplaceValue( dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); properties_map_[dbus::ObjectPath(kUnconnectableDevicePath)] = properties; device_list_.push_back(dbus::ObjectPath(kUnconnectableDevicePath)); FOR_EACH_OBSERVER( BluetoothDeviceClient::Observer, observers_, DeviceAdded(dbus::ObjectPath(kUnconnectableDevicePath))); } if (std::find(device_list_.begin(), device_list_.end(), dbus::ObjectPath(kUnpairableDevicePath)) == device_list_.end()) { Properties* properties = new Properties(base::Bind( &FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), dbus::ObjectPath(kUnpairableDevicePath))); properties->address.ReplaceValue(kUnpairableDeviceAddress); properties->bluetooth_class.ReplaceValue(kUnpairableDeviceClass); properties->name.ReplaceValue("Fake Unpairable Device"); properties->alias.ReplaceValue(kUnpairableDeviceName); properties->adapter.ReplaceValue( dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); properties_map_[dbus::ObjectPath(kUnpairableDevicePath)] = properties; device_list_.push_back(dbus::ObjectPath(kUnpairableDevicePath)); FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, DeviceAdded(dbus::ObjectPath(kUnpairableDevicePath))); } } else if (discovery_simulation_step_ == 13) { RemoveDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), dbus::ObjectPath(kVanishingDevicePath)); } else if (discovery_simulation_step_ == 14) { return; } ++discovery_simulation_step_; base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::DiscoverySimulationTimer, base::Unretained(this)), base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); } 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, "Cancaled"); } 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<FakeBluetoothInputClient*>( DBusThreadManager::Get()->GetBluetoothInputClient()); if ((properties->bluetooth_class.value() & 0x001f03) == 0x000500) fake_bluetooth_input_client->AddInputDevice(object_path); } 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) { base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, base::Unretained(this), object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(3 * simulation_interval_ms_)); } else if (status == BluetoothAgentServiceProvider::Delegate::CANCELLED) { base::MessageLoop::current()->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::MessageLoop::current()->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) { base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, base::Unretained(this), object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(3 * simulation_interval_ms_)); } else if (status == BluetoothAgentServiceProvider::Delegate::CANCELLED) { base::MessageLoop::current()->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::MessageLoop::current()->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::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, base::Unretained(this), object_path, callback, error_callback), base::TimeDelta::FromMilliseconds(3 * simulation_interval_ms_)); } else if (status == BluetoothAgentServiceProvider::Delegate::CANCELLED) { base::MessageLoop::current()->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::MessageLoop::current()->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<FakeBluetoothAgentManagerClient*>( 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, 123456, entered); if (entered < 7) { base::MessageLoop::current()->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::MessageLoop::current()->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