| // 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 |