diff options
19 files changed, 1409 insertions, 601 deletions
diff --git a/chromeos/dbus/fake_bluetooth_device_client.cc b/chromeos/dbus/fake_bluetooth_device_client.cc index 08f748a..b77a956 100644 --- a/chromeos/dbus/fake_bluetooth_device_client.cc +++ b/chromeos/dbus/fake_bluetooth_device_client.cc @@ -400,105 +400,7 @@ void FakeBluetoothDeviceClient::Pair( 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"); - } + SimulatePairing(object_path, false, callback, error_callback); } void FakeBluetoothDeviceClient::CancelPairing( @@ -534,6 +436,106 @@ 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; + + Properties* properties = new Properties(base::Bind( + &FakeBluetoothDeviceClient::OnPropertyChanged, + base::Unretained(this), + device_path)); + properties->adapter.ReplaceValue(adapter_path); + + if (device_path == dbus::ObjectPath(kAppleMousePath)) { + properties->address.ReplaceValue(kAppleMouseAddress); + properties->bluetooth_class.ReplaceValue(kAppleMouseClass); + properties->name.ReplaceValue("Fake Apple Magic Mouse"); + properties->alias.ReplaceValue(kAppleMouseName); + + std::vector<std::string> uuids; + uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); + properties->uuids.ReplaceValue(uuids); + + } else if (device_path == dbus::ObjectPath(kAppleKeyboardPath)) { + properties->address.ReplaceValue(kAppleKeyboardAddress); + properties->bluetooth_class.ReplaceValue(kAppleKeyboardClass); + properties->name.ReplaceValue("Fake Apple Wireless Keyboard"); + properties->alias.ReplaceValue(kAppleKeyboardName); + + std::vector<std::string> 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("Fake Vanishing Device"); + properties->alias.ReplaceValue(kVanishingDeviceName); + + } else if (device_path == dbus::ObjectPath(kMicrosoftMousePath)) { + properties->address.ReplaceValue(kMicrosoftMouseAddress); + properties->bluetooth_class.ReplaceValue(kMicrosoftMouseClass); + properties->name.ReplaceValue("Fake Microsoft Mouse"); + properties->alias.ReplaceValue(kMicrosoftMouseName); + + std::vector<std::string> uuids; + uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); + properties->uuids.ReplaceValue(uuids); + + } else if (device_path == dbus::ObjectPath(kMotorolaKeyboardPath)) { + properties->address.ReplaceValue(kMotorolaKeyboardAddress); + properties->bluetooth_class.ReplaceValue(kMotorolaKeyboardClass); + properties->name.ReplaceValue("Fake Motorola Keyboard"); + properties->alias.ReplaceValue(kMotorolaKeyboardName); + + std::vector<std::string> uuids; + uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); + properties->uuids.ReplaceValue(uuids); + + } else if (device_path == dbus::ObjectPath(kSonyHeadphonesPath)) { + properties->address.ReplaceValue(kSonyHeadphonesAddress); + properties->bluetooth_class.ReplaceValue(kSonyHeadphonesClass); + properties->name.ReplaceValue("Fake Sony Headphones"); + properties->alias.ReplaceValue(kSonyHeadphonesName); + + } else if (device_path == dbus::ObjectPath(kPhonePath)) { + properties->address.ReplaceValue(kPhoneAddress); + properties->bluetooth_class.ReplaceValue(kPhoneClass); + properties->name.ReplaceValue("Fake Phone"); + properties->alias.ReplaceValue(kPhoneName); + + } else if (device_path == dbus::ObjectPath(kWeirdDevicePath)) { + properties->address.ReplaceValue(kWeirdDeviceAddress); + properties->bluetooth_class.ReplaceValue(kWeirdDeviceClass); + properties->name.ReplaceValue("Fake Weird Device"); + properties->alias.ReplaceValue(kWeirdDeviceName); + + } else if (device_path == dbus::ObjectPath(kUnconnectableDevicePath)) { + properties->address.ReplaceValue(kUnconnectableDeviceAddress); + properties->bluetooth_class.ReplaceValue(kUnconnectableDeviceClass); + properties->name.ReplaceValue("Fake Unconnectable Device"); + 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 { + NOTREACHED(); + + } + + properties_map_[device_path] = properties; + device_list_.push_back(device_path); + FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, + DeviceAdded(device_path)); +} + void FakeBluetoothDeviceClient::RemoveDevice( const dbus::ObjectPath& adapter_path, const dbus::ObjectPath& device_path) { @@ -578,222 +580,34 @@ void FakeBluetoothDeviceClient::DiscoverySimulationTimer() { // 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))); - } + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + 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))); - } + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kAppleKeyboardPath)); + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + 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))); - } + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + 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))); - } + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kMotorolaKeyboardPath)); + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + 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))); - } + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kPhonePath)); + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kWeirdDevicePath)); + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kUnconnectableDevicePath)); + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kUnpairableDevicePath)); } else if (discovery_simulation_step_ == 13) { RemoveDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), @@ -813,6 +627,109 @@ void FakeBluetoothDeviceClient::DiscoverySimulationTimer() { } +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<FakeBluetoothAgentManagerClient*>( + DBusThreadManager::Get()->GetBluetoothAgentManagerClient()); + FakeBluetoothAgentServiceProvider* agent_service_provider = + fake_bluetooth_agent_manager_client->GetAgentServiceProvider(); + CHECK(agent_service_provider != NULL); + + 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::CompleteSimulatedPairing( const dbus::ObjectPath& object_path, const base::Closure& callback, @@ -822,7 +739,7 @@ void FakeBluetoothDeviceClient::CompleteSimulatedPairing( pairing_cancelled_ = false; error_callback.Run(bluetooth_device::kErrorAuthenticationCanceled, - "Cancaled"); + "Cancelled"); } else { Properties* properties = GetProperties(object_path); diff --git a/chromeos/dbus/fake_bluetooth_device_client.h b/chromeos/dbus/fake_bluetooth_device_client.h index 95b21ba..e232416 100644 --- a/chromeos/dbus/fake_bluetooth_device_client.h +++ b/chromeos/dbus/fake_bluetooth_device_client.h @@ -72,14 +72,27 @@ class CHROMEOS_EXPORT FakeBluetoothDeviceClient void SetSimulationIntervalMs(int interval_ms); - // Simulate discovery of devices for the given adapter. + // Simulates discovery of devices for the given adapter. void BeginDiscoverySimulation(const dbus::ObjectPath& adapter_path); void EndDiscoverySimulation(const dbus::ObjectPath& adapter_path); - // Remove a device from the set we return for the given adapter. + // Creates a device from the set we return for the given adapter. + void CreateDevice(const dbus::ObjectPath& adapter_path, + const dbus::ObjectPath& device_path); + + // Removes a device from the set we return for the given adapter. void RemoveDevice(const dbus::ObjectPath& adapter_path, const dbus::ObjectPath& device_path); + // Simulates a pairing for the device with the given D-Bus object path, + // |object_path|. Set |incoming_request| to true if simulating an incoming + // pairing request, false for an outgoing one. On successful completion + // |callback| will be called, on failure, |error_callback| is called. + void SimulatePairing(const dbus::ObjectPath& object_path, + bool incoming_request, + const base::Closure& callback, + const ErrorCallback& error_callback); + // Object paths, names, addresses and bluetooth classes of the devices // we can emulate. static const char kPairedDevicePath[]; diff --git a/device/bluetooth/bluetooth.gyp b/device/bluetooth/bluetooth.gyp index c4012bd..1f13ef3 100644 --- a/device/bluetooth/bluetooth.gyp +++ b/device/bluetooth/bluetooth.gyp @@ -48,6 +48,8 @@ 'bluetooth_init_win.cc', 'bluetooth_init_win.h', 'bluetooth_out_of_band_pairing_data.h', + 'bluetooth_pairing_chromeos.cc', + 'bluetooth_pairing_chromeos.h', 'bluetooth_profile.cc', 'bluetooth_profile.h', 'bluetooth_profile_chromeos.cc', diff --git a/device/bluetooth/bluetooth_adapter.cc b/device/bluetooth/bluetooth_adapter.cc index bb372f1..1462f49 100644 --- a/device/bluetooth/bluetooth_adapter.cc +++ b/device/bluetooth/bluetooth_adapter.cc @@ -62,4 +62,38 @@ const BluetoothDevice* BluetoothAdapter::GetDevice( return NULL; } +void BluetoothAdapter::AddPairingDelegate( + BluetoothDevice::PairingDelegate* pairing_delegate, + PairingDelegatePriority priority) { + // Remove the delegate, if it already exists, before inserting to allow a + // change of priority. + RemovePairingDelegate(pairing_delegate); + + // Find the first point with a lower priority, or the end of the list. + std::list<PairingDelegatePair>::iterator iter = pairing_delegates_.begin(); + while (iter != pairing_delegates_.end() && iter->second >= priority) + ++iter; + + pairing_delegates_.insert(iter, std::make_pair(pairing_delegate, priority)); +} + +void BluetoothAdapter::RemovePairingDelegate( + BluetoothDevice::PairingDelegate* pairing_delegate) { + for (std::list<PairingDelegatePair>::iterator iter = + pairing_delegates_.begin(); iter != pairing_delegates_.end(); ++iter) { + if (iter->first == pairing_delegate) { + RemovePairingDelegateInternal(pairing_delegate); + pairing_delegates_.erase(iter); + return; + } + } +} + +BluetoothDevice::PairingDelegate* BluetoothAdapter::DefaultPairingDelegate() { + if (pairing_delegates_.empty()) + return NULL; + + return pairing_delegates_.front().first; +} + } // namespace device diff --git a/device/bluetooth/bluetooth_adapter.h b/device/bluetooth/bluetooth_adapter.h index 42fe8db..cec53e0 100644 --- a/device/bluetooth/bluetooth_adapter.h +++ b/device/bluetooth/bluetooth_adapter.h @@ -5,17 +5,17 @@ #ifndef DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_H_ #define DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_H_ +#include <list> #include <map> #include <string> -#include <vector> +#include <utility> #include "base/callback.h" #include "base/memory/ref_counted.h" +#include "device/bluetooth/bluetooth_device.h" namespace device { -class BluetoothDevice; - struct BluetoothOutOfBandPairingData; // BluetoothAdapter represents a local Bluetooth adapter which may be used to @@ -175,6 +175,33 @@ class BluetoothAdapter : public base::RefCounted<BluetoothAdapter> { const BluetoothOutOfBandPairingDataCallback& callback, const ErrorCallback& error_callback) = 0; + // Possible priorities for AddPairingDelegate(), low is intended for + // permanent UI and high is intended for interactive UI or applications. + enum PairingDelegatePriority { + PAIRING_DELEGATE_PRIORITY_LOW, + PAIRING_DELEGATE_PRIORITY_HIGH + }; + + // Adds a default pairing delegate with priority |priority|, method calls + // will be made on |pairing_delegate| for incoming pairing requests if the + // priority is higher than any other registered, or for those of the same + // priority, the first registered. + // + // |pairing_delegate| must not be freed without first calling + // RemovePairingDelegate(). + virtual void AddPairingDelegate( + BluetoothDevice::PairingDelegate* pairing_delegate, + PairingDelegatePriority priority); + + // Removes a previously added pairing delegate. + virtual void RemovePairingDelegate( + BluetoothDevice::PairingDelegate* pairing_delegate); + + // Returns the first registered pairing delegate with the highest priority, + // or NULL if no delegate is registered. Used to select the delegate for + // incoming pairing requests. + virtual BluetoothDevice::PairingDelegate* DefaultPairingDelegate(); + protected: friend class base::RefCounted<BluetoothAdapter>; BluetoothAdapter(); @@ -215,12 +242,23 @@ class BluetoothAdapter : public base::RefCounted<BluetoothAdapter> { virtual void RemoveDiscoverySession(const base::Closure& callback, const ErrorCallback& error_callback) = 0; + // Called by RemovePairingDelegate() in order to perform any class-specific + // internal functionality necessary to remove the pairing delegate, such as + // cleaning up ongoing pairings using it. + virtual void RemovePairingDelegateInternal( + BluetoothDevice::PairingDelegate* pairing_delegate) = 0; + // Devices paired with, connected to, discovered by, or visible to the // adapter. The key is the Bluetooth address of the device and the value // is the BluetoothDevice object whose lifetime is managed by the // adapter instance. typedef std::map<const std::string, BluetoothDevice*> DevicesMap; DevicesMap devices_; + + // Default pairing delegates registered with the adapter. + typedef std::pair<BluetoothDevice::PairingDelegate*, + PairingDelegatePriority> PairingDelegatePair; + std::list<PairingDelegatePair> pairing_delegates_; }; } // namespace device diff --git a/device/bluetooth/bluetooth_adapter_chromeos.cc b/device/bluetooth/bluetooth_adapter_chromeos.cc index 29bc05e..9539ebb 100644 --- a/device/bluetooth/bluetooth_adapter_chromeos.cc +++ b/device/bluetooth/bluetooth_adapter_chromeos.cc @@ -18,6 +18,7 @@ #include "chromeos/dbus/dbus_thread_manager.h" #include "device/bluetooth/bluetooth_device.h" #include "device/bluetooth/bluetooth_device_chromeos.h" +#include "device/bluetooth/bluetooth_pairing_chromeos.h" #include "third_party/cros_system_api/dbus/service_constants.h" using device::BluetoothAdapter; @@ -29,19 +30,6 @@ namespace { // exist per D-Bus connection, it just has to be unique within Chromium. const char kAgentPath[] = "/org/chromium/bluetooth_agent"; -// Histogram enumerations for pairing methods. -enum UMAPairingMethod { - UMA_PAIRING_METHOD_NONE, - UMA_PAIRING_METHOD_REQUEST_PINCODE, - UMA_PAIRING_METHOD_REQUEST_PASSKEY, - UMA_PAIRING_METHOD_DISPLAY_PINCODE, - UMA_PAIRING_METHOD_DISPLAY_PASSKEY, - UMA_PAIRING_METHOD_CONFIRM_PASSKEY, - // NOTE: Add new pairing methods immediately above this line. Make sure to - // update the enum list in tools/histogram/histograms.xml accordingly. - UMA_PAIRING_METHOD_COUNT -}; - void OnUnregisterAgentError(const std::string& error_name, const std::string& error_message) { LOG(WARNING) << "Failed to unregister pairing agent: " @@ -219,6 +207,22 @@ void BluetoothAdapterChromeOS::ReadLocalOutOfBandPairingData( error_callback.Run(); } +void BluetoothAdapterChromeOS::RemovePairingDelegateInternal( + BluetoothDevice::PairingDelegate* pairing_delegate) { + // Before removing a pairing delegate make sure that there aren't any devices + // currently using it; if there are, clear the pairing context which will + // make any responses no-ops. + for (DevicesMap::iterator iter = devices_.begin(); + iter != devices_.end(); ++iter) { + BluetoothDeviceChromeOS* device_chromeos = + static_cast<BluetoothDeviceChromeOS*>(iter->second); + + BluetoothPairingChromeOS* pairing = device_chromeos->GetPairing(); + if (pairing && pairing->GetPairingDelegate() == pairing_delegate) + device_chromeos->EndPairing(); + } +} + void BluetoothAdapterChromeOS::AdapterAdded( const dbus::ObjectPath& object_path) { // Set the adapter to the newly added adapter only if no adapter is present. @@ -352,22 +356,13 @@ void BluetoothAdapterChromeOS::RequestPinCode( DCHECK(agent_.get()); VLOG(1) << device_path.value() << ": RequestPinCode"; - BluetoothDeviceChromeOS* device_chromeos; - PairingContext* pairing_context; - if (!GetDeviceAndPairingContext(device_path, - &device_chromeos, &pairing_context)) { + BluetoothPairingChromeOS* pairing = GetPairing(device_path); + if (!pairing) { callback.Run(REJECTED, ""); return; } - UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", - UMA_PAIRING_METHOD_REQUEST_PINCODE, - UMA_PAIRING_METHOD_COUNT); - - DCHECK(pairing_context->pincode_callback_.is_null()); - pairing_context->pincode_callback_ = callback; - pairing_context->pairing_delegate_->RequestPinCode(device_chromeos); - pairing_context->pairing_delegate_used_ = true; + pairing->RequestPinCode(callback); } void BluetoothAdapterChromeOS::DisplayPinCode( @@ -376,18 +371,11 @@ void BluetoothAdapterChromeOS::DisplayPinCode( DCHECK(agent_.get()); VLOG(1) << device_path.value() << ": DisplayPinCode: " << pincode; - BluetoothDeviceChromeOS* device_chromeos; - PairingContext* pairing_context; - if (!GetDeviceAndPairingContext(device_path, - &device_chromeos, &pairing_context)) + BluetoothPairingChromeOS* pairing = GetPairing(device_path); + if (!pairing) return; - UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", - UMA_PAIRING_METHOD_DISPLAY_PINCODE, - UMA_PAIRING_METHOD_COUNT); - - pairing_context->pairing_delegate_->DisplayPinCode(device_chromeos, pincode); - pairing_context->pairing_delegate_used_ = true; + pairing->DisplayPinCode(pincode); } void BluetoothAdapterChromeOS::RequestPasskey( @@ -396,22 +384,13 @@ void BluetoothAdapterChromeOS::RequestPasskey( DCHECK(agent_.get()); VLOG(1) << device_path.value() << ": RequestPasskey"; - BluetoothDeviceChromeOS* device_chromeos; - PairingContext* pairing_context; - if (!GetDeviceAndPairingContext(device_path, - &device_chromeos, &pairing_context)) { + BluetoothPairingChromeOS* pairing = GetPairing(device_path); + if (!pairing) { callback.Run(REJECTED, 0); return; } - UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", - UMA_PAIRING_METHOD_REQUEST_PASSKEY, - UMA_PAIRING_METHOD_COUNT); - - DCHECK(pairing_context->passkey_callback_.is_null()); - pairing_context->passkey_callback_ = callback; - pairing_context->pairing_delegate_->RequestPasskey(device_chromeos); - pairing_context->pairing_delegate_used_ = true; + pairing->RequestPasskey(callback); } void BluetoothAdapterChromeOS::DisplayPasskey( @@ -422,23 +401,14 @@ void BluetoothAdapterChromeOS::DisplayPasskey( VLOG(1) << device_path.value() << ": DisplayPasskey: " << passkey << " (" << entered << " entered)"; - BluetoothDeviceChromeOS* device_chromeos; - PairingContext* pairing_context; - if (!GetDeviceAndPairingContext(device_path, - &device_chromeos, &pairing_context)) + BluetoothPairingChromeOS* pairing = GetPairing(device_path); + if (!pairing) return; if (entered == 0) - UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", - UMA_PAIRING_METHOD_DISPLAY_PASSKEY, - UMA_PAIRING_METHOD_COUNT); - - if (entered == 0) - pairing_context->pairing_delegate_->DisplayPasskey(device_chromeos, - passkey); + pairing->DisplayPasskey(passkey); - pairing_context->pairing_delegate_->KeysEntered(device_chromeos, entered); - pairing_context->pairing_delegate_used_ = true; + pairing->KeysEntered(entered); } void BluetoothAdapterChromeOS::RequestConfirmation( @@ -448,22 +418,13 @@ void BluetoothAdapterChromeOS::RequestConfirmation( DCHECK(agent_.get()); VLOG(1) << device_path.value() << ": RequestConfirmation: " << passkey; - BluetoothDeviceChromeOS* device_chromeos; - PairingContext* pairing_context; - if (!GetDeviceAndPairingContext(device_path, - &device_chromeos, &pairing_context)) { + BluetoothPairingChromeOS* pairing = GetPairing(device_path); + if (!pairing) { callback.Run(REJECTED); return; } - UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", - UMA_PAIRING_METHOD_CONFIRM_PASSKEY, - UMA_PAIRING_METHOD_COUNT); - - DCHECK(pairing_context->confirmation_callback_.is_null()); - pairing_context->confirmation_callback_ = callback; - pairing_context->pairing_delegate_->ConfirmPasskey(device_chromeos, passkey); - pairing_context->pairing_delegate_used_ = true; + pairing->RequestConfirmation(passkey, callback); } void BluetoothAdapterChromeOS::RequestAuthorization( @@ -492,110 +453,37 @@ void BluetoothAdapterChromeOS::Cancel() { VLOG(1) << "Cancel"; } -bool BluetoothAdapterChromeOS::PairingContext::ExpectingPinCode() const { - return !pincode_callback_.is_null(); -} - -bool BluetoothAdapterChromeOS::PairingContext::ExpectingPasskey() const { - return !passkey_callback_.is_null(); -} - -bool BluetoothAdapterChromeOS::PairingContext::ExpectingConfirmation() const { - return !confirmation_callback_.is_null(); -} - -void BluetoothAdapterChromeOS::PairingContext::SetPinCode( - const std::string& pincode) { - if (pincode_callback_.is_null()) - return; - - pincode_callback_.Run(SUCCESS, pincode); - pincode_callback_.Reset(); -} - -void BluetoothAdapterChromeOS::PairingContext::SetPasskey(uint32 passkey) { - if (passkey_callback_.is_null()) - return; - - passkey_callback_.Run(SUCCESS, passkey); - passkey_callback_.Reset(); -} - -void BluetoothAdapterChromeOS::PairingContext::ConfirmPairing() { - if (confirmation_callback_.is_null()) - return; - - confirmation_callback_.Run(SUCCESS); - confirmation_callback_.Reset(); -} - -bool BluetoothAdapterChromeOS::PairingContext::RejectPairing() { - return RunPairingCallbacks(REJECTED); -} - -bool BluetoothAdapterChromeOS::PairingContext::CancelPairing() { - return RunPairingCallbacks(CANCELLED); -} - -BluetoothAdapterChromeOS::PairingContext::PairingContext( - BluetoothDevice::PairingDelegate* pairing_delegate) - : pairing_delegate_(pairing_delegate), - pairing_delegate_used_(false) { - VLOG(1) << "Created PairingContext"; -} - -BluetoothAdapterChromeOS::PairingContext::~PairingContext() { - VLOG(1) << "Destroying PairingContext"; - - if (!pairing_delegate_used_) - UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", - UMA_PAIRING_METHOD_NONE, - UMA_PAIRING_METHOD_COUNT); +void BluetoothAdapterChromeOS::OnRegisterAgent() { + VLOG(1) << "Pairing agent registered, requesting to be made default"; - DCHECK(pincode_callback_.is_null()); - DCHECK(passkey_callback_.is_null()); - DCHECK(confirmation_callback_.is_null()); + DBusThreadManager::Get()->GetBluetoothAgentManagerClient()-> + RequestDefaultAgent( + dbus::ObjectPath(kAgentPath), + base::Bind(&BluetoothAdapterChromeOS::OnRequestDefaultAgent, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothAdapterChromeOS::OnRequestDefaultAgentError, + weak_ptr_factory_.GetWeakPtr())); - pairing_delegate_ = NULL; } -bool BluetoothAdapterChromeOS::PairingContext::RunPairingCallbacks( - BluetoothAgentServiceProvider::Delegate::Status status) { - pairing_delegate_used_ = true; - - bool callback_run = false; - if (!pincode_callback_.is_null()) { - pincode_callback_.Run(status, ""); - pincode_callback_.Reset(); - callback_run = true; - } - - if (!passkey_callback_.is_null()) { - passkey_callback_.Run(status, 0); - passkey_callback_.Reset(); - callback_run = true; - } - - if (!confirmation_callback_.is_null()) { - confirmation_callback_.Run(status); - confirmation_callback_.Reset(); - callback_run = true; - } +void BluetoothAdapterChromeOS::OnRegisterAgentError( + const std::string& error_name, + const std::string& error_message) { + LOG(WARNING) << ": Failed to register pairing agent: " + << error_name << ": " << error_message; - return callback_run; + agent_.reset(); } -void BluetoothAdapterChromeOS::OnRegisterAgent() { - VLOG(1) << "Pairing agent registered"; +void BluetoothAdapterChromeOS::OnRequestDefaultAgent() { + VLOG(1) << "Pairing agent now default"; } -void BluetoothAdapterChromeOS::OnRegisterAgentError( +void BluetoothAdapterChromeOS::OnRequestDefaultAgentError( const std::string& error_name, const std::string& error_message) { - LOG(WARNING) << ": Failed to register pairing agent: " + LOG(WARNING) << ": Failed to make pairing agent default: " << error_name << ": " << error_message; - - agent_.reset(); } BluetoothDeviceChromeOS* @@ -612,26 +500,27 @@ BluetoothAdapterChromeOS::GetDeviceWithPath( return NULL; } -bool BluetoothAdapterChromeOS::GetDeviceAndPairingContext( - const dbus::ObjectPath& object_path, - BluetoothDeviceChromeOS** device_chromeos, - PairingContext** pairing_context) +BluetoothPairingChromeOS* BluetoothAdapterChromeOS::GetPairing( + const dbus::ObjectPath& object_path) { - *device_chromeos = GetDeviceWithPath(object_path); + BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path); if (!device_chromeos) { LOG(WARNING) << "Pairing Agent request for unknown device: " << object_path.value(); - return false; + return NULL; } - *pairing_context = (*device_chromeos)->pairing_context_.get(); - if (*pairing_context) - return true; + BluetoothPairingChromeOS* pairing = device_chromeos->GetPairing(); + if (pairing) + return pairing; + + // The device doesn't have its own pairing context, so this is an incoming + // pairing request that should use our best default delegate (if we have one). + BluetoothDevice::PairingDelegate* pairing_delegate = DefaultPairingDelegate(); + if (!pairing_delegate) + return NULL; - // TODO(keybuk): this is the point we need a default pairing delegate, create - // a PairingContext with that passed in, set it as the context on the device - // and return true. - return false; + return device_chromeos->BeginPairing(pairing_delegate); } void BluetoothAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) { diff --git a/device/bluetooth/bluetooth_adapter_chromeos.h b/device/bluetooth/bluetooth_adapter_chromeos.h index f48a8a8..c8b5ea6 100644 --- a/device/bluetooth/bluetooth_adapter_chromeos.h +++ b/device/bluetooth/bluetooth_adapter_chromeos.h @@ -27,6 +27,7 @@ namespace chromeos { class BluetoothChromeOSTest; class BluetoothDeviceChromeOS; +class BluetoothPairingChromeOS; // The BluetoothAdapterChromeOS class implements BluetoothAdapter for the // Chrome OS platform. @@ -65,6 +66,11 @@ class BluetoothAdapterChromeOS callback, const ErrorCallback& error_callback) OVERRIDE; + protected: + // BluetoothAdapter override + virtual void RemovePairingDelegateInternal( + device::BluetoothDevice::PairingDelegate* pairing_delegate) OVERRIDE; + private: friend class device::BluetoothAdapterFactory; friend class BluetoothChromeOSTest; @@ -123,96 +129,29 @@ class BluetoothAdapterChromeOS // PairingContext is an API between BluetoothAdapterChromeOS and // BluetoothDeviceChromeOS for a single pairing attempt, wrapping the // callbacks of the underlying BluetoothAgentServiceProvider object. - class PairingContext { - public: - ~PairingContext(); - - // Indicates whether the device is currently pairing and expecting a - // PIN Code to be returned. - bool ExpectingPinCode() const; - - // Indicates whether the device is currently pairing and expecting a - // Passkey to be returned. - bool ExpectingPasskey() const; - - // Indicates whether the device is currently pairing and expecting - // confirmation of a displayed passkey. - bool ExpectingConfirmation() const; - - // Sends the PIN code |pincode| to the remote device during pairing. - // - // PIN Codes are generally required for Bluetooth 2.0 and earlier devices - // for which there is no automatic pairing or special handling. - void SetPinCode(const std::string& pincode); - - // Sends the Passkey |passkey| to the remote device during pairing. - // - // Passkeys are generally required for Bluetooth 2.1 and later devices - // which cannot provide input or display on their own, and don't accept - // passkey-less pairing, and are a numeric in the range 0-999999. - void SetPasskey(uint32 passkey); - - // Confirms to the remote device during pairing that a passkey provided by - // the ConfirmPasskey() delegate call is displayed on both devices. - void ConfirmPairing(); - - // Rejects a pairing or connection request from a remote device, returns - // false if there was no way to reject the pairing. - bool RejectPairing(); - - // Cancels a pairing or connection attempt to a remote device, returns - // false if there was no way to cancel the pairing. - bool CancelPairing(); - - private: - friend class BluetoothAdapterChromeOS; - friend class BluetoothDeviceChromeOS; - - explicit PairingContext( - device::BluetoothDevice::PairingDelegate* pairing_delegate_); - - // Internal method to response to the relevant callback for a RejectPairing - // or CancelPairing call. - bool RunPairingCallbacks( - BluetoothAgentServiceProvider::Delegate::Status status); - - // UI Pairing Delegate to make method calls on, this must live as long as - // the object capturing the PairingContext. - device::BluetoothDevice::PairingDelegate* pairing_delegate_; - - // Flag to indicate whether any pairing delegate method has been called - // during pairing. Used to determine whether we need to log the - // "no pairing interaction" metric. - bool pairing_delegate_used_; - - // During pairing these callbacks are set to those provided by method calls - // made on the BluetoothAdapterChromeOS instance by its respective - // BluetoothAgentServiceProvider instance, and are called by our own - // method calls such as SetPinCode() and SetPasskey(). - PinCodeCallback pincode_callback_; - PasskeyCallback passkey_callback_; - ConfirmationCallback confirmation_callback_; - }; - // Called by dbus:: on completion of the D-Bus method call to register the // pairing agent. void OnRegisterAgent(); void OnRegisterAgentError(const std::string& error_name, const std::string& error_message); + // Called by dbus:: on completion of the D-Bus method call to request that + // the pairing agent be made the default. + void OnRequestDefaultAgent(); + void OnRequestDefaultAgentError(const std::string& error_name, + const std::string& error_message); + // Internal method used to locate the device object by object path // (the devices map and BluetoothDevice methods are by address) BluetoothDeviceChromeOS* GetDeviceWithPath( const dbus::ObjectPath& object_path); - // Internal method to obtain the ChromeOS BluetoothDevice object, returned in - // |device_chromeos| and associated PairingContext, returned in - // |pairing_context| for the device with path |object_path|. - // Returns true on success, false if device doesn't exist or there is no - // pairing context for it. - bool GetDeviceAndPairingContext(const dbus::ObjectPath& object_path, - BluetoothDeviceChromeOS** device_chromeos, - PairingContext** pairing_context); + // Internal method to obtain a BluetoothPairingChromeOS object for the device + // with path |object_path|. Returns the existing pairing object if the device + // already has one (usually an outgoing connection in progress) or a new + // pairing object with the default pairing delegate if not. If no default + // pairing object exists, NULL will be returned. + BluetoothPairingChromeOS* GetPairing(const dbus::ObjectPath& object_path); // Set the tracked adapter to the one in |object_path|, this object will // subsequently operate on that adapter until it is removed. diff --git a/device/bluetooth/bluetooth_adapter_mac.h b/device/bluetooth/bluetooth_adapter_mac.h index 8098206..064b6bf 100644 --- a/device/bluetooth/bluetooth_adapter_mac.h +++ b/device/bluetooth/bluetooth_adapter_mac.h @@ -75,6 +75,11 @@ class BluetoothAdapterMac : public BluetoothAdapter { IOReturn error, bool aborted); + protected: + // BluetoothAdapter override + virtual void RemovePairingDelegateInternal( + device::BluetoothDevice::PairingDelegate* pairing_delegate) OVERRIDE; + private: friend class BluetoothAdapterFactory; friend class BluetoothAdapterMacTest; diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm index e330965..9ac7caa 100644 --- a/device/bluetooth/bluetooth_adapter_mac.mm +++ b/device/bluetooth/bluetooth_adapter_mac.mm @@ -198,6 +198,10 @@ void BluetoothAdapterMac::RemoveDiscoverySession( MaybeStopDeviceInquiry(); } +void BluetoothAdapterMac::RemovePairingDelegateInternal( + BluetoothDevice::PairingDelegate* pairing_delegate) { +} + void BluetoothAdapterMac::Init() { ui_task_runner_ = base::ThreadTaskRunnerHandle::Get(); PollAdapter(); diff --git a/device/bluetooth/bluetooth_adapter_unittest.cc b/device/bluetooth/bluetooth_adapter_unittest.cc new file mode 100644 index 0000000..c063bed --- /dev/null +++ b/device/bluetooth/bluetooth_adapter_unittest.cc @@ -0,0 +1,187 @@ +// Copyright 2014 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 "base/memory/ref_counted.h" +#include "device/bluetooth/bluetooth_adapter.h" +#include "device/bluetooth/bluetooth_device.h" +#include "testing/gtest/include/gtest/gtest.h" + +using device::BluetoothAdapter; +using device::BluetoothDevice; + +namespace device { + +class TestBluetoothAdapter : public BluetoothAdapter { + public: + TestBluetoothAdapter() { + } + + virtual void AddObserver(BluetoothAdapter::Observer* observer) OVERRIDE { + } + + virtual void RemoveObserver(BluetoothAdapter::Observer* observer) OVERRIDE { + + } + + virtual std::string GetAddress() const OVERRIDE { + return ""; + } + + virtual std::string GetName() const OVERRIDE { + return ""; + } + + virtual void SetName(const std::string& name, + const base::Closure& callback, + const ErrorCallback& error_callback) OVERRIDE { + } + + virtual bool IsInitialized() const OVERRIDE { + return false; + } + + virtual bool IsPresent() const OVERRIDE { + return false; + } + + virtual bool IsPowered() const OVERRIDE { + return false; + } + + virtual void SetPowered( + bool powered, + const base::Closure& callback, + const ErrorCallback& error_callback) OVERRIDE { + } + + virtual bool IsDiscoverable() const OVERRIDE { + return false; + } + + virtual void SetDiscoverable( + bool discoverable, + const base::Closure& callback, + const ErrorCallback& error_callback) OVERRIDE { + } + + virtual bool IsDiscovering() const OVERRIDE { + return false; + } + + virtual void StartDiscovering( + const base::Closure& callback, + const ErrorCallback& error_callback) OVERRIDE { + } + + virtual void StopDiscovering( + const base::Closure& callback, + const ErrorCallback& error_callback) OVERRIDE { + } + + virtual void AddDiscoverySession( + const base::Closure& callback, + const ErrorCallback& error_callback) OVERRIDE { + } + + virtual void RemoveDiscoverySession( + const base::Closure& callback, + const ErrorCallback& error_callback) OVERRIDE { + } + + virtual void ReadLocalOutOfBandPairingData( + const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback, + const ErrorCallback& error_callback) OVERRIDE { + } + + protected: + virtual ~TestBluetoothAdapter() {} + + virtual void RemovePairingDelegateInternal( + BluetoothDevice::PairingDelegate* pairing_delegate) OVERRIDE { + } +}; + +class TestPairingDelegate : public BluetoothDevice::PairingDelegate { + public: + virtual void RequestPinCode(BluetoothDevice* device) OVERRIDE {} + virtual void RequestPasskey(BluetoothDevice* device) OVERRIDE {} + virtual void DisplayPinCode(BluetoothDevice* device, + const std::string& pincode) OVERRIDE {} + virtual void DisplayPasskey(BluetoothDevice* device, + uint32 passkey) OVERRIDE {} + virtual void KeysEntered(BluetoothDevice* device, + uint32 entered) OVERRIDE {} + virtual void ConfirmPasskey(BluetoothDevice* device, + uint32 passkey) OVERRIDE {} +}; + + +TEST(BluetoothAdapterTest, NoDefaultPairingDelegate) { + scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter(); + + // Verify that when there is no registered pairing delegate, NULL is returned. + EXPECT_TRUE(adapter->DefaultPairingDelegate() == NULL); +} + +TEST(BluetoothAdapterTest, OneDefaultPairingDelegate) { + scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter(); + + // Verify that when there is one registered pairing delegate, it is returned. + TestPairingDelegate delegate; + + adapter->AddPairingDelegate(&delegate, + BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW); + + EXPECT_EQ(&delegate, adapter->DefaultPairingDelegate()); +} + +TEST(BluetoothAdapterTest, SamePriorityDelegates) { + scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter(); + + // Verify that when there are two registered pairing delegates of the same + // priority, the first one registered is returned. + TestPairingDelegate delegate1, delegate2; + + adapter->AddPairingDelegate(&delegate1, + BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW); + adapter->AddPairingDelegate(&delegate2, + BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW); + + EXPECT_EQ(&delegate1, adapter->DefaultPairingDelegate()); + + // After unregistering the first, the second can be returned. + adapter->RemovePairingDelegate(&delegate1); + + EXPECT_EQ(&delegate2, adapter->DefaultPairingDelegate()); +} + +TEST(BluetoothAdapterTest, HighestPriorityDelegate) { + scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter(); + + // Verify that when there are two registered pairing delegates, the one with + // the highest priority is returned. + TestPairingDelegate delegate1, delegate2; + + adapter->AddPairingDelegate(&delegate1, + BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW); + adapter->AddPairingDelegate(&delegate2, + BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH); + + EXPECT_EQ(&delegate2, adapter->DefaultPairingDelegate()); +} + +TEST(BluetoothAdapterTest, UnregisterDelegate) { + scoped_refptr<BluetoothAdapter> adapter = new TestBluetoothAdapter(); + + // Verify that after unregistering a delegate, NULL is returned. + TestPairingDelegate delegate; + + adapter->AddPairingDelegate(&delegate, + BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW); + adapter->RemovePairingDelegate(&delegate); + + EXPECT_TRUE(adapter->DefaultPairingDelegate() == NULL); +} + +} // namespace device diff --git a/device/bluetooth/bluetooth_adapter_win.cc b/device/bluetooth/bluetooth_adapter_win.cc index e56ceae..bf0a9b7 100644 --- a/device/bluetooth/bluetooth_adapter_win.cc +++ b/device/bluetooth/bluetooth_adapter_win.cc @@ -150,6 +150,10 @@ void BluetoothAdapterWin::ReadLocalOutOfBandPairingData( NOTIMPLEMENTED(); } +void BluetoothAdapterWin::RemovePairingDelegateInternal( + BluetoothDevice::PairingDelegate* pairing_delegate) { +} + void BluetoothAdapterWin::AdapterStateChanged( const BluetoothTaskManagerWin::AdapterState& state) { DCHECK(thread_checker_.CalledOnValidThread()); diff --git a/device/bluetooth/bluetooth_adapter_win.h b/device/bluetooth/bluetooth_adapter_win.h index a017cd6..6e7382f 100644 --- a/device/bluetooth/bluetooth_adapter_win.h +++ b/device/bluetooth/bluetooth_adapter_win.h @@ -72,6 +72,11 @@ class BluetoothAdapterWin : public BluetoothAdapter, const ScopedVector<BluetoothTaskManagerWin::DeviceState>& devices) OVERRIDE; + protected: + // BluetoothAdapter override + virtual void RemovePairingDelegateInternal( + device::BluetoothDevice::PairingDelegate* pairing_delegate) OVERRIDE; + private: friend class BluetoothAdapterFactory; friend class BluetoothAdapterWinTest; diff --git a/device/bluetooth/bluetooth_chromeos_unittest.cc b/device/bluetooth/bluetooth_chromeos_unittest.cc index 5576f40..e39a746 100644 --- a/device/bluetooth/bluetooth_chromeos_unittest.cc +++ b/device/bluetooth/bluetooth_chromeos_unittest.cc @@ -15,7 +15,9 @@ #include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/bluetooth_device.h" #include "device/bluetooth/bluetooth_device_chromeos.h" +#include "device/bluetooth/bluetooth_pairing_chromeos.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/cros_system_api/dbus/service_constants.h" using device::BluetoothAdapter; using device::BluetoothAdapterFactory; @@ -230,6 +232,7 @@ class BluetoothChromeOSTest : public testing::Test { callback_count_ = 0; error_callback_count_ = 0; last_connect_error_ = BluetoothDevice::ERROR_UNKNOWN; + last_client_error_ = ""; } virtual void TearDown() { @@ -251,10 +254,11 @@ class BluetoothChromeOSTest : public testing::Test { void DBusErrorCallback(const std::string& error_name, const std::string& error_message) { ++error_callback_count_; + last_client_error_ = error_name; QuitMessageLoop(); } - void ConnectErrorCallback(enum BluetoothDevice::ConnectErrorCode error) { + void ConnectErrorCallback(BluetoothDevice::ConnectErrorCode error) { ++error_callback_count_; last_connect_error_ = error; } @@ -338,15 +342,16 @@ class BluetoothChromeOSTest : public testing::Test { int callback_count_; int error_callback_count_; enum BluetoothDevice::ConnectErrorCode last_connect_error_; + std::string last_client_error_; private: - // Some tests use a message loop since background processing is simulated; - // break out of those loops. - void QuitMessageLoop() { - if (base::MessageLoop::current() && - base::MessageLoop::current()->is_running()) - base::MessageLoop::current()->Quit(); - } + // Some tests use a message loop since background processing is simulated; + // break out of those loops. + void QuitMessageLoop() { + if (base::MessageLoop::current() && + base::MessageLoop::current()->is_running()) + base::MessageLoop::current()->Quit(); + } }; TEST_F(BluetoothChromeOSTest, AlreadyPresent) { @@ -2498,4 +2503,351 @@ TEST_F(BluetoothChromeOSTest, PairingCancelledInFlight) { EXPECT_FALSE(device->IsPaired()); } +TEST_F(BluetoothChromeOSTest, IncomingPairSonyHeadphones) { + base::MessageLoop message_loop; + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + + TestPairingDelegate pairing_delegate; + adapter_->AddPairingDelegate( + &pairing_delegate, + BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH); + + // The sony headphones requests that we provide a PIN code. + fake_bluetooth_device_client_->CreateDevice( + dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(FakeBluetoothDeviceClient::kSonyHeadphonesPath)); + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kSonyHeadphonesAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + fake_bluetooth_device_client_->SimulatePairing( + dbus::ObjectPath(FakeBluetoothDeviceClient::kSonyHeadphonesPath), + true, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::DBusErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.request_pincode_count_); + + // Set the PIN. + device->SetPinCode("1234"); + message_loop.Run(); + + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + // One for paired. + EXPECT_EQ(1, observer.device_changed_count_); + EXPECT_EQ(device, observer.last_device_); + + EXPECT_TRUE(device->IsPaired()); + + // No pairing context should remain on the device. + BluetoothDeviceChromeOS* device_chromeos = + static_cast<BluetoothDeviceChromeOS*>(device); + EXPECT_TRUE(device_chromeos->GetPairing() == NULL); +} + +TEST_F(BluetoothChromeOSTest, IncomingPairPhone) { + base::MessageLoop message_loop; + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + + TestPairingDelegate pairing_delegate; + adapter_->AddPairingDelegate( + &pairing_delegate, + BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH); + + // The fake phone requests that we confirm a displayed passkey. + fake_bluetooth_device_client_->CreateDevice( + dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(FakeBluetoothDeviceClient::kPhonePath)); + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kPhoneAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + fake_bluetooth_device_client_->SimulatePairing( + dbus::ObjectPath(FakeBluetoothDeviceClient::kPhonePath), + true, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::DBusErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_); + EXPECT_EQ(123456U, pairing_delegate.last_passkey_); + + // Confirm the passkey. + device->ConfirmPairing(); + message_loop.Run(); + + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + // One for paired. + EXPECT_EQ(1, observer.device_changed_count_); + EXPECT_EQ(device, observer.last_device_); + + EXPECT_TRUE(device->IsPaired()); + + // No pairing context should remain on the device. + BluetoothDeviceChromeOS* device_chromeos = + static_cast<BluetoothDeviceChromeOS*>(device); + EXPECT_TRUE(device_chromeos->GetPairing() == NULL); +} + +TEST_F(BluetoothChromeOSTest, IncomingPairWeirdDevice) { + base::MessageLoop message_loop; + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + + TestPairingDelegate pairing_delegate; + adapter_->AddPairingDelegate( + &pairing_delegate, + BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH); + + // The weird device requests that we provide a Passkey. + fake_bluetooth_device_client_->CreateDevice( + dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(FakeBluetoothDeviceClient::kWeirdDevicePath)); + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kWeirdDeviceAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + fake_bluetooth_device_client_->SimulatePairing( + dbus::ObjectPath(FakeBluetoothDeviceClient::kWeirdDevicePath), + true, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::DBusErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.request_passkey_count_); + + // Set the Passkey. + device->SetPasskey(1234); + message_loop.Run(); + + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + // One for paired. + EXPECT_EQ(1, observer.device_changed_count_); + EXPECT_EQ(device, observer.last_device_); + + EXPECT_TRUE(device->IsPaired()); + + // No pairing context should remain on the device. + BluetoothDeviceChromeOS* device_chromeos = + static_cast<BluetoothDeviceChromeOS*>(device); + EXPECT_TRUE(device_chromeos->GetPairing() == NULL); +} + +TEST_F(BluetoothChromeOSTest, IncomingPairSonyHeadphonesWithoutDelegate) { + base::MessageLoop message_loop; + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + + // The Sony Headphones requests that we provide a PIN Code, without a + // pairing delegate, that will be rejected. + fake_bluetooth_device_client_->CreateDevice( + dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(FakeBluetoothDeviceClient::kSonyHeadphonesPath)); + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kSonyHeadphonesAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + fake_bluetooth_device_client_->SimulatePairing( + dbus::ObjectPath(FakeBluetoothDeviceClient::kSonyHeadphonesPath), + true, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::DBusErrorCallback, + base::Unretained(this))); + + message_loop.Run(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_); + + // No changes should be observer. + EXPECT_EQ(0, observer.device_changed_count_); + + EXPECT_FALSE(device->IsPaired()); + + // No pairing context should remain on the device. + BluetoothDeviceChromeOS* device_chromeos = + static_cast<BluetoothDeviceChromeOS*>(device); + EXPECT_TRUE(device_chromeos->GetPairing() == NULL); +} + +TEST_F(BluetoothChromeOSTest, IncomingPairPhoneWithoutDelegate) { + base::MessageLoop message_loop; + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + + // The fake phone requests that we confirm a displayed passkey, without a + // pairing delegate, that will be rejected. + fake_bluetooth_device_client_->CreateDevice( + dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(FakeBluetoothDeviceClient::kPhonePath)); + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kPhoneAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + fake_bluetooth_device_client_->SimulatePairing( + dbus::ObjectPath(FakeBluetoothDeviceClient::kPhonePath), + true, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::DBusErrorCallback, + base::Unretained(this))); + + message_loop.Run(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_); + + // No changes should be observer. + EXPECT_EQ(0, observer.device_changed_count_); + + EXPECT_FALSE(device->IsPaired()); + + // No pairing context should remain on the device. + BluetoothDeviceChromeOS* device_chromeos = + static_cast<BluetoothDeviceChromeOS*>(device); + EXPECT_TRUE(device_chromeos->GetPairing() == NULL); +} + +TEST_F(BluetoothChromeOSTest, IncomingPairWeirdDeviceWithoutDelegate) { + base::MessageLoop message_loop; + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + + // The weird device requests that we provide a displayed passkey, without a + // pairing delegate, that will be rejected. + fake_bluetooth_device_client_->CreateDevice( + dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(FakeBluetoothDeviceClient::kWeirdDevicePath)); + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kWeirdDeviceAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + fake_bluetooth_device_client_->SimulatePairing( + dbus::ObjectPath(FakeBluetoothDeviceClient::kWeirdDevicePath), + true, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::DBusErrorCallback, + base::Unretained(this))); + + message_loop.Run(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_); + + // No changes should be observer. + EXPECT_EQ(0, observer.device_changed_count_); + + EXPECT_FALSE(device->IsPaired()); + + // No pairing context should remain on the device. + BluetoothDeviceChromeOS* device_chromeos = + static_cast<BluetoothDeviceChromeOS*>(device); + EXPECT_TRUE(device_chromeos->GetPairing() == NULL); +} + +TEST_F(BluetoothChromeOSTest, RemovePairingDelegateDuringPairing) { + base::MessageLoop message_loop; + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + + TestPairingDelegate pairing_delegate; + adapter_->AddPairingDelegate( + &pairing_delegate, + BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH); + + // The weird device requests that we provide a Passkey. + fake_bluetooth_device_client_->CreateDevice( + dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(FakeBluetoothDeviceClient::kWeirdDevicePath)); + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kWeirdDeviceAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + fake_bluetooth_device_client_->SimulatePairing( + dbus::ObjectPath(FakeBluetoothDeviceClient::kWeirdDevicePath), + true, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::DBusErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.request_passkey_count_); + + // A pairing context should now be set on the device. + BluetoothDeviceChromeOS* device_chromeos = + static_cast<BluetoothDeviceChromeOS*>(device); + ASSERT_TRUE(device_chromeos->GetPairing() != NULL); + + // Removing the pairing delegate should remove that pairing context. + adapter_->RemovePairingDelegate(&pairing_delegate); + + EXPECT_TRUE(device_chromeos->GetPairing() == NULL); + + // Set the Passkey, this should now have no effect since the pairing has + // been, in-effect, cancelled + device->SetPasskey(1234); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(0, error_callback_count_); + EXPECT_EQ(0, observer.device_changed_count_); + + EXPECT_FALSE(device->IsPaired()); +} + } // namespace chromeos diff --git a/device/bluetooth/bluetooth_device_chromeos.cc b/device/bluetooth/bluetooth_device_chromeos.cc index 2d43661..b76c512 100644 --- a/device/bluetooth/bluetooth_device_chromeos.cc +++ b/device/bluetooth/bluetooth_device_chromeos.cc @@ -14,6 +14,7 @@ #include "chromeos/dbus/dbus_thread_manager.h" #include "dbus/bus.h" #include "device/bluetooth/bluetooth_adapter_chromeos.h" +#include "device/bluetooth/bluetooth_pairing_chromeos.h" #include "device/bluetooth/bluetooth_profile_chromeos.h" #include "device/bluetooth/bluetooth_socket.h" #include "third_party/cros_system_api/dbus/service_constants.h" @@ -226,15 +227,15 @@ void BluetoothDeviceChromeOS::ProvidesServiceWithName( } bool BluetoothDeviceChromeOS::ExpectingPinCode() const { - return pairing_context_.get() && pairing_context_->ExpectingPinCode(); + return pairing_.get() && pairing_->ExpectingPinCode(); } bool BluetoothDeviceChromeOS::ExpectingPasskey() const { - return pairing_context_.get() && pairing_context_->ExpectingPasskey(); + return pairing_.get() && pairing_->ExpectingPasskey(); } bool BluetoothDeviceChromeOS::ExpectingConfirmation() const { - return pairing_context_.get() && pairing_context_->ExpectingConfirmation(); + return pairing_.get() && pairing_->ExpectingConfirmation(); } void BluetoothDeviceChromeOS::Connect( @@ -252,9 +253,7 @@ void BluetoothDeviceChromeOS::Connect( ConnectInternal(false, callback, error_callback); } else { // Initiate high-security connection with pairing. - DCHECK(!pairing_context_); - pairing_context_.reset( - new BluetoothAdapterChromeOS::PairingContext(pairing_delegate)); + BeginPairing(pairing_delegate); DBusThreadManager::Get()->GetBluetoothDeviceClient()-> Pair(object_path_, @@ -268,31 +267,31 @@ void BluetoothDeviceChromeOS::Connect( } void BluetoothDeviceChromeOS::SetPinCode(const std::string& pincode) { - if (!pairing_context_.get()) + if (!pairing_.get()) return; - pairing_context_->SetPinCode(pincode); + pairing_->SetPinCode(pincode); } void BluetoothDeviceChromeOS::SetPasskey(uint32 passkey) { - if (!pairing_context_.get()) + if (!pairing_.get()) return; - pairing_context_->SetPasskey(passkey); + pairing_->SetPasskey(passkey); } void BluetoothDeviceChromeOS::ConfirmPairing() { - if (!pairing_context_.get()) + if (!pairing_.get()) return; - pairing_context_->ConfirmPairing(); + pairing_->ConfirmPairing(); } void BluetoothDeviceChromeOS::RejectPairing() { - if (!pairing_context_.get()) + if (!pairing_.get()) return; - pairing_context_->RejectPairing(); + pairing_->RejectPairing(); } void BluetoothDeviceChromeOS::CancelPairing() { @@ -300,7 +299,7 @@ void BluetoothDeviceChromeOS::CancelPairing() { // If there is a callback in progress that we can reply to then use that // to cancel the current pairing request. - if (pairing_context_.get() && pairing_context_->CancelPairing()) + if (pairing_.get() && pairing_->CancelPairing()) canceled = true; // If not we have to send an explicit CancelPairing() to the device instead. @@ -313,14 +312,13 @@ void BluetoothDeviceChromeOS::CancelPairing() { base::Bind(&base::DoNothing), base::Bind(&BluetoothDeviceChromeOS::OnCancelPairingError, weak_ptr_factory_.GetWeakPtr())); - canceled = true; } // Since there is no callback to this method it's possible that the pairing // delegate is going to be freed before things complete (indeed it's // documented that this is the method you should call while freeing the // pairing delegate), so clear our the context holding on to it. - pairing_context_.reset(); + EndPairing(); } void BluetoothDeviceChromeOS::Disconnect(const base::Closure& callback, @@ -395,6 +393,21 @@ void BluetoothDeviceChromeOS::ClearOutOfBandPairingData( error_callback.Run(); } +BluetoothPairingChromeOS* BluetoothDeviceChromeOS::BeginPairing( + BluetoothDevice::PairingDelegate* pairing_delegate) { + DCHECK(!pairing_); + pairing_.reset(new BluetoothPairingChromeOS(this, pairing_delegate)); + return pairing_.get(); +} + +void BluetoothDeviceChromeOS::EndPairing() { + pairing_.reset(); +} + +BluetoothPairingChromeOS* BluetoothDeviceChromeOS::GetPairing() const { + return pairing_.get(); +} + void BluetoothDeviceChromeOS::ConnectInternal( bool after_pairing, const base::Closure& callback, @@ -466,7 +479,7 @@ void BluetoothDeviceChromeOS::OnPair( const ConnectErrorCallback& error_callback) { VLOG(1) << object_path_.value() << ": Paired"; - pairing_context_.reset(); + EndPairing(); SetTrusted(); ConnectInternal(true, callback, error_callback); @@ -485,7 +498,7 @@ void BluetoothDeviceChromeOS::OnPairError( VLOG(1) << object_path_.value() << ": " << num_connecting_calls_ << " still in progress"; - pairing_context_.reset(); + EndPairing(); // Determine the error code from error_name. ConnectErrorCode error_code = ERROR_UNKNOWN; diff --git a/device/bluetooth/bluetooth_device_chromeos.h b/device/bluetooth/bluetooth_device_chromeos.h index e2f2997..797f66e 100644 --- a/device/bluetooth/bluetooth_device_chromeos.h +++ b/device/bluetooth/bluetooth_device_chromeos.h @@ -11,11 +11,13 @@ #include "base/memory/weak_ptr.h" #include "chromeos/dbus/bluetooth_device_client.h" #include "dbus/object_path.h" -#include "device/bluetooth/bluetooth_adapter_chromeos.h" #include "device/bluetooth/bluetooth_device.h" namespace chromeos { +class BluetoothAdapterChromeOS; +class BluetoothPairingChromeOS; + // The BluetoothDeviceChromeOS class implements BluetoothDevice for the // Chrome OS platform. class BluetoothDeviceChromeOS @@ -69,6 +71,19 @@ class BluetoothDeviceChromeOS const base::Closure& callback, const ErrorCallback& error_callback) OVERRIDE; + // Creates a pairing object with the given delegate |pairing_delegate| and + // establishes it as the pairing context for this device. All pairing-related + // method calls will be forwarded to this object until it is released. + BluetoothPairingChromeOS* BeginPairing( + BluetoothDevice::PairingDelegate* pairing_delegate); + + // Releases the current pairing object, any pairing-related method calls will + // be ignored. + void EndPairing(); + + // Returns the current pairing object or NULL if no pairing is in progress. + BluetoothPairingChromeOS* GetPairing() const; + protected: // BluetoothDevice override virtual std::string GetDeviceName() const OVERRIDE; @@ -135,7 +150,7 @@ class BluetoothDeviceChromeOS const std::string& error_name, const std::string& error_message); - // Return the object path of the device; used by BluetoothAdapterChromeOS + // Returns the object path of the device; used by BluetoothAdapterChromeOS const dbus::ObjectPath& object_path() const { return object_path_; } // The adapter that owns this device instance. @@ -150,9 +165,7 @@ class BluetoothDeviceChromeOS // During pairing this is set to an object that we don't own, but on which // we can make method calls to request, display or confirm PIN Codes and // Passkeys. Generally it is the object that owns this one. - PairingDelegate* pairing_delegate_; - - scoped_ptr<BluetoothAdapterChromeOS::PairingContext> pairing_context_; + scoped_ptr<BluetoothPairingChromeOS> pairing_; // Note: This should remain the last member so it'll be destroyed and // invalidate its weak pointers before any other members are destroyed. diff --git a/device/bluetooth/bluetooth_pairing_chromeos.cc b/device/bluetooth/bluetooth_pairing_chromeos.cc new file mode 100644 index 0000000..d3b826d --- /dev/null +++ b/device/bluetooth/bluetooth_pairing_chromeos.cc @@ -0,0 +1,248 @@ +// Copyright 2014 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 "device/bluetooth/bluetooth_pairing_chromeos.h" + +#include "base/logging.h" +#include "base/metrics/histogram.h" +#include "device/bluetooth/bluetooth_device.h" +#include "device/bluetooth/bluetooth_device_chromeos.h" + +using device::BluetoothDevice; + +namespace { + +// Histogram enumerations for pairing methods. +enum UMAPairingMethod { + UMA_PAIRING_METHOD_NONE, + UMA_PAIRING_METHOD_REQUEST_PINCODE, + UMA_PAIRING_METHOD_REQUEST_PASSKEY, + UMA_PAIRING_METHOD_DISPLAY_PINCODE, + UMA_PAIRING_METHOD_DISPLAY_PASSKEY, + UMA_PAIRING_METHOD_CONFIRM_PASSKEY, + // NOTE: Add new pairing methods immediately above this line. Make sure to + // update the enum list in tools/histogram/histograms.xml accordingly. + UMA_PAIRING_METHOD_COUNT +}; + +} // namespace + +namespace chromeos { + +BluetoothPairingChromeOS::BluetoothPairingChromeOS( + BluetoothDeviceChromeOS* device, + BluetoothDevice::PairingDelegate* pairing_delegate) + : device_(device), + pairing_delegate_(pairing_delegate), + pairing_delegate_used_(false) { + VLOG(1) << "Created BluetoothPairingChromeOS for " + << device_->GetAddress(); +} + +BluetoothPairingChromeOS::~BluetoothPairingChromeOS() { + VLOG(1) << "Destroying BluetoothPairingChromeOS for " + << device_->GetAddress(); + + if (!pairing_delegate_used_) { + UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", + UMA_PAIRING_METHOD_NONE, + UMA_PAIRING_METHOD_COUNT); + } + + if (!pincode_callback_.is_null()) { + pincode_callback_.Run( + BluetoothAgentServiceProvider::Delegate::CANCELLED, ""); + } + + if (!passkey_callback_.is_null()) { + passkey_callback_.Run( + BluetoothAgentServiceProvider::Delegate::CANCELLED, 0); + } + + if (!confirmation_callback_.is_null()) { + confirmation_callback_.Run( + BluetoothAgentServiceProvider::Delegate::CANCELLED); + } + + pairing_delegate_ = NULL; +} + +void BluetoothPairingChromeOS::RequestPinCode( + const BluetoothAgentServiceProvider::Delegate::PinCodeCallback& callback) { + UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", + UMA_PAIRING_METHOD_REQUEST_PINCODE, + UMA_PAIRING_METHOD_COUNT); + + DCHECK(pincode_callback_.is_null()); + pincode_callback_ = callback; + pairing_delegate_->RequestPinCode(device_); + pairing_delegate_used_ = true; +} + +bool BluetoothPairingChromeOS::ExpectingPinCode() const { + return !pincode_callback_.is_null(); +} + +void BluetoothPairingChromeOS::SetPinCode(const std::string& pincode) { + if (pincode_callback_.is_null()) + return; + + pincode_callback_.Run(BluetoothAgentServiceProvider::Delegate::SUCCESS, + pincode); + pincode_callback_.Reset(); + + // If this is not an outgoing connection to the device, clean up the pairing + // context since the pairing is done. The outgoing connection case is cleaned + // up in the callback for the underlying Pair() call. + if (!device_->IsConnecting()) + device_->EndPairing(); +} + +void BluetoothPairingChromeOS::DisplayPinCode(const std::string& pincode) { + UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", + UMA_PAIRING_METHOD_DISPLAY_PINCODE, + UMA_PAIRING_METHOD_COUNT); + + pairing_delegate_->DisplayPinCode(device_, pincode); + pairing_delegate_used_ = true; + + // If this is not an outgoing connection to the device, the pairing context + // needs to be cleaned up again as there's no reliable indication of + // completion of incoming pairing. + if (!device_->IsConnecting()) + device_->EndPairing(); +} + +void BluetoothPairingChromeOS::RequestPasskey( + const BluetoothAgentServiceProvider::Delegate::PasskeyCallback& callback) { + UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", + UMA_PAIRING_METHOD_REQUEST_PASSKEY, + UMA_PAIRING_METHOD_COUNT); + + DCHECK(passkey_callback_.is_null()); + passkey_callback_ = callback; + pairing_delegate_->RequestPasskey(device_); + pairing_delegate_used_ = true; +} + +bool BluetoothPairingChromeOS::ExpectingPasskey() const { + return !passkey_callback_.is_null(); +} + +void BluetoothPairingChromeOS::SetPasskey(uint32 passkey) { + if (passkey_callback_.is_null()) + return; + + passkey_callback_.Run(BluetoothAgentServiceProvider::Delegate::SUCCESS, + passkey); + passkey_callback_.Reset(); + + // If this is not an outgoing connection to the device, clean up the pairing + // context since the pairing is done. The outgoing connection case is cleaned + // up in the callback for the underlying Pair() call. + if (!device_->IsConnecting()) + device_->EndPairing(); +} + +void BluetoothPairingChromeOS::DisplayPasskey(uint32 passkey) { + UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", + UMA_PAIRING_METHOD_DISPLAY_PASSKEY, + UMA_PAIRING_METHOD_COUNT); + + + pairing_delegate_->DisplayPasskey(device_, passkey); + pairing_delegate_used_ = true; + + // If this is not an outgoing connection to the device, the pairing context + // needs to be cleaned up again as there's no reliable indication of + // completion of incoming pairing. + if (!device_->IsConnecting()) + device_->EndPairing(); +} + +void BluetoothPairingChromeOS::KeysEntered(uint16 entered) { + pairing_delegate_->KeysEntered(device_, entered); + pairing_delegate_used_ = true; +} + +void BluetoothPairingChromeOS::RequestConfirmation( + uint32 passkey, + const BluetoothAgentServiceProvider::Delegate::ConfirmationCallback& + callback) { + UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", + UMA_PAIRING_METHOD_CONFIRM_PASSKEY, + UMA_PAIRING_METHOD_COUNT); + + DCHECK(confirmation_callback_.is_null()); + confirmation_callback_ = callback; + pairing_delegate_->ConfirmPasskey(device_, passkey); + pairing_delegate_used_ = true; +} + +bool BluetoothPairingChromeOS::ExpectingConfirmation() const { + return !confirmation_callback_.is_null(); +} + +void BluetoothPairingChromeOS::ConfirmPairing() { + if (confirmation_callback_.is_null()) + return; + + confirmation_callback_.Run(BluetoothAgentServiceProvider::Delegate::SUCCESS); + confirmation_callback_.Reset(); + + // If this is not an outgoing connection to the device, clean up the pairing + // context since the pairing is done. The outgoing connection case is cleaned + // up in the callback for the underlying Pair() call. + if (!device_->IsConnecting()) + device_->EndPairing(); +} + +bool BluetoothPairingChromeOS::RejectPairing() { + return RunPairingCallbacks( + BluetoothAgentServiceProvider::Delegate::REJECTED); +} + +bool BluetoothPairingChromeOS::CancelPairing() { + return RunPairingCallbacks( + BluetoothAgentServiceProvider::Delegate::CANCELLED); +} + +BluetoothDevice::PairingDelegate* +BluetoothPairingChromeOS::GetPairingDelegate() const { + return pairing_delegate_; +} + +bool BluetoothPairingChromeOS::RunPairingCallbacks( + BluetoothAgentServiceProvider::Delegate::Status status) { + pairing_delegate_used_ = true; + + bool callback_run = false; + if (!pincode_callback_.is_null()) { + pincode_callback_.Run(status, ""); + pincode_callback_.Reset(); + callback_run = true; + } + + if (!passkey_callback_.is_null()) { + passkey_callback_.Run(status, 0); + passkey_callback_.Reset(); + callback_run = true; + } + + if (!confirmation_callback_.is_null()) { + confirmation_callback_.Run(status); + confirmation_callback_.Reset(); + callback_run = true; + } + + // If this is not an outgoing connection to the device, clean up the pairing + // context since the pairing is done. The outgoing connection case is cleaned + // up in the callback for the underlying Pair() call. + if (!device_->IsConnecting()) + device_->EndPairing(); + + return callback_run; +} + +} // namespace chromeos diff --git a/device/bluetooth/bluetooth_pairing_chromeos.h b/device/bluetooth/bluetooth_pairing_chromeos.h new file mode 100644 index 0000000..6772497 --- /dev/null +++ b/device/bluetooth/bluetooth_pairing_chromeos.h @@ -0,0 +1,134 @@ +// Copyright 2014 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. + +#ifndef DEVICE_BLUETOOTH_BLUETOOTH_PAIRING_CHROMEOS_H_ +#define DEVICE_BLUETOOTH_BLUETOOTH_PAIRING_CHROMEOS_H_ + +#include "chromeos/dbus/bluetooth_agent_service_provider.h" +#include "device/bluetooth/bluetooth_device.h" + +namespace chromeos { + +class BluetoothDeviceChromeOS; + +// The BluetoothPairingChromeOS class encapsulates the logic for an individual +// device pairing, acting as a bridge between BluetoothAdapterChromeOS which +// communicates with the underlying Controller and Host Subsystem, and +// BluetoothDeviceChromeOS which presents the pairing logic to the application. +class BluetoothPairingChromeOS { + public: + BluetoothPairingChromeOS( + BluetoothDeviceChromeOS* device, + device::BluetoothDevice::PairingDelegate* pairing_delegate); + ~BluetoothPairingChromeOS(); + + // Indicates whether the device is currently pairing and expecting a + // Passkey to be returned. + bool ExpectingPasskey() const; + + // Indicates whether the device is currently pairing and expecting + // confirmation of a displayed passkey. + bool ExpectingConfirmation() const; + + // Requests a PIN code for the current device from the current pairing + // delegate, the SetPinCode(), RejectPairing() and CancelPairing() method + // calls on this object are translated into the appropriate response to + // |callback|. + void RequestPinCode( + const BluetoothAgentServiceProvider::Delegate::PinCodeCallback& callback); + + // Indicates whether the device is currently pairing and expecting a + // PIN Code to be returned. + bool ExpectingPinCode() const; + + // Sends the PIN code |pincode| to the remote device during pairing. + // + // PIN Codes are generally required for Bluetooth 2.0 and earlier devices + // for which there is no automatic pairing or special handling. + void SetPinCode(const std::string& pincode); + + // Requests a PIN code for the current device be displayed by the current + // pairing delegate. No response is expected from the delegate. + void DisplayPinCode(const std::string& pincode); + + // Requests a Passkey for the current device from the current pairing + // delegate, the SetPasskey(), RejectPairing() and CancelPairing() method + // calls on this object are translated into the appropriate response to + // |callback|. + void RequestPasskey( + const BluetoothAgentServiceProvider::Delegate::PasskeyCallback& callback); + + // Sends the Passkey |passkey| to the remote device during pairing. + // + // Passkeys are generally required for Bluetooth 2.1 and later devices + // which cannot provide input or display on their own, and don't accept + // passkey-less pairing, and are a numeric in the range 0-999999. + void SetPasskey(uint32 passkey); + + // Requests a Passkey for the current device be displayed by the current + // pairing delegate. No response is expected from the delegate. + void DisplayPasskey(uint32 passkey); + + // Informs the current pairing delegate that |entered| keys have been + // provided to the remote device since the DisplayPasskey() call. No + // response is expected from the delegate. + void KeysEntered(uint16 entered); + + // Requests confirmation that |passkey| is displayed on the current device + // from the current pairing delegate. The ConfirmPairing(), RejectPairing() + // and CancelPairing() method calls on this object are translated into the + // appropriate response to |callback|. + void RequestConfirmation( + uint32 passkey, + const BluetoothAgentServiceProvider::Delegate::ConfirmationCallback& + callback); + + // Confirms to the remote device during pairing that a passkey provided by + // the ConfirmPasskey() delegate call is displayed on both devices. + void ConfirmPairing(); + + // Rejects a pairing or connection request from a remote device, returns + // false if there was no way to reject the pairing. + bool RejectPairing(); + + // Cancels a pairing or connection attempt to a remote device, returns + // false if there was no way to cancel the pairing. + bool CancelPairing(); + + // Returns the pairing delegate being used by this pairing object. + device::BluetoothDevice::PairingDelegate* GetPairingDelegate() const; + + private: + // Internal method to respond to the relevant callback for a RejectPairing + // or CancelPairing call. + bool RunPairingCallbacks( + BluetoothAgentServiceProvider::Delegate::Status status); + + // The underlying BluetoothDeviceChromeOS that owns this pairing context. + BluetoothDeviceChromeOS* device_; + + // UI Pairing Delegate to make method calls on, this must live as long as + // the object capturing the PairingContext. + device::BluetoothDevice::PairingDelegate* pairing_delegate_; + + // Flag to indicate whether any pairing delegate method has been called + // during pairing. Used to determine whether we need to log the + // "no pairing interaction" metric. + bool pairing_delegate_used_; + + // During pairing these callbacks are set to those provided by method calls + // made on the BluetoothAdapterChromeOS instance by its respective + // BluetoothAgentServiceProvider instance, and are called by our own + // method calls such as SetPinCode() and SetPasskey(). + BluetoothAgentServiceProvider::Delegate::PinCodeCallback pincode_callback_; + BluetoothAgentServiceProvider::Delegate::PasskeyCallback passkey_callback_; + BluetoothAgentServiceProvider::Delegate::ConfirmationCallback + confirmation_callback_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothPairingChromeOS); +}; + +} // namespace chromeos + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_PAIRING_CHROMEOS_H_ diff --git a/device/bluetooth/test/mock_bluetooth_adapter.h b/device/bluetooth/test/mock_bluetooth_adapter.h index 1e1cb35..56647fe 100644 --- a/device/bluetooth/test/mock_bluetooth_adapter.h +++ b/device/bluetooth/test/mock_bluetooth_adapter.h @@ -66,6 +66,13 @@ class MockBluetoothAdapter : public BluetoothAdapter { ReadLocalOutOfBandPairingData, void(const BluetoothOutOfBandPairingDataCallback& callback, const ErrorCallback& error_callback)); + MOCK_METHOD2(AddPairingDelegate, + void(BluetoothDevice::PairingDelegate* pairing_delegate, + enum PairingDelegatePriority priority)); + MOCK_METHOD1(RemovePairingDelegate, + void(BluetoothDevice::PairingDelegate* pairing_delegate)); + MOCK_METHOD0(DefaultPairingDelegate, BluetoothDevice::PairingDelegate*()); + protected: MOCK_METHOD2(AddDiscoverySession, void(const base::Closure& callback, @@ -74,6 +81,9 @@ class MockBluetoothAdapter : public BluetoothAdapter { void(const base::Closure& callback, const ErrorCallback& error_callback)); virtual ~MockBluetoothAdapter(); + + MOCK_METHOD1(RemovePairingDelegateInternal, + void(BluetoothDevice::PairingDelegate* pairing_delegate)); }; } // namespace device diff --git a/device/device_tests.gyp b/device/device_tests.gyp index 268b270..63c0a82 100644 --- a/device/device_tests.gyp +++ b/device/device_tests.gyp @@ -23,6 +23,7 @@ ], 'sources': [ 'bluetooth/bluetooth_adapter_mac_unittest.mm', + 'bluetooth/bluetooth_adapter_unittest.cc', 'bluetooth/bluetooth_adapter_win_unittest.cc', 'bluetooth/bluetooth_device_win_unittest.cc', 'bluetooth/bluetooth_chromeos_unittest.cc', |