From 627f4f4ec29191d02998358a0b9d8896547ffca1 Mon Sep 17 00:00:00 2001 From: "keybuk@chromium.org" Date: Wed, 12 Jun 2013 14:47:32 +0000 Subject: Bluetooth: drop "Experimental" from Chrome OS backend BUG=221813 TEST=device_unittests Review URL: https://chromiumcodereview.appspot.com/14932007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@205777 0039d316-1c4b-4281-b951-d872f2087c98 --- device/bluetooth/bluetooth.gyp | 16 +- device/bluetooth/bluetooth_adapter_chromeos.cc | 451 +++++ device/bluetooth/bluetooth_adapter_chromeos.h | 148 ++ .../bluetooth_adapter_experimental_chromeos.cc | 468 ----- .../bluetooth_adapter_experimental_chromeos.h | 150 -- device/bluetooth/bluetooth_adapter_factory.cc | 6 +- device/bluetooth/bluetooth_chromeos_unittest.cc | 2027 ++++++++++++++++++++ device/bluetooth/bluetooth_device_chromeos.cc | 832 ++++++++ device/bluetooth/bluetooth_device_chromeos.h | 223 +++ .../bluetooth_device_experimental_chromeos.cc | 849 -------- .../bluetooth_device_experimental_chromeos.h | 227 --- .../bluetooth_experimental_chromeos_unittest.cc | 2027 -------------------- device/bluetooth/bluetooth_profile.cc | 6 +- device/bluetooth/bluetooth_profile_chromeos.cc | 253 +++ device/bluetooth/bluetooth_profile_chromeos.h | 127 ++ .../bluetooth_profile_chromeos_unittest.cc | 8 +- .../bluetooth_profile_experimental_chromeos.cc | 259 --- .../bluetooth_profile_experimental_chromeos.h | 132 -- device/bluetooth/bluetooth_socket_chromeos.cc | 167 ++ device/bluetooth/bluetooth_socket_chromeos.h | 73 + .../bluetooth_socket_experimental_chromeos.cc | 170 -- .../bluetooth_socket_experimental_chromeos.h | 75 - device/device.gyp | 2 +- 23 files changed, 4320 insertions(+), 4376 deletions(-) create mode 100644 device/bluetooth/bluetooth_adapter_chromeos.cc create mode 100644 device/bluetooth/bluetooth_adapter_chromeos.h delete mode 100644 device/bluetooth/bluetooth_adapter_experimental_chromeos.cc delete mode 100644 device/bluetooth/bluetooth_adapter_experimental_chromeos.h create mode 100644 device/bluetooth/bluetooth_chromeos_unittest.cc create mode 100644 device/bluetooth/bluetooth_device_chromeos.cc create mode 100644 device/bluetooth/bluetooth_device_chromeos.h delete mode 100644 device/bluetooth/bluetooth_device_experimental_chromeos.cc delete mode 100644 device/bluetooth/bluetooth_device_experimental_chromeos.h delete mode 100644 device/bluetooth/bluetooth_experimental_chromeos_unittest.cc create mode 100644 device/bluetooth/bluetooth_profile_chromeos.cc create mode 100644 device/bluetooth/bluetooth_profile_chromeos.h delete mode 100644 device/bluetooth/bluetooth_profile_experimental_chromeos.cc delete mode 100644 device/bluetooth/bluetooth_profile_experimental_chromeos.h create mode 100644 device/bluetooth/bluetooth_socket_chromeos.cc create mode 100644 device/bluetooth/bluetooth_socket_chromeos.h delete mode 100644 device/bluetooth/bluetooth_socket_experimental_chromeos.cc delete mode 100644 device/bluetooth/bluetooth_socket_experimental_chromeos.h (limited to 'device') diff --git a/device/bluetooth/bluetooth.gyp b/device/bluetooth/bluetooth.gyp index cd4788b..9da5cda 100644 --- a/device/bluetooth/bluetooth.gyp +++ b/device/bluetooth/bluetooth.gyp @@ -21,8 +21,8 @@ 'sources': [ 'bluetooth_adapter.cc', 'bluetooth_adapter.h', - 'bluetooth_adapter_experimental_chromeos.cc', - 'bluetooth_adapter_experimental_chromeos.h', + 'bluetooth_adapter_chromeos.cc', + 'bluetooth_adapter_chromeos.h', 'bluetooth_adapter_factory.cc', 'bluetooth_adapter_factory.h', 'bluetooth_adapter_mac.h', @@ -31,8 +31,8 @@ 'bluetooth_adapter_win.h', 'bluetooth_device.cc', 'bluetooth_device.h', - 'bluetooth_device_experimental_chromeos.cc', - 'bluetooth_device_experimental_chromeos.h', + 'bluetooth_device_chromeos.cc', + 'bluetooth_device_chromeos.h', 'bluetooth_device_mac.h', 'bluetooth_device_mac.mm', 'bluetooth_device_win.cc', @@ -42,8 +42,8 @@ 'bluetooth_out_of_band_pairing_data.h', 'bluetooth_profile.cc', 'bluetooth_profile.h', - 'bluetooth_profile_experimental_chromeos.cc', - 'bluetooth_profile_experimental_chromeos.h', + 'bluetooth_profile_chromeos.cc', + 'bluetooth_profile_chromeos.h', 'bluetooth_profile_mac.h', 'bluetooth_profile_mac.mm', 'bluetooth_profile_win.cc', @@ -55,8 +55,8 @@ 'bluetooth_service_record_win.cc', 'bluetooth_service_record_win.h', 'bluetooth_socket.h', - 'bluetooth_socket_experimental_chromeos.cc', - 'bluetooth_socket_experimental_chromeos.h', + 'bluetooth_socket_chromeos.cc', + 'bluetooth_socket_chromeos.h', 'bluetooth_socket_mac.h', 'bluetooth_socket_mac.mm', 'bluetooth_socket_win.cc', diff --git a/device/bluetooth/bluetooth_adapter_chromeos.cc b/device/bluetooth/bluetooth_adapter_chromeos.cc new file mode 100644 index 0000000..b5f6ba0 --- /dev/null +++ b/device/bluetooth/bluetooth_adapter_chromeos.cc @@ -0,0 +1,451 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/bluetooth/bluetooth_adapter_chromeos.h" + +#include + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/metrics/histogram.h" +#include "chromeos/chromeos_switches.h" +#include "chromeos/dbus/bluetooth_adapter_client.h" +#include "chromeos/dbus/bluetooth_device_client.h" +#include "chromeos/dbus/bluetooth_input_client.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "device/bluetooth/bluetooth_device.h" +#include "device/bluetooth/bluetooth_device_chromeos.h" + +using device::BluetoothAdapter; +using device::BluetoothDevice; + +namespace chromeos { + +BluetoothAdapterChromeOS::BluetoothAdapterChromeOS() + : weak_ptr_factory_(this) { + DBusThreadManager::Get()->GetBluetoothAdapterClient()->AddObserver(this); + DBusThreadManager::Get()->GetBluetoothDeviceClient()->AddObserver(this); + DBusThreadManager::Get()->GetBluetoothInputClient()->AddObserver(this); + + std::vector object_paths = + DBusThreadManager::Get()->GetBluetoothAdapterClient()->GetAdapters(); + + if (!object_paths.empty()) { + VLOG(1) << object_paths.size() << " Bluetooth adapter(s) available."; + SetAdapter(object_paths[0]); + } +} + +BluetoothAdapterChromeOS::~BluetoothAdapterChromeOS() { + DBusThreadManager::Get()->GetBluetoothAdapterClient()->RemoveObserver(this); + DBusThreadManager::Get()->GetBluetoothDeviceClient()->RemoveObserver(this); + DBusThreadManager::Get()->GetBluetoothInputClient()->RemoveObserver(this); +} + +void BluetoothAdapterChromeOS::AddObserver( + BluetoothAdapter::Observer* observer) { + DCHECK(observer); + observers_.AddObserver(observer); +} + +void BluetoothAdapterChromeOS::RemoveObserver( + BluetoothAdapter::Observer* observer) { + DCHECK(observer); + observers_.RemoveObserver(observer); +} + +std::string BluetoothAdapterChromeOS::GetAddress() const { + if (!IsPresent()) + return std::string(); + + BluetoothAdapterClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + GetProperties(object_path_); + DCHECK(properties); + + return properties->address.value(); +} + +std::string BluetoothAdapterChromeOS::GetName() const { + if (!IsPresent()) + return std::string(); + + BluetoothAdapterClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + GetProperties(object_path_); + DCHECK(properties); + + return properties->alias.value(); +} + +bool BluetoothAdapterChromeOS::IsInitialized() const { + return true; +} + +bool BluetoothAdapterChromeOS::IsPresent() const { + return !object_path_.value().empty(); +} + +bool BluetoothAdapterChromeOS::IsPowered() const { + if (!IsPresent()) + return false; + + BluetoothAdapterClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + GetProperties(object_path_); + + return properties->powered.value(); +} + +void BluetoothAdapterChromeOS::SetPowered( + bool powered, + const base::Closure& callback, + const ErrorCallback& error_callback) { + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + GetProperties(object_path_)->powered.Set( + powered, + base::Bind(&BluetoothAdapterChromeOS::OnSetPowered, + weak_ptr_factory_.GetWeakPtr(), + callback, + error_callback)); +} + +bool BluetoothAdapterChromeOS::IsDiscovering() const { + if (!IsPresent()) + return false; + + BluetoothAdapterClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + GetProperties(object_path_); + + return properties->discovering.value(); +} + +void BluetoothAdapterChromeOS::StartDiscovering( + const base::Closure& callback, + const ErrorCallback& error_callback) { + // BlueZ counts discovery sessions, and permits multiple sessions for a + // single connection, so issue a StartDiscovery() call for every use + // within Chromium for the right behavior. + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + StartDiscovery( + object_path_, + base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery, + weak_ptr_factory_.GetWeakPtr(), + callback), + base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError, + weak_ptr_factory_.GetWeakPtr(), + error_callback)); +} + +void BluetoothAdapterChromeOS::StopDiscovering( + const base::Closure& callback, + const ErrorCallback& error_callback) { + // Inform BlueZ to stop one of our open discovery sessions. + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + StopDiscovery( + object_path_, + base::Bind(&BluetoothAdapterChromeOS::OnStopDiscovery, + weak_ptr_factory_.GetWeakPtr(), + callback), + base::Bind(&BluetoothAdapterChromeOS::OnStopDiscoveryError, + weak_ptr_factory_.GetWeakPtr(), + error_callback)); +} + +void BluetoothAdapterChromeOS::ReadLocalOutOfBandPairingData( + const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback, + const ErrorCallback& error_callback) { + error_callback.Run(); +} + +void BluetoothAdapterChromeOS::AdapterAdded( + const dbus::ObjectPath& object_path) { + // Set the adapter to the newly added adapter only if no adapter is present. + if (!IsPresent()) + SetAdapter(object_path); +} + +void BluetoothAdapterChromeOS::AdapterRemoved( + const dbus::ObjectPath& object_path) { + if (object_path == object_path_) + RemoveAdapter(); +} + +void BluetoothAdapterChromeOS::AdapterPropertyChanged( + const dbus::ObjectPath& object_path, + const std::string& property_name) { + if (object_path != object_path_) + return; + + BluetoothAdapterClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + GetProperties(object_path_); + + if (property_name == properties->powered.name()) + PoweredChanged(properties->powered.value()); + else if (property_name == properties->discovering.name()) + DiscoveringChanged(properties->discovering.value()); +} + +void BluetoothAdapterChromeOS::DeviceAdded( + const dbus::ObjectPath& object_path) { + BluetoothDeviceClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + GetProperties(object_path); + if (properties->adapter.value() != object_path_) + return; + + BluetoothDeviceChromeOS* device_chromeos = + new BluetoothDeviceChromeOS(this, object_path); + DCHECK(devices_.find(device_chromeos->GetAddress()) == devices_.end()); + + devices_[device_chromeos->GetAddress()] = device_chromeos; + + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceAdded(this, device_chromeos)); +} + +void BluetoothAdapterChromeOS::DeviceRemoved( + const dbus::ObjectPath& object_path) { + for (DevicesMap::iterator iter = devices_.begin(); + iter != devices_.end(); ++iter) { + BluetoothDeviceChromeOS* device_chromeos = + static_cast(iter->second); + if (device_chromeos->object_path() == object_path) { + devices_.erase(iter); + + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceRemoved(this, device_chromeos)); + delete device_chromeos; + return; + } + } +} + +void BluetoothAdapterChromeOS::DevicePropertyChanged( + const dbus::ObjectPath& object_path, + const std::string& property_name) { + BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path); + if (!device_chromeos) + return; + + BluetoothDeviceClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + GetProperties(object_path); + + if (property_name == properties->bluetooth_class.name() || + property_name == properties->address.name() || + property_name == properties->alias.name() || + property_name == properties->paired.name() || + property_name == properties->trusted.name() || + property_name == properties->connected.name() || + property_name == properties->uuids.name()) + NotifyDeviceChanged(device_chromeos); + + // UMA connection counting + if (property_name == properties->connected.name()) { + int count = 0; + + for (DevicesMap::iterator iter = devices_.begin(); + iter != devices_.end(); ++iter) { + if (iter->second->IsPaired() && iter->second->IsConnected()) + ++count; + } + + UMA_HISTOGRAM_COUNTS_100("Bluetooth.ConnectedDeviceCount", count); + } +} + +void BluetoothAdapterChromeOS::InputPropertyChanged( + const dbus::ObjectPath& object_path, + const std::string& property_name) { + BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path); + if (!device_chromeos) + return; + + BluetoothInputClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothInputClient()-> + GetProperties(object_path); + + // Properties structure can be removed, which triggers a change in the + // BluetoothDevice::IsConnectable() property, as does a change in the + // actual reconnect_mode property. + if (!properties || + property_name == properties->reconnect_mode.name()) + NotifyDeviceChanged(device_chromeos); +} + +BluetoothDeviceChromeOS* +BluetoothAdapterChromeOS::GetDeviceWithPath( + const dbus::ObjectPath& object_path) { + for (DevicesMap::iterator iter = devices_.begin(); + iter != devices_.end(); ++iter) { + BluetoothDeviceChromeOS* device_chromeos = + static_cast(iter->second); + if (device_chromeos->object_path() == object_path) + return device_chromeos; + } + + return NULL; +} + +void BluetoothAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) { + DCHECK(!IsPresent()); + object_path_ = object_path; + + VLOG(1) << object_path_.value() << ": using adapter."; + + SetAdapterName(); + + BluetoothAdapterClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + GetProperties(object_path_); + + PresentChanged(true); + + if (properties->powered.value()) + PoweredChanged(true); + if (properties->discovering.value()) + DiscoveringChanged(true); + + std::vector device_paths = + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + GetDevicesForAdapter(object_path_); + + for (std::vector::iterator iter = device_paths.begin(); + iter != device_paths.end(); ++iter) { + BluetoothDeviceChromeOS* device_chromeos = + new BluetoothDeviceChromeOS(this, *iter); + + devices_[device_chromeos->GetAddress()] = device_chromeos; + + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceAdded(this, device_chromeos)); + } +} + +void BluetoothAdapterChromeOS::SetAdapterName() { + // Set a better name for the adapter than "BlueZ 5.x"; this isn't an ideal + // way to do this but it'll do for now. See http://crbug.com/126732 and + // http://crbug.com/126802. + std::string board; + const CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(chromeos::switches::kChromeOSReleaseBoard)) { + board = command_line-> + GetSwitchValueASCII(chromeos::switches::kChromeOSReleaseBoard); + } + + std::string alias; + if (board.substr(0, 6) == "stumpy") { + alias = "Chromebox"; + } else if (board.substr(0, 4) == "link") { + alias = "Chromebook Pixel"; + } else { + alias = "Chromebook"; + } + + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + GetProperties(object_path_)->alias.Set( + alias, + base::Bind(&BluetoothAdapterChromeOS::OnSetAlias, + weak_ptr_factory_.GetWeakPtr())); +} + +void BluetoothAdapterChromeOS::OnSetAlias(bool success) { + LOG_IF(WARNING, !success) << object_path_.value() + << ": Failed to set adapter alias"; +} + +void BluetoothAdapterChromeOS::RemoveAdapter() { + DCHECK(IsPresent()); + VLOG(1) << object_path_.value() << ": adapter removed."; + + BluetoothAdapterClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + GetProperties(object_path_); + + object_path_ = dbus::ObjectPath(""); + + if (properties->powered.value()) + PoweredChanged(false); + if (properties->discovering.value()) + DiscoveringChanged(false); + + // Copy the devices list here and clear the original so that when we + // send DeviceRemoved(), GetDevices() returns no devices. + DevicesMap devices = devices_; + devices_.clear(); + + for (DevicesMap::iterator iter = devices.begin(); + iter != devices.end(); ++iter) { + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceRemoved(this, iter->second)); + delete iter->second; + } + + PresentChanged(false); +} + +void BluetoothAdapterChromeOS::PoweredChanged(bool powered) { + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + AdapterPoweredChanged(this, powered)); +} + +void BluetoothAdapterChromeOS::DiscoveringChanged( + bool discovering) { + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + AdapterDiscoveringChanged(this, discovering)); +} + +void BluetoothAdapterChromeOS::PresentChanged(bool present) { + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + AdapterPresentChanged(this, present)); +} + +void BluetoothAdapterChromeOS::NotifyDeviceChanged( + BluetoothDeviceChromeOS* device) { + DCHECK(device->adapter_ == this); + + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceChanged(this, device)); +} + +void BluetoothAdapterChromeOS::OnSetPowered(const base::Closure& callback, + const ErrorCallback& error_callback, + bool success) { + if (success) + callback.Run(); + else + error_callback.Run(); +} + +void BluetoothAdapterChromeOS::OnStartDiscovery(const base::Closure& callback) { + callback.Run(); +} + +void BluetoothAdapterChromeOS::OnStartDiscoveryError( + const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message) { + LOG(WARNING) << object_path_.value() << ": Failed to start discovery: " + << error_name << ": " << error_message; + error_callback.Run(); +} + +void BluetoothAdapterChromeOS::OnStopDiscovery(const base::Closure& callback) { + callback.Run(); +} + +void BluetoothAdapterChromeOS::OnStopDiscoveryError( + const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message) { + LOG(WARNING) << object_path_.value() << ": Failed to stop discovery: " + << error_name << ": " << error_message; + error_callback.Run(); +} + +} // namespace chromeos diff --git a/device/bluetooth/bluetooth_adapter_chromeos.h b/device/bluetooth/bluetooth_adapter_chromeos.h new file mode 100644 index 0000000..74beb35 --- /dev/null +++ b/device/bluetooth/bluetooth_adapter_chromeos.h @@ -0,0 +1,148 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_CHROMEOS_H_ +#define DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_CHROMEOS_H_ + +#include + +#include "base/memory/weak_ptr.h" +#include "chromeos/dbus/bluetooth_adapter_client.h" +#include "chromeos/dbus/bluetooth_device_client.h" +#include "chromeos/dbus/bluetooth_input_client.h" +#include "dbus/object_path.h" +#include "device/bluetooth/bluetooth_adapter.h" + +namespace device { + +class BluetoothAdapterFactory; + +} // namespace device + +namespace chromeos { + +class BluetoothChromeOSTest; +class BluetoothDeviceChromeOS; + +// The BluetoothAdapterChromeOS class implements BluetoothAdapter for the +// Chrome OS platform. +class BluetoothAdapterChromeOS + : public device::BluetoothAdapter, + private chromeos::BluetoothAdapterClient::Observer, + private chromeos::BluetoothDeviceClient::Observer, + private chromeos::BluetoothInputClient::Observer { + public: + // BluetoothAdapter override + virtual void AddObserver( + device::BluetoothAdapter::Observer* observer) OVERRIDE; + virtual void RemoveObserver( + device::BluetoothAdapter::Observer* observer) OVERRIDE; + virtual std::string GetAddress() const OVERRIDE; + virtual std::string GetName() const OVERRIDE; + virtual bool IsInitialized() const OVERRIDE; + virtual bool IsPresent() const OVERRIDE; + virtual bool IsPowered() const OVERRIDE; + virtual void SetPowered( + bool powered, + const base::Closure& callback, + const ErrorCallback& error_callback) OVERRIDE; + virtual bool IsDiscovering() const OVERRIDE; + 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 ReadLocalOutOfBandPairingData( + const device::BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& + callback, + const ErrorCallback& error_callback) OVERRIDE; + + private: + friend class device::BluetoothAdapterFactory; + friend class BluetoothChromeOSTest; + friend class BluetoothDeviceChromeOS; + friend class BluetoothProfileChromeOS; + friend class BluetoothProfileChromeOSTest; + + BluetoothAdapterChromeOS(); + virtual ~BluetoothAdapterChromeOS(); + + // BluetoothAdapterClient::Observer override. + virtual void AdapterAdded(const dbus::ObjectPath& object_path) OVERRIDE; + virtual void AdapterRemoved(const dbus::ObjectPath& object_path) OVERRIDE; + virtual void AdapterPropertyChanged( + const dbus::ObjectPath& object_path, + const std::string& property_name) OVERRIDE; + + // BluetoothDeviceClient::Observer override. + virtual void DeviceAdded(const dbus::ObjectPath& object_path) OVERRIDE; + virtual void DeviceRemoved(const dbus::ObjectPath& object_path) OVERRIDE; + virtual void DevicePropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) OVERRIDE; + + // BluetoothInputClient::Observer override. + virtual void InputPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) OVERRIDE; + + // 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); + + // Set the tracked adapter to the one in |object_path|, this object will + // subsequently operate on that adapter until it is removed. + void SetAdapter(const dbus::ObjectPath& object_path); + + // Set the adapter name to one chosen from the system information, and method + // called by dbus:: on completion of the alias property change. + void SetAdapterName(); + void OnSetAlias(bool success); + + // Remove the currently tracked adapter. IsPresent() will return false after + // this is called. + void RemoveAdapter(); + + // Announce to observers a change in the adapter state. + void PoweredChanged(bool powered); + void DiscoveringChanged(bool discovering); + void PresentChanged(bool present); + + // Announce to observers a change in device state that is not reflected by + // its D-Bus properties. + void NotifyDeviceChanged(BluetoothDeviceChromeOS* device); + + // Called by dbus:: on completion of the powered property change. + void OnSetPowered(const base::Closure& callback, + const ErrorCallback& error_callback, + bool success); + + // Called by dbus:: on completion of the D-Bus method call to start discovery. + void OnStartDiscovery(const base::Closure& callback); + void OnStartDiscoveryError(const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message); + + // Called by dbus:: on completion of the D-Bus method call to stop discovery. + void OnStopDiscovery(const base::Closure& callback); + void OnStopDiscoveryError(const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message); + + // Object path of the adapter we track. + dbus::ObjectPath object_path_; + + // List of observers interested in event notifications from us. + ObserverList observers_; + + // Note: This should remain the last member so it'll be destroyed and + // invalidate its weak pointers before any other members are destroyed. + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothAdapterChromeOS); +}; + +} // namespace chromeos + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_CHROMEOS_H_ diff --git a/device/bluetooth/bluetooth_adapter_experimental_chromeos.cc b/device/bluetooth/bluetooth_adapter_experimental_chromeos.cc deleted file mode 100644 index 579dbb5..0000000 --- a/device/bluetooth/bluetooth_adapter_experimental_chromeos.cc +++ /dev/null @@ -1,468 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "device/bluetooth/bluetooth_adapter_experimental_chromeos.h" - -#include - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/logging.h" -#include "base/metrics/histogram.h" -#include "chromeos/chromeos_switches.h" -#include "chromeos/dbus/dbus_thread_manager.h" -#include "chromeos/dbus/experimental_bluetooth_adapter_client.h" -#include "chromeos/dbus/experimental_bluetooth_device_client.h" -#include "chromeos/dbus/experimental_bluetooth_input_client.h" -#include "device/bluetooth/bluetooth_device.h" -#include "device/bluetooth/bluetooth_device_experimental_chromeos.h" - -using device::BluetoothAdapter; -using device::BluetoothDevice; - -namespace chromeos { - -BluetoothAdapterExperimentalChromeOS::BluetoothAdapterExperimentalChromeOS() - : weak_ptr_factory_(this) { - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - AddObserver(this); - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - AddObserver(this); - DBusThreadManager::Get()->GetExperimentalBluetoothInputClient()-> - AddObserver(this); - - std::vector object_paths = - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - GetAdapters(); - - if (!object_paths.empty()) { - VLOG(1) << object_paths.size() << " Bluetooth adapter(s) available."; - SetAdapter(object_paths[0]); - } -} - -BluetoothAdapterExperimentalChromeOS::~BluetoothAdapterExperimentalChromeOS() { - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - RemoveObserver(this); - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - RemoveObserver(this); - DBusThreadManager::Get()->GetExperimentalBluetoothInputClient()-> - RemoveObserver(this); -} - -void BluetoothAdapterExperimentalChromeOS::AddObserver( - BluetoothAdapter::Observer* observer) { - DCHECK(observer); - observers_.AddObserver(observer); -} - -void BluetoothAdapterExperimentalChromeOS::RemoveObserver( - BluetoothAdapter::Observer* observer) { - DCHECK(observer); - observers_.RemoveObserver(observer); -} - -std::string BluetoothAdapterExperimentalChromeOS::GetAddress() const { - if (!IsPresent()) - return std::string(); - - ExperimentalBluetoothAdapterClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - GetProperties(object_path_); - DCHECK(properties); - - return properties->address.value(); -} - -std::string BluetoothAdapterExperimentalChromeOS::GetName() const { - if (!IsPresent()) - return std::string(); - - ExperimentalBluetoothAdapterClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - GetProperties(object_path_); - DCHECK(properties); - - return properties->alias.value(); -} - -bool BluetoothAdapterExperimentalChromeOS::IsInitialized() const { - return true; -} - -bool BluetoothAdapterExperimentalChromeOS::IsPresent() const { - return !object_path_.value().empty(); -} - -bool BluetoothAdapterExperimentalChromeOS::IsPowered() const { - if (!IsPresent()) - return false; - - ExperimentalBluetoothAdapterClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - GetProperties(object_path_); - - return properties->powered.value(); -} - -void BluetoothAdapterExperimentalChromeOS::SetPowered( - bool powered, - const base::Closure& callback, - const ErrorCallback& error_callback) { - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - GetProperties(object_path_)->powered.Set( - powered, - base::Bind(&BluetoothAdapterExperimentalChromeOS::OnSetPowered, - weak_ptr_factory_.GetWeakPtr(), - callback, - error_callback)); -} - -bool BluetoothAdapterExperimentalChromeOS::IsDiscovering() const { - if (!IsPresent()) - return false; - - ExperimentalBluetoothAdapterClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - GetProperties(object_path_); - - return properties->discovering.value(); -} - -void BluetoothAdapterExperimentalChromeOS::StartDiscovering( - const base::Closure& callback, - const ErrorCallback& error_callback) { - // BlueZ counts discovery sessions, and permits multiple sessions for a - // single connection, so issue a StartDiscovery() call for every use - // within Chromium for the right behavior. - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - StartDiscovery( - object_path_, - base::Bind( - &BluetoothAdapterExperimentalChromeOS::OnStartDiscovery, - weak_ptr_factory_.GetWeakPtr(), - callback), - base::Bind( - &BluetoothAdapterExperimentalChromeOS::OnStartDiscoveryError, - weak_ptr_factory_.GetWeakPtr(), - error_callback)); -} - -void BluetoothAdapterExperimentalChromeOS::StopDiscovering( - const base::Closure& callback, - const ErrorCallback& error_callback) { - // Inform BlueZ to stop one of our open discovery sessions. - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - StopDiscovery( - object_path_, - base::Bind( - &BluetoothAdapterExperimentalChromeOS::OnStopDiscovery, - weak_ptr_factory_.GetWeakPtr(), - callback), - base::Bind( - &BluetoothAdapterExperimentalChromeOS::OnStopDiscoveryError, - weak_ptr_factory_.GetWeakPtr(), - error_callback)); -} - -void BluetoothAdapterExperimentalChromeOS::ReadLocalOutOfBandPairingData( - const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback, - const ErrorCallback& error_callback) { - error_callback.Run(); -} - -void BluetoothAdapterExperimentalChromeOS::AdapterAdded( - const dbus::ObjectPath& object_path) { - // Set the adapter to the newly added adapter only if no adapter is present. - if (!IsPresent()) - SetAdapter(object_path); -} - -void BluetoothAdapterExperimentalChromeOS::AdapterRemoved( - const dbus::ObjectPath& object_path) { - if (object_path == object_path_) - RemoveAdapter(); -} - -void BluetoothAdapterExperimentalChromeOS::AdapterPropertyChanged( - const dbus::ObjectPath& object_path, - const std::string& property_name) { - if (object_path != object_path_) - return; - - ExperimentalBluetoothAdapterClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - GetProperties(object_path_); - - if (property_name == properties->powered.name()) - PoweredChanged(properties->powered.value()); - else if (property_name == properties->discovering.name()) - DiscoveringChanged(properties->discovering.value()); -} - -void BluetoothAdapterExperimentalChromeOS::DeviceAdded( - const dbus::ObjectPath& object_path) { - ExperimentalBluetoothDeviceClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - GetProperties(object_path); - if (properties->adapter.value() != object_path_) - return; - - BluetoothDeviceExperimentalChromeOS* device_chromeos = - new BluetoothDeviceExperimentalChromeOS(this, object_path); - DCHECK(devices_.find(device_chromeos->GetAddress()) == devices_.end()); - - devices_[device_chromeos->GetAddress()] = device_chromeos; - - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceAdded(this, device_chromeos)); -} - -void BluetoothAdapterExperimentalChromeOS::DeviceRemoved( - const dbus::ObjectPath& object_path) { - for (DevicesMap::iterator iter = devices_.begin(); - iter != devices_.end(); ++iter) { - BluetoothDeviceExperimentalChromeOS* device_chromeos = - static_cast(iter->second); - if (device_chromeos->object_path() == object_path) { - devices_.erase(iter); - - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceRemoved(this, device_chromeos)); - delete device_chromeos; - return; - } - } -} - -void BluetoothAdapterExperimentalChromeOS::DevicePropertyChanged( - const dbus::ObjectPath& object_path, - const std::string& property_name) { - BluetoothDeviceExperimentalChromeOS* device_chromeos = - GetDeviceWithPath(object_path); - if (!device_chromeos) - return; - - ExperimentalBluetoothDeviceClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - GetProperties(object_path); - - if (property_name == properties->bluetooth_class.name() || - property_name == properties->address.name() || - property_name == properties->alias.name() || - property_name == properties->paired.name() || - property_name == properties->trusted.name() || - property_name == properties->connected.name() || - property_name == properties->uuids.name()) - NotifyDeviceChanged(device_chromeos); - - // UMA connection counting - if (property_name == properties->connected.name()) { - int count = 0; - - for (DevicesMap::iterator iter = devices_.begin(); - iter != devices_.end(); ++iter) { - if (iter->second->IsPaired() && iter->second->IsConnected()) - ++count; - } - - UMA_HISTOGRAM_COUNTS_100("Bluetooth.ConnectedDeviceCount", count); - } -} - -void BluetoothAdapterExperimentalChromeOS::InputPropertyChanged( - const dbus::ObjectPath& object_path, - const std::string& property_name) { - BluetoothDeviceExperimentalChromeOS* device_chromeos = - GetDeviceWithPath(object_path); - if (!device_chromeos) - return; - - ExperimentalBluetoothInputClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothInputClient()-> - GetProperties(object_path); - - // Properties structure can be removed, which triggers a change in the - // BluetoothDevice::IsConnectable() property, as does a change in the - // actual reconnect_mode property. - if (!properties || - property_name == properties->reconnect_mode.name()) - NotifyDeviceChanged(device_chromeos); -} - -BluetoothDeviceExperimentalChromeOS* -BluetoothAdapterExperimentalChromeOS::GetDeviceWithPath( - const dbus::ObjectPath& object_path) { - for (DevicesMap::iterator iter = devices_.begin(); - iter != devices_.end(); ++iter) { - BluetoothDeviceExperimentalChromeOS* device_chromeos = - static_cast(iter->second); - if (device_chromeos->object_path() == object_path) - return device_chromeos; - } - - return NULL; -} - -void BluetoothAdapterExperimentalChromeOS::SetAdapter( - const dbus::ObjectPath& object_path) { - DCHECK(!IsPresent()); - object_path_ = object_path; - - VLOG(1) << object_path_.value() << ": using adapter."; - - SetAdapterName(); - - ExperimentalBluetoothAdapterClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - GetProperties(object_path_); - - PresentChanged(true); - - if (properties->powered.value()) - PoweredChanged(true); - if (properties->discovering.value()) - DiscoveringChanged(true); - - std::vector device_paths = - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - GetDevicesForAdapter(object_path_); - - for (std::vector::iterator iter = device_paths.begin(); - iter != device_paths.end(); ++iter) { - BluetoothDeviceExperimentalChromeOS* device_chromeos = - new BluetoothDeviceExperimentalChromeOS(this, *iter); - - devices_[device_chromeos->GetAddress()] = device_chromeos; - - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceAdded(this, device_chromeos)); - } -} - -void BluetoothAdapterExperimentalChromeOS::SetAdapterName() { - // Set a better name for the adapter than "BlueZ 5.x"; this isn't an ideal - // way to do this but it'll do for now. See http://crbug.com/126732 and - // http://crbug.com/126802. - std::string board; - const CommandLine* command_line = CommandLine::ForCurrentProcess(); - if (command_line->HasSwitch(chromeos::switches::kChromeOSReleaseBoard)) { - board = command_line-> - GetSwitchValueASCII(chromeos::switches::kChromeOSReleaseBoard); - } - - std::string alias; - if (board.substr(0, 6) == "stumpy") { - alias = "Chromebox"; - } else if (board.substr(0, 4) == "link") { - alias = "Chromebook Pixel"; - } else { - alias = "Chromebook"; - } - - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - GetProperties(object_path_)->alias.Set( - alias, - base::Bind(&BluetoothAdapterExperimentalChromeOS::OnSetAlias, - weak_ptr_factory_.GetWeakPtr())); -} - -void BluetoothAdapterExperimentalChromeOS::OnSetAlias(bool success) { - LOG_IF(WARNING, !success) << object_path_.value() - << ": Failed to set adapter alias"; -} - -void BluetoothAdapterExperimentalChromeOS::RemoveAdapter() { - DCHECK(IsPresent()); - VLOG(1) << object_path_.value() << ": adapter removed."; - - ExperimentalBluetoothAdapterClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - GetProperties(object_path_); - - object_path_ = dbus::ObjectPath(""); - - if (properties->powered.value()) - PoweredChanged(false); - if (properties->discovering.value()) - DiscoveringChanged(false); - - // Copy the devices list here and clear the original so that when we - // send DeviceRemoved(), GetDevices() returns no devices. - DevicesMap devices = devices_; - devices_.clear(); - - for (DevicesMap::iterator iter = devices.begin(); - iter != devices.end(); ++iter) { - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceRemoved(this, iter->second)); - delete iter->second; - } - - PresentChanged(false); -} - -void BluetoothAdapterExperimentalChromeOS::PoweredChanged(bool powered) { - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - AdapterPoweredChanged(this, powered)); -} - -void BluetoothAdapterExperimentalChromeOS::DiscoveringChanged( - bool discovering) { - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - AdapterDiscoveringChanged(this, discovering)); -} - -void BluetoothAdapterExperimentalChromeOS::PresentChanged(bool present) { - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - AdapterPresentChanged(this, present)); -} - -void BluetoothAdapterExperimentalChromeOS::NotifyDeviceChanged( - BluetoothDeviceExperimentalChromeOS* device) { - DCHECK(device->adapter_ == this); - - FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, - DeviceChanged(this, device)); -} - -void BluetoothAdapterExperimentalChromeOS::OnSetPowered( - const base::Closure& callback, - const ErrorCallback& error_callback, - bool success) { - if (success) - callback.Run(); - else - error_callback.Run(); -} - -void BluetoothAdapterExperimentalChromeOS::OnStartDiscovery( - const base::Closure& callback) { - callback.Run(); -} - -void BluetoothAdapterExperimentalChromeOS::OnStartDiscoveryError( - const ErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message) { - LOG(WARNING) << object_path_.value() << ": Failed to start discovery: " - << error_name << ": " << error_message; - error_callback.Run(); -} - -void BluetoothAdapterExperimentalChromeOS::OnStopDiscovery( - const base::Closure& callback) { - callback.Run(); -} - -void BluetoothAdapterExperimentalChromeOS::OnStopDiscoveryError( - const ErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message) { - LOG(WARNING) << object_path_.value() << ": Failed to stop discovery: " - << error_name << ": " << error_message; - error_callback.Run(); -} - -} // namespace chromeos diff --git a/device/bluetooth/bluetooth_adapter_experimental_chromeos.h b/device/bluetooth/bluetooth_adapter_experimental_chromeos.h deleted file mode 100644 index 80dce84..0000000 --- a/device/bluetooth/bluetooth_adapter_experimental_chromeos.h +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_EXPERIMENTAL_CHROMEOS_H_ -#define DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_EXPERIMENTAL_CHROMEOS_H_ - -#include - -#include "base/memory/weak_ptr.h" -#include "chromeos/dbus/experimental_bluetooth_adapter_client.h" -#include "chromeos/dbus/experimental_bluetooth_device_client.h" -#include "chromeos/dbus/experimental_bluetooth_input_client.h" -#include "dbus/object_path.h" -#include "device/bluetooth/bluetooth_adapter.h" - -namespace device { - -class BluetoothAdapterFactory; - -} // namespace device - -namespace chromeos { - -class BluetoothDeviceExperimentalChromeOS; -class BluetoothExperimentalChromeOSTest; - -// The BluetoothAdapterExperimentalChromeOS class is an alternate implementation -// of BluetoothAdapter for the Chrome OS platform using the Bluetooth Smart -// capable backend. It will become the sole implementation for Chrome OS, and -// be renamed to BluetoothAdapterChromeOS, once the backend is switched, -class BluetoothAdapterExperimentalChromeOS - : public device::BluetoothAdapter, - private chromeos::ExperimentalBluetoothAdapterClient::Observer, - private chromeos::ExperimentalBluetoothDeviceClient::Observer, - private chromeos::ExperimentalBluetoothInputClient::Observer { - public: - // BluetoothAdapter override - virtual void AddObserver( - device::BluetoothAdapter::Observer* observer) OVERRIDE; - virtual void RemoveObserver( - device::BluetoothAdapter::Observer* observer) OVERRIDE; - virtual std::string GetAddress() const OVERRIDE; - virtual std::string GetName() const OVERRIDE; - virtual bool IsInitialized() const OVERRIDE; - virtual bool IsPresent() const OVERRIDE; - virtual bool IsPowered() const OVERRIDE; - virtual void SetPowered( - bool powered, - const base::Closure& callback, - const ErrorCallback& error_callback) OVERRIDE; - virtual bool IsDiscovering() const OVERRIDE; - 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 ReadLocalOutOfBandPairingData( - const device::BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& - callback, - const ErrorCallback& error_callback) OVERRIDE; - - private: - friend class device::BluetoothAdapterFactory; - friend class BluetoothDeviceExperimentalChromeOS; - friend class BluetoothExperimentalChromeOSTest; - friend class BluetoothProfileExperimentalChromeOS; - friend class BluetoothProfileChromeOSTest; - - BluetoothAdapterExperimentalChromeOS(); - virtual ~BluetoothAdapterExperimentalChromeOS(); - - // ExperimentalBluetoothAdapterClient::Observer override. - virtual void AdapterAdded(const dbus::ObjectPath& object_path) OVERRIDE; - virtual void AdapterRemoved(const dbus::ObjectPath& object_path) OVERRIDE; - virtual void AdapterPropertyChanged( - const dbus::ObjectPath& object_path, - const std::string& property_name) OVERRIDE; - - // ExperimentalBluetoothDeviceClient::Observer override. - virtual void DeviceAdded(const dbus::ObjectPath& object_path) OVERRIDE; - virtual void DeviceRemoved(const dbus::ObjectPath& object_path) OVERRIDE; - virtual void DevicePropertyChanged(const dbus::ObjectPath& object_path, - const std::string& property_name) OVERRIDE; - - // ExperimentalBluetoothInputClient::Observer override. - virtual void InputPropertyChanged(const dbus::ObjectPath& object_path, - const std::string& property_name) OVERRIDE; - - // Internal method used to locate the device object by object path - // (the devices map and BluetoothDevice methods are by address) - BluetoothDeviceExperimentalChromeOS* GetDeviceWithPath( - 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. - void SetAdapter(const dbus::ObjectPath& object_path); - - // Set the adapter name to one chosen from the system information, and method - // called by dbus:: on completion of the alias property change. - void SetAdapterName(); - void OnSetAlias(bool success); - - // Remove the currently tracked adapter. IsPresent() will return false after - // this is called. - void RemoveAdapter(); - - // Announce to observers a change in the adapter state. - void PoweredChanged(bool powered); - void DiscoveringChanged(bool discovering); - void PresentChanged(bool present); - - // Announce to observers a change in device state that is not reflected by - // its D-Bus properties. - void NotifyDeviceChanged(BluetoothDeviceExperimentalChromeOS* device); - - // Called by dbus:: on completion of the powered property change. - void OnSetPowered(const base::Closure& callback, - const ErrorCallback& error_callback, - bool success); - - // Called by dbus:: on completion of the D-Bus method call to start discovery. - void OnStartDiscovery(const base::Closure& callback); - void OnStartDiscoveryError(const ErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message); - - // Called by dbus:: on completion of the D-Bus method call to stop discovery. - void OnStopDiscovery(const base::Closure& callback); - void OnStopDiscoveryError(const ErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message); - - // Object path of the adapter we track. - dbus::ObjectPath object_path_; - - // List of observers interested in event notifications from us. - ObserverList observers_; - - // Note: This should remain the last member so it'll be destroyed and - // invalidate its weak pointers before any other members are destroyed. - base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(BluetoothAdapterExperimentalChromeOS); -}; - -} // namespace chromeos - -#endif // DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_EXPERIMENTAL_CHROMEOS_H_ diff --git a/device/bluetooth/bluetooth_adapter_factory.cc b/device/bluetooth/bluetooth_adapter_factory.cc index 0b9d160..262c95b 100644 --- a/device/bluetooth/bluetooth_adapter_factory.cc +++ b/device/bluetooth/bluetooth_adapter_factory.cc @@ -13,7 +13,7 @@ #include "device/bluetooth/bluetooth_adapter.h" #if defined(OS_CHROMEOS) -#include "device/bluetooth/bluetooth_adapter_experimental_chromeos.h" +#include "device/bluetooth/bluetooth_adapter_chromeos.h" #elif defined(OS_WIN) #include "device/bluetooth/bluetooth_adapter_win.h" #elif defined(OS_MACOSX) @@ -74,8 +74,8 @@ bool BluetoothAdapterFactory::IsBluetoothAdapterAvailable() { void BluetoothAdapterFactory::GetAdapter(const AdapterCallback& callback) { if (!default_adapter.Get().get()) { #if defined(OS_CHROMEOS) - chromeos::BluetoothAdapterExperimentalChromeOS* new_adapter = - new chromeos::BluetoothAdapterExperimentalChromeOS(); + chromeos::BluetoothAdapterChromeOS* new_adapter = + new chromeos::BluetoothAdapterChromeOS(); default_adapter.Get() = new_adapter->weak_ptr_factory_.GetWeakPtr(); #elif defined(OS_WIN) BluetoothAdapterWin* new_adapter = new BluetoothAdapterWin( diff --git a/device/bluetooth/bluetooth_chromeos_unittest.cc b/device/bluetooth/bluetooth_chromeos_unittest.cc new file mode 100644 index 0000000..6020ce4 --- /dev/null +++ b/device/bluetooth/bluetooth_chromeos_unittest.cc @@ -0,0 +1,2027 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop.h" +#include "base/strings/utf_string_conversions.h" +#include "chromeos/dbus/fake_bluetooth_adapter_client.h" +#include "chromeos/dbus/fake_bluetooth_device_client.h" +#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" +#include "dbus/object_path.h" +#include "device/bluetooth/bluetooth_adapter.h" +#include "device/bluetooth/bluetooth_adapter_chromeos.h" +#include "device/bluetooth/bluetooth_adapter_factory.h" +#include "device/bluetooth/bluetooth_device.h" +#include "device/bluetooth/bluetooth_device_chromeos.h" +#include "testing/gtest/include/gtest/gtest.h" + +using device::BluetoothAdapter; +using device::BluetoothAdapterFactory; +using device::BluetoothDevice; + +namespace chromeos { + +class TestObserver : public BluetoothAdapter::Observer { + public: + TestObserver(scoped_refptr adapter) + : present_changed_count_(0), + powered_changed_count_(0), + discovering_changed_count_(0), + last_present_(false), + last_powered_(false), + last_discovering_(false), + device_added_count_(0), + device_changed_count_(0), + device_removed_count_(0), + last_device_(NULL), + adapter_(adapter) { + } + virtual ~TestObserver() {} + + virtual void AdapterPresentChanged(BluetoothAdapter* adapter, + bool present) OVERRIDE { + EXPECT_EQ(adapter_, adapter); + + ++present_changed_count_; + last_present_ = present; + } + + virtual void AdapterPoweredChanged(BluetoothAdapter* adapter, + bool powered) OVERRIDE { + EXPECT_EQ(adapter_, adapter); + + ++powered_changed_count_; + last_powered_ = powered; + } + + virtual void AdapterDiscoveringChanged(BluetoothAdapter* adapter, + bool discovering) OVERRIDE { + EXPECT_EQ(adapter_, adapter); + + ++discovering_changed_count_; + last_discovering_ = discovering; + } + + virtual void DeviceAdded(BluetoothAdapter* adapter, + BluetoothDevice* device) OVERRIDE { + EXPECT_EQ(adapter_, adapter); + + ++device_added_count_; + last_device_ = device; + last_device_address_ = device->GetAddress(); + + QuitMessageLoop(); + } + + virtual void DeviceChanged(BluetoothAdapter* adapter, + BluetoothDevice* device) OVERRIDE { + EXPECT_EQ(adapter_, adapter); + + ++device_changed_count_; + last_device_ = device; + last_device_address_ = device->GetAddress(); + + QuitMessageLoop(); + } + + virtual void DeviceRemoved(BluetoothAdapter* adapter, + BluetoothDevice* device) OVERRIDE { + EXPECT_EQ(adapter_, adapter); + + ++device_removed_count_; + // Can't save device, it may be freed + last_device_address_ = device->GetAddress(); + + QuitMessageLoop(); + } + + int present_changed_count_; + int powered_changed_count_; + int discovering_changed_count_; + bool last_present_; + bool last_powered_; + bool last_discovering_; + int device_added_count_; + int device_changed_count_; + int device_removed_count_; + BluetoothDevice* last_device_; + std::string last_device_address_; + + 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(); + } + + scoped_refptr adapter_; +}; + +class TestPairingDelegate : public BluetoothDevice::PairingDelegate { + public: + TestPairingDelegate() + : call_count_(0), + request_pincode_count_(0), + request_passkey_count_(0), + display_pincode_count_(0), + display_passkey_count_(0), + keys_entered_count_(0), + confirm_passkey_count_(0), + dismiss_count_(0), + last_passkey_(9999999U), + last_entered_(999U) {} + virtual ~TestPairingDelegate() {} + + virtual void RequestPinCode(BluetoothDevice* device) OVERRIDE { + ++call_count_; + ++request_pincode_count_; + QuitMessageLoop(); + } + + virtual void RequestPasskey(BluetoothDevice* device) OVERRIDE { + ++call_count_; + ++request_passkey_count_; + QuitMessageLoop(); + } + + virtual void DisplayPinCode(BluetoothDevice* device, + const std::string& pincode) OVERRIDE { + ++call_count_; + ++display_pincode_count_; + last_pincode_ = pincode; + QuitMessageLoop(); + } + + virtual void DisplayPasskey(BluetoothDevice* device, + uint32 passkey) OVERRIDE { + ++call_count_; + ++display_passkey_count_; + last_passkey_ = passkey; + QuitMessageLoop(); + } + + virtual void KeysEntered(BluetoothDevice* device, uint32 entered) OVERRIDE { + ++call_count_; + ++keys_entered_count_; + last_entered_ = entered; + QuitMessageLoop(); + } + + virtual void ConfirmPasskey(BluetoothDevice* device, + uint32 passkey) OVERRIDE { + ++call_count_; + ++confirm_passkey_count_; + last_passkey_ = passkey; + QuitMessageLoop(); + } + + virtual void DismissDisplayOrConfirm() OVERRIDE { + ++call_count_; + ++dismiss_count_; + QuitMessageLoop(); + } + + int call_count_; + int request_pincode_count_; + int request_passkey_count_; + int display_pincode_count_; + int display_passkey_count_; + int keys_entered_count_; + int confirm_passkey_count_; + int dismiss_count_; + uint32 last_passkey_; + uint32 last_entered_; + std::string last_pincode_; + + 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(); + } +}; + +class BluetoothChromeOSTest : public testing::Test { + public: + virtual void SetUp() { + mock_dbus_thread_manager_ = + new MockDBusThreadManagerWithoutGMock(); + DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager_); + + fake_bluetooth_adapter_client_ = + mock_dbus_thread_manager_->fake_bluetooth_adapter_client(); + fake_bluetooth_device_client_ = + mock_dbus_thread_manager_->fake_bluetooth_device_client(); + + callback_count_ = 0; + error_callback_count_ = 0; + last_connect_error_ = BluetoothDevice::ERROR_UNKNOWN; + } + + virtual void TearDown() { + adapter_ = NULL; + DBusThreadManager::Shutdown(); + } + + // Generic callbacks + void Callback() { + ++callback_count_; + } + + void ErrorCallback() { + ++error_callback_count_; + } + + void ConnectErrorCallback(enum BluetoothDevice::ConnectErrorCode error) { + ++error_callback_count_; + last_connect_error_ = error; + } + + // Call to fill the adapter_ member with a BluetoothAdapter instance. + void GetAdapter() { + adapter_ = new BluetoothAdapterChromeOS(); + ASSERT_TRUE(adapter_ != NULL); + ASSERT_TRUE(adapter_->IsInitialized()); + } + + // Run a discovery phase until the named device is detected, or if the named + // device is not created, the discovery process ends without finding it. + // + // The correct behavior of discovery is tested by the "Discovery" test case + // without using this function. + void DiscoverDevice(const std::string& address) { + ASSERT_TRUE(adapter_ != NULL); + + if (base::MessageLoop::current() == NULL) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + DiscoverDevices(); + return; + } + + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + adapter_->SetPowered( + true, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + adapter_->StartDiscovering( + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + ASSERT_EQ(2, callback_count_); + ASSERT_EQ(0, error_callback_count_); + callback_count_ = 0; + + ASSERT_TRUE(adapter_->IsPowered()); + ASSERT_TRUE(adapter_->IsDiscovering()); + + while (!observer.device_removed_count_ && + observer.last_device_address_ != address) + base::MessageLoop::current()->Run(); + + adapter_->StopDiscovering( + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + ASSERT_EQ(1, callback_count_); + ASSERT_EQ(0, error_callback_count_); + callback_count_ = 0; + + ASSERT_FALSE(adapter_->IsDiscovering()); + + adapter_->RemoveObserver(&observer); + } + + // Run a discovery phase so we have devices that can be paired with. + void DiscoverDevices() { + // Pass an invalid address for the device so that the discovery process + // completes with all devices. + DiscoverDevice("does not exist"); + } + + protected: + FakeBluetoothAdapterClient* fake_bluetooth_adapter_client_; + FakeBluetoothDeviceClient* fake_bluetooth_device_client_; + MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager_; + scoped_refptr adapter_; + + int callback_count_; + int error_callback_count_; + enum BluetoothDevice::ConnectErrorCode last_connect_error_; +}; + +TEST_F(BluetoothChromeOSTest, AlreadyPresent) { + GetAdapter(); + + // This verifies that the class gets the list of adapters when created; + // and initializes with an existing adapter if there is one. + EXPECT_TRUE(adapter_->IsPresent()); + EXPECT_FALSE(adapter_->IsPowered()); + EXPECT_EQ(FakeBluetoothAdapterClient::kAdapterAddress, + adapter_->GetAddress()); + EXPECT_FALSE(adapter_->IsDiscovering()); + + // There should be a device + BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); + EXPECT_EQ(1U, devices.size()); + EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, + devices[0]->GetAddress()); +} + +TEST_F(BluetoothChromeOSTest, BecomePresent) { + fake_bluetooth_adapter_client_->SetVisible(false); + GetAdapter(); + ASSERT_FALSE(adapter_->IsPresent()); + + // Install an observer; expect the AdapterPresentChanged to be called + // with true, and IsPresent() to return true. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + fake_bluetooth_adapter_client_->SetVisible(true); + + EXPECT_EQ(1, observer.present_changed_count_); + EXPECT_TRUE(observer.last_present_); + + EXPECT_TRUE(adapter_->IsPresent()); + + // We should have had a device announced. + EXPECT_EQ(1, observer.device_added_count_); + EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, + observer.last_device_address_); + + // Other callbacks shouldn't be called if the values are false. + EXPECT_EQ(0, observer.powered_changed_count_); + EXPECT_EQ(0, observer.discovering_changed_count_); + EXPECT_FALSE(adapter_->IsPowered()); + EXPECT_FALSE(adapter_->IsDiscovering()); +} + +TEST_F(BluetoothChromeOSTest, BecomeNotPresent) { + GetAdapter(); + ASSERT_TRUE(adapter_->IsPresent()); + + // Install an observer; expect the AdapterPresentChanged to be called + // with false, and IsPresent() to return false. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + fake_bluetooth_adapter_client_->SetVisible(false); + + EXPECT_EQ(1, observer.present_changed_count_); + EXPECT_FALSE(observer.last_present_); + + EXPECT_FALSE(adapter_->IsPresent()); + + // We should have had a device removed. + EXPECT_EQ(1, observer.device_removed_count_); + EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, + observer.last_device_address_); + + // Other callbacks shouldn't be called since the values are false. + EXPECT_EQ(0, observer.powered_changed_count_); + EXPECT_EQ(0, observer.discovering_changed_count_); + EXPECT_FALSE(adapter_->IsPowered()); + EXPECT_FALSE(adapter_->IsDiscovering()); +} + +TEST_F(BluetoothChromeOSTest, SecondAdapter) { + GetAdapter(); + ASSERT_TRUE(adapter_->IsPresent()); + + // Install an observer, then add a second adapter. Nothing should change, + // we ignore the second adapter. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + fake_bluetooth_adapter_client_->SetSecondVisible(true); + + EXPECT_EQ(0, observer.present_changed_count_); + + EXPECT_TRUE(adapter_->IsPresent()); + EXPECT_EQ(FakeBluetoothAdapterClient::kAdapterAddress, + adapter_->GetAddress()); + + // Try removing the first adapter, we should now act as if the adapter + // is no longer present rather than fall back to the second. + fake_bluetooth_adapter_client_->SetVisible(false); + + EXPECT_EQ(1, observer.present_changed_count_); + EXPECT_FALSE(observer.last_present_); + + EXPECT_FALSE(adapter_->IsPresent()); + + // We should have had a device removed. + EXPECT_EQ(1, observer.device_removed_count_); + EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, + observer.last_device_address_); + + // Other callbacks shouldn't be called since the values are false. + EXPECT_EQ(0, observer.powered_changed_count_); + EXPECT_EQ(0, observer.discovering_changed_count_); + EXPECT_FALSE(adapter_->IsPowered()); + EXPECT_FALSE(adapter_->IsDiscovering()); + + observer.device_removed_count_ = 0; + + // Removing the second adapter shouldn't set anything either. + fake_bluetooth_adapter_client_->SetSecondVisible(false); + + EXPECT_EQ(0, observer.device_removed_count_); + EXPECT_EQ(0, observer.powered_changed_count_); + EXPECT_EQ(0, observer.discovering_changed_count_); +} + +TEST_F(BluetoothChromeOSTest, BecomePowered) { + GetAdapter(); + ASSERT_FALSE(adapter_->IsPowered()); + + // Install an observer; expect the AdapterPoweredChanged to be called + // with true, and IsPowered() to return true. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + adapter_->SetPowered( + true, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + EXPECT_EQ(1, observer.powered_changed_count_); + EXPECT_TRUE(observer.last_powered_); + + EXPECT_TRUE(adapter_->IsPowered()); +} + +TEST_F(BluetoothChromeOSTest, BecomeNotPowered) { + GetAdapter(); + adapter_->SetPowered( + true, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + callback_count_ = 0; + + ASSERT_TRUE(adapter_->IsPowered()); + + // Install an observer; expect the AdapterPoweredChanged to be called + // with false, and IsPowered() to return false. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + adapter_->SetPowered( + false, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + EXPECT_EQ(1, observer.powered_changed_count_); + EXPECT_FALSE(observer.last_powered_); + + EXPECT_FALSE(adapter_->IsPowered()); +} + +TEST_F(BluetoothChromeOSTest, StopDiscovery) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + + GetAdapter(); + + adapter_->SetPowered( + true, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + adapter_->StartDiscovering( + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(2, callback_count_); + EXPECT_EQ(0, error_callback_count_); + callback_count_ = 0; + + ASSERT_TRUE(adapter_->IsPowered()); + ASSERT_TRUE(adapter_->IsDiscovering()); + + // Install an observer; aside from the callback, expect the + // AdapterDiscoveringChanged method to be called and no longer to be + // discovering, + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + adapter_->StopDiscovering( + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + EXPECT_EQ(1, observer.discovering_changed_count_); + EXPECT_FALSE(observer.last_discovering_); + + EXPECT_FALSE(adapter_->IsDiscovering()); +} + +TEST_F(BluetoothChromeOSTest, StopDiscoveryAfterTwoStarts) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + + GetAdapter(); + + adapter_->SetPowered( + true, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + adapter_->StartDiscovering( + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(2, callback_count_); + EXPECT_EQ(0, error_callback_count_); + callback_count_ = 0; + + ASSERT_TRUE(adapter_->IsPowered()); + ASSERT_TRUE(adapter_->IsDiscovering()); + + // Install an observer and start discovering again; only the callback + // should be called since we were already discovering to begin with. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + adapter_->StartDiscovering( + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + callback_count_ = 0; + + EXPECT_EQ(0, observer.discovering_changed_count_); + + // Stop discovering; only the callback should be called since we're still + // discovering. The adapter should be still discovering. + adapter_->StopDiscovering( + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + callback_count_ = 0; + + EXPECT_EQ(0, observer.discovering_changed_count_); + + EXPECT_TRUE(adapter_->IsDiscovering()); + + // Stop discovering one more time; aside from the callback, expect the + // AdapterDiscoveringChanged method to be called and no longer to be + // discovering, + adapter_->StopDiscovering( + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + EXPECT_EQ(1, observer.discovering_changed_count_); + EXPECT_FALSE(observer.last_discovering_); + + EXPECT_FALSE(adapter_->IsDiscovering()); +} + +TEST_F(BluetoothChromeOSTest, Discovery) { + // Test a simulated discovery session. + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + GetAdapter(); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + adapter_->SetPowered( + true, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + adapter_->StartDiscovering( + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(2, callback_count_); + EXPECT_EQ(0, error_callback_count_); + callback_count_ = 0; + + ASSERT_TRUE(adapter_->IsPowered()); + ASSERT_TRUE(adapter_->IsDiscovering()); + + // First device to appear should be an Apple Mouse. + message_loop.Run(); + + EXPECT_EQ(1, observer.device_added_count_); + EXPECT_EQ(FakeBluetoothDeviceClient::kAppleMouseAddress, + observer.last_device_address_); + + // Next we should get another two devices... + message_loop.Run(); + EXPECT_EQ(3, observer.device_added_count_); + + // Okay, let's run forward until a device is actually removed... + while (!observer.device_removed_count_) + message_loop.Run(); + + EXPECT_EQ(1, observer.device_removed_count_); + EXPECT_EQ(FakeBluetoothDeviceClient::kVanishingDeviceAddress, + observer.last_device_address_); +} + +TEST_F(BluetoothChromeOSTest, PoweredAndDiscovering) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + + GetAdapter(); + adapter_->SetPowered( + true, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + adapter_->StartDiscovering( + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(2, callback_count_); + EXPECT_EQ(0, error_callback_count_); + callback_count_ = 0; + + // Stop the timers that the simulation uses + fake_bluetooth_device_client_->EndDiscoverySimulation( + dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); + + ASSERT_TRUE(adapter_->IsPowered()); + ASSERT_TRUE(adapter_->IsDiscovering()); + + fake_bluetooth_adapter_client_->SetVisible(false); + ASSERT_FALSE(adapter_->IsPresent()); + + // Install an observer; expect the AdapterPresentChanged, + // AdapterPoweredChanged and AdapterDiscoveringChanged methods to be called + // with true, and IsPresent(), IsPowered() and IsDiscovering() to all + // return true. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + fake_bluetooth_adapter_client_->SetVisible(true); + + EXPECT_EQ(1, observer.present_changed_count_); + EXPECT_TRUE(observer.last_present_); + EXPECT_TRUE(adapter_->IsPresent()); + + EXPECT_EQ(1, observer.powered_changed_count_); + EXPECT_TRUE(observer.last_powered_); + EXPECT_TRUE(adapter_->IsPowered()); + + EXPECT_EQ(1, observer.discovering_changed_count_); + EXPECT_TRUE(observer.last_discovering_); + EXPECT_TRUE(adapter_->IsDiscovering()); + + observer.present_changed_count_ = 0; + observer.powered_changed_count_ = 0; + observer.discovering_changed_count_ = 0; + + // Now mark the adapter not present again. Expect the methods to be called + // again, to reset the properties back to false + fake_bluetooth_adapter_client_->SetVisible(false); + + EXPECT_EQ(1, observer.present_changed_count_); + EXPECT_FALSE(observer.last_present_); + EXPECT_FALSE(adapter_->IsPresent()); + + EXPECT_EQ(1, observer.powered_changed_count_); + EXPECT_FALSE(observer.last_powered_); + EXPECT_FALSE(adapter_->IsPowered()); + + EXPECT_EQ(1, observer.discovering_changed_count_); + EXPECT_FALSE(observer.last_discovering_); + EXPECT_FALSE(adapter_->IsDiscovering()); +} + +TEST_F(BluetoothChromeOSTest, DeviceProperties) { + GetAdapter(); + + BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); + ASSERT_EQ(1U, devices.size()); + ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, + devices[0]->GetAddress()); + + // Verify the other device properties. + EXPECT_EQ(UTF8ToUTF16(FakeBluetoothDeviceClient::kPairedDeviceName), + devices[0]->GetName()); + EXPECT_EQ(BluetoothDevice::DEVICE_COMPUTER, devices[0]->GetDeviceType()); + EXPECT_TRUE(devices[0]->IsPaired()); + EXPECT_FALSE(devices[0]->IsConnected()); + EXPECT_FALSE(devices[0]->IsConnecting()); + + // Non HID devices are always connectable. + EXPECT_TRUE(devices[0]->IsConnectable()); + + BluetoothDevice::ServiceList uuids = devices[0]->GetServices(); + ASSERT_EQ(2U, uuids.size()); + EXPECT_EQ(uuids[0], "00001800-0000-1000-8000-00805f9b34fb"); + EXPECT_EQ(uuids[1], "00001801-0000-1000-8000-00805f9b34fb"); + + EXPECT_EQ(0x05ac, devices[0]->GetVendorID()); + EXPECT_EQ(0x030d, devices[0]->GetProductID()); + EXPECT_EQ(0x0306, devices[0]->GetDeviceID()); +} + +TEST_F(BluetoothChromeOSTest, DeviceClassChanged) { + // Simulate a change of class of a device, as sometimes occurs + // during discovery. + GetAdapter(); + + BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); + ASSERT_EQ(1U, devices.size()); + ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, + devices[0]->GetAddress()); + ASSERT_EQ(BluetoothDevice::DEVICE_COMPUTER, devices[0]->GetDeviceType()); + + // Install an observer; expect the DeviceChanged method to be called when + // we change the class of the device. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + FakeBluetoothDeviceClient::Properties* properties = + fake_bluetooth_device_client_->GetProperties( + dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath)); + + properties->bluetooth_class.ReplaceValue(0x002580); + + EXPECT_EQ(1, observer.device_changed_count_); + EXPECT_EQ(devices[0], observer.last_device_); + + EXPECT_EQ(BluetoothDevice::DEVICE_MOUSE, devices[0]->GetDeviceType()); +} + +TEST_F(BluetoothChromeOSTest, DeviceNameChanged) { + // Simulate a change of name of a device. + GetAdapter(); + + BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); + ASSERT_EQ(1U, devices.size()); + ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, + devices[0]->GetAddress()); + ASSERT_EQ(UTF8ToUTF16(FakeBluetoothDeviceClient::kPairedDeviceName), + devices[0]->GetName()); + + // Install an observer; expect the DeviceChanged method to be called when + // we change the alias of the device. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + FakeBluetoothDeviceClient::Properties* properties = + fake_bluetooth_device_client_->GetProperties( + dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath)); + + static const std::string new_name("New Device Name"); + properties->alias.ReplaceValue(new_name); + + EXPECT_EQ(1, observer.device_changed_count_); + EXPECT_EQ(devices[0], observer.last_device_); + + EXPECT_EQ(UTF8ToUTF16(new_name), devices[0]->GetName()); +} + +TEST_F(BluetoothChromeOSTest, DeviceUuidsChanged) { + // Simulate a change of advertised services of a device. + GetAdapter(); + + BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); + ASSERT_EQ(1U, devices.size()); + ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, + devices[0]->GetAddress()); + + BluetoothDevice::ServiceList uuids = devices[0]->GetServices(); + ASSERT_EQ(2U, uuids.size()); + ASSERT_EQ(uuids[0], "00001800-0000-1000-8000-00805f9b34fb"); + ASSERT_EQ(uuids[1], "00001801-0000-1000-8000-00805f9b34fb"); + + // Install an observer; expect the DeviceChanged method to be called when + // we change the class of the device. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + FakeBluetoothDeviceClient::Properties* properties = + fake_bluetooth_device_client_->GetProperties( + dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath)); + + uuids.push_back("0000110c-0000-1000-8000-00805f9b34fb"); + uuids.push_back("0000110e-0000-1000-8000-00805f9b34fb"); + uuids.push_back("0000110a-0000-1000-8000-00805f9b34fb"); + + properties->uuids.ReplaceValue(uuids); + + EXPECT_EQ(1, observer.device_changed_count_); + EXPECT_EQ(devices[0], observer.last_device_); + + // Fetching the value should give the new one. + uuids = devices[0]->GetServices(); + ASSERT_EQ(5U, uuids.size()); + EXPECT_EQ(uuids[0], "00001800-0000-1000-8000-00805f9b34fb"); + EXPECT_EQ(uuids[1], "00001801-0000-1000-8000-00805f9b34fb"); + EXPECT_EQ(uuids[2], "0000110c-0000-1000-8000-00805f9b34fb"); + EXPECT_EQ(uuids[3], "0000110e-0000-1000-8000-00805f9b34fb"); + EXPECT_EQ(uuids[4], "0000110a-0000-1000-8000-00805f9b34fb"); +} + +TEST_F(BluetoothChromeOSTest, ForgetDevice) { + GetAdapter(); + + BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); + ASSERT_EQ(1U, devices.size()); + ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, + devices[0]->GetAddress()); + + std::string address = devices[0]->GetAddress(); + + // Install an observer; expect the DeviceRemoved method to be called + // with the device we remove. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + devices[0]->Forget( + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(0, error_callback_count_); + + EXPECT_EQ(1, observer.device_removed_count_); + EXPECT_EQ(address, observer.last_device_address_); + + // GetDevices shouldn't return the device either. + devices = adapter_->GetDevices(); + ASSERT_EQ(0U, devices.size()); +} + +TEST_F(BluetoothChromeOSTest, ForgetUnpairedDevice) { + GetAdapter(); + DiscoverDevices(); + + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kMicrosoftMouseAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + // Connect the device so it becomes trusted and remembered. + device->Connect( + NULL, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + ASSERT_EQ(1, callback_count_); + ASSERT_EQ(0, error_callback_count_); + callback_count_ = 0; + + ASSERT_TRUE(device->IsConnected()); + ASSERT_FALSE(device->IsConnecting()); + + // Make sure the trusted property has been set to true. + FakeBluetoothDeviceClient::Properties* properties = + fake_bluetooth_device_client_->GetProperties( + dbus::ObjectPath(FakeBluetoothDeviceClient::kMicrosoftMousePath)); + ASSERT_TRUE(properties->trusted.value()); + + // Install an observer; expect the DeviceRemoved method to be called + // with the device we remove. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + device->Forget( + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + EXPECT_EQ(0, error_callback_count_); + + EXPECT_EQ(1, observer.device_removed_count_); + EXPECT_EQ(FakeBluetoothDeviceClient::kMicrosoftMouseAddress, + observer.last_device_address_); + + // GetDevices shouldn't return the device either. + device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kMicrosoftMouseAddress); + EXPECT_FALSE(device != NULL); +} + +TEST_F(BluetoothChromeOSTest, ConnectPairedDevice) { + GetAdapter(); + + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kPairedDeviceAddress); + ASSERT_TRUE(device != NULL); + ASSERT_TRUE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + // Connect without a pairing delegate; since the device is already Paired + // this should succeed and the device should become connected. + device->Connect( + NULL, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + // Two changes for connecting, one for connected and one for for trusted + // after connecting. + EXPECT_EQ(4, observer.device_changed_count_); + EXPECT_EQ(device, observer.last_device_); + + EXPECT_TRUE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); +} + +TEST_F(BluetoothChromeOSTest, ConnectUnpairableDevice) { + GetAdapter(); + DiscoverDevices(); + + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kMicrosoftMouseAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + // Connect without a pairing delegate; since the device does not require + // pairing, this should succeed and the device should become connected. + device->Connect( + NULL, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + // Two changes for connecting, one for connected, one for for trusted after + // connection, and one for the reconnect mode (IsConnectable). + EXPECT_EQ(5, observer.device_changed_count_); + EXPECT_EQ(device, observer.last_device_); + + EXPECT_TRUE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + + // Make sure the trusted property has been set to true. + FakeBluetoothDeviceClient::Properties* properties = + fake_bluetooth_device_client_->GetProperties( + dbus::ObjectPath(FakeBluetoothDeviceClient::kMicrosoftMousePath)); + EXPECT_TRUE(properties->trusted.value()); + + // Verify is a HID device and is not connectable. + BluetoothDevice::ServiceList uuids = device->GetServices(); + ASSERT_EQ(1U, uuids.size()); + EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb"); + EXPECT_FALSE(device->IsConnectable()); +} + +TEST_F(BluetoothChromeOSTest, ConnectConnectedDevice) { + GetAdapter(); + + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kPairedDeviceAddress); + ASSERT_TRUE(device != NULL); + ASSERT_TRUE(device->IsPaired()); + + device->Connect( + NULL, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + ASSERT_EQ(1, callback_count_); + ASSERT_EQ(0, error_callback_count_); + callback_count_ = 0; + + ASSERT_TRUE(device->IsConnected()); + + // Connect again; since the device is already Connected, this shouldn't do + // anything to initiate the connection. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + device->Connect( + NULL, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + // The observer will be called because Connecting will toggle true and false, + // and the trusted property will be updated to true. + EXPECT_EQ(3, observer.device_changed_count_); + + EXPECT_TRUE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); +} + +TEST_F(BluetoothChromeOSTest, ConnectDeviceFails) { + GetAdapter(); + DiscoverDevices(); + + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kAppleMouseAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + // Connect without a pairing delegate; since the device requires pairing, + // this should fail with an error. + device->Connect( + NULL, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_); + + EXPECT_EQ(2, observer.device_changed_count_); + + EXPECT_FALSE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); +} + +TEST_F(BluetoothChromeOSTest, DisconnectDevice) { + GetAdapter(); + + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kPairedDeviceAddress); + ASSERT_TRUE(device != NULL); + ASSERT_TRUE(device->IsPaired()); + + device->Connect( + NULL, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + ASSERT_EQ(1, callback_count_); + ASSERT_EQ(0, error_callback_count_); + callback_count_ = 0; + + ASSERT_TRUE(device->IsConnected()); + ASSERT_FALSE(device->IsConnecting()); + + // Disconnect the device, we should see the observer method fire and the + // device get dropped. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + device->Disconnect( + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + EXPECT_EQ(1, observer.device_changed_count_); + EXPECT_EQ(device, observer.last_device_); + + EXPECT_FALSE(device->IsConnected()); +} + +TEST_F(BluetoothChromeOSTest, DisconnectUnconnectedDevice) { + GetAdapter(); + + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kPairedDeviceAddress); + ASSERT_TRUE(device != NULL); + ASSERT_TRUE(device->IsPaired()); + ASSERT_FALSE(device->IsConnected()); + + // Disconnect the device, we should see the observer method fire and the + // device get dropped. + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + device->Disconnect( + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + + EXPECT_EQ(0, observer.device_changed_count_); + + EXPECT_FALSE(device->IsConnected()); +} + +TEST_F(BluetoothChromeOSTest, PairAppleMouse) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevices(); + + // The Apple Mouse requires no PIN or Passkey to pair; this is equivalent + // to Simple Secure Pairing or a device with a fixed 0000 PIN. + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kAppleMouseAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(0, pairing_delegate.call_count_); + EXPECT_TRUE(device->IsConnecting()); + + message_loop.Run(); + + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + // Two changes for connecting, one change for connected, one for paired, + // two for trusted (after pairing and connection), and one for the reconnect + // mode (IsConnectable). + EXPECT_EQ(7, observer.device_changed_count_); + EXPECT_EQ(device, observer.last_device_); + + EXPECT_TRUE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + + EXPECT_TRUE(device->IsPaired()); + + // Verify is a HID device and is connectable. + BluetoothDevice::ServiceList uuids = device->GetServices(); + ASSERT_EQ(1U, uuids.size()); + EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb"); + EXPECT_TRUE(device->IsConnectable()); + + // Pairing dialog should be dismissed + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.dismiss_count_); + + // Make sure the trusted property has been set to true. + FakeBluetoothDeviceClient::Properties* properties = + fake_bluetooth_device_client_->GetProperties( + dbus::ObjectPath(FakeBluetoothDeviceClient::kAppleMousePath)); + EXPECT_TRUE(properties->trusted.value()); +} + +TEST_F(BluetoothChromeOSTest, PairAppleKeyboard) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevices(); + + // The Apple Keyboard requires that we display a randomly generated + // PIN on the screen. + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kAppleKeyboardAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.display_pincode_count_); + EXPECT_EQ("123456", pairing_delegate.last_pincode_); + EXPECT_TRUE(device->IsConnecting()); + + message_loop.Run(); + + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + // Two changes for connecting, one change for connected, one for paired, + // two for trusted (after pairing and connection), and one for the reconnect + // mode (IsConnectable). + EXPECT_EQ(7, observer.device_changed_count_); + EXPECT_EQ(device, observer.last_device_); + + EXPECT_TRUE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + + EXPECT_TRUE(device->IsPaired()); + + // Verify is a HID device and is connectable. + BluetoothDevice::ServiceList uuids = device->GetServices(); + ASSERT_EQ(1U, uuids.size()); + EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb"); + EXPECT_TRUE(device->IsConnectable()); + + // Pairing dialog should be dismissed + EXPECT_EQ(1, pairing_delegate.dismiss_count_); + + // Make sure the trusted property has been set to true. + FakeBluetoothDeviceClient::Properties* properties = + fake_bluetooth_device_client_->GetProperties( + dbus::ObjectPath(FakeBluetoothDeviceClient::kAppleKeyboardPath)); + EXPECT_TRUE(properties->trusted.value()); +} + +TEST_F(BluetoothChromeOSTest, PairMotorolaKeyboard) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevices(); + + // The Motorola Keyboard requires that we display a randomly generated + // Passkey on the screen, and notifies us as it's typed in. + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kMotorolaKeyboardAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + // One call for DisplayPasskey() and one for KeysEntered(). + EXPECT_EQ(2, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.display_passkey_count_); + EXPECT_EQ(123456U, pairing_delegate.last_passkey_); + EXPECT_EQ(1, pairing_delegate.keys_entered_count_); + EXPECT_EQ(0U, pairing_delegate.last_entered_); + + EXPECT_TRUE(device->IsConnecting()); + + // One call to KeysEntered() for each key, including [enter]. + for(int i = 1; i <= 7; ++i) { + message_loop.Run(); + + EXPECT_EQ(2 + i, pairing_delegate.call_count_); + EXPECT_EQ(1 + i, pairing_delegate.keys_entered_count_); + EXPECT_EQ(static_cast(i), pairing_delegate.last_entered_); + } + + message_loop.Run(); + + // 8 KeysEntered notifications (0 to 7, inclusive). Two aditional calls for + // DisplayPasskey() and DismissDisplayOrConfirm(). + EXPECT_EQ(10, pairing_delegate.call_count_); + EXPECT_EQ(8, pairing_delegate.keys_entered_count_); + EXPECT_EQ(7U, pairing_delegate.last_entered_); + + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + // Two changes for connecting, one change for connected, one for paired, + // two for trusted (after pairing and connection), and one for the reconnect + // mode (IsConnectable). + EXPECT_EQ(7, observer.device_changed_count_); + EXPECT_EQ(device, observer.last_device_); + + EXPECT_TRUE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + + EXPECT_TRUE(device->IsPaired()); + + // Verify is a HID device. + BluetoothDevice::ServiceList uuids = device->GetServices(); + ASSERT_EQ(1U, uuids.size()); + EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb"); + + // Fake MotorolaKeyboard is not connectable. + EXPECT_FALSE(device->IsConnectable()); + + // Pairing dialog should be dismissed + EXPECT_EQ(1, pairing_delegate.dismiss_count_); + + // Make sure the trusted property has been set to true. + FakeBluetoothDeviceClient::Properties* properties = + fake_bluetooth_device_client_->GetProperties( + dbus::ObjectPath(FakeBluetoothDeviceClient::kMotorolaKeyboardPath)); + EXPECT_TRUE(properties->trusted.value()); +} + +TEST_F(BluetoothChromeOSTest, PairSonyHeadphones) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevices(); + + // The Sony Headphones fake requires that the user enters a PIN for them. + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kSonyHeadphonesAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.request_pincode_count_); + EXPECT_TRUE(device->IsConnecting()); + + // Set the PIN. + device->SetPinCode("1234"); + message_loop.Run(); + + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + // Two changes for connecting, one change for connected, one for paired and + // two for trusted (after pairing and connection). + EXPECT_EQ(6, observer.device_changed_count_); + EXPECT_EQ(device, observer.last_device_); + + EXPECT_TRUE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + + EXPECT_TRUE(device->IsPaired()); + + // Verify is not a HID device. + BluetoothDevice::ServiceList uuids = device->GetServices(); + ASSERT_EQ(0U, uuids.size()); + + // Non HID devices are always connectable. + EXPECT_TRUE(device->IsConnectable()); + + // Pairing dialog should be dismissed + EXPECT_EQ(2, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.dismiss_count_); + + // Make sure the trusted property has been set to true. + FakeBluetoothDeviceClient::Properties* properties = + fake_bluetooth_device_client_->GetProperties( + dbus::ObjectPath(FakeBluetoothDeviceClient::kSonyHeadphonesPath)); + EXPECT_TRUE(properties->trusted.value()); +} + +TEST_F(BluetoothChromeOSTest, PairPhone) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevices(); + + // The fake phone requests that we confirm a displayed passkey. + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kPhoneAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + 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_); + EXPECT_TRUE(device->IsConnecting()); + + // Confirm the passkey. + device->ConfirmPairing(); + message_loop.Run(); + + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + // Two changes for connecting, one change for connected, one for paired and + // two for trusted (after pairing and connection). + EXPECT_EQ(6, observer.device_changed_count_); + EXPECT_EQ(device, observer.last_device_); + + EXPECT_TRUE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + + EXPECT_TRUE(device->IsPaired()); + + // Non HID devices are always connectable. + EXPECT_TRUE(device->IsConnectable()); + + // Pairing dialog should be dismissed + EXPECT_EQ(2, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.dismiss_count_); + + // Make sure the trusted property has been set to true. + FakeBluetoothDeviceClient::Properties* properties = + fake_bluetooth_device_client_->GetProperties( + dbus::ObjectPath(FakeBluetoothDeviceClient::kPhonePath)); + EXPECT_TRUE(properties->trusted.value()); +} + +TEST_F(BluetoothChromeOSTest, PairWeirdDevice) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevices(); + + // Use the "weird device" fake that requires that the user enters a Passkey, + // this would be some kind of device that has a display, but doesn't use + // "just works" - maybe a car? + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kWeirdDeviceAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.request_passkey_count_); + EXPECT_TRUE(device->IsConnecting()); + + // Set the Passkey. + device->SetPasskey(1234); + message_loop.Run(); + + EXPECT_EQ(1, callback_count_); + EXPECT_EQ(0, error_callback_count_); + + // Two changes for connecting, one change for connected, one for paired and + // two for trusted (after pairing and connection). + EXPECT_EQ(6, observer.device_changed_count_); + EXPECT_EQ(device, observer.last_device_); + + EXPECT_TRUE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + + EXPECT_TRUE(device->IsPaired()); + + // Non HID devices are always connectable. + EXPECT_TRUE(device->IsConnectable()); + + // Pairing dialog should be dismissed + EXPECT_EQ(2, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.dismiss_count_); + + // Make sure the trusted property has been set to true. + FakeBluetoothDeviceClient::Properties* properties = + fake_bluetooth_device_client_->GetProperties( + dbus::ObjectPath(FakeBluetoothDeviceClient::kWeirdDevicePath)); + EXPECT_TRUE(properties->trusted.value()); +} + +TEST_F(BluetoothChromeOSTest, PairUnpairableDeviceFails) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevice(FakeBluetoothDeviceClient::kUnconnectableDeviceAddress); + + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kUnpairableDeviceAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(0, pairing_delegate.call_count_); + EXPECT_TRUE(device->IsConnecting()); + + // Run the loop to get the error.. + message_loop.Run(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + + EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_); + + EXPECT_FALSE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + EXPECT_FALSE(device->IsPaired()); + + // Pairing dialog should be dismissed + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.dismiss_count_); +} + +TEST_F(BluetoothChromeOSTest, PairingFails) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevice(FakeBluetoothDeviceClient::kVanishingDeviceAddress); + + // The vanishing device times out during pairing + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kVanishingDeviceAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(0, pairing_delegate.call_count_); + EXPECT_TRUE(device->IsConnecting()); + + // Run the loop to get the error.. + message_loop.Run(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + + EXPECT_EQ(BluetoothDevice::ERROR_AUTH_TIMEOUT, last_connect_error_); + + EXPECT_FALSE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + EXPECT_FALSE(device->IsPaired()); + + // Pairing dialog should be dismissed + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.dismiss_count_); +} + +TEST_F(BluetoothChromeOSTest, PairingFailsAtConnection) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevices(); + + // Everything seems to go according to plan with the unconnectable device; + // it pairs, but then you can't make connections to it after. + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kUnconnectableDeviceAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(0, pairing_delegate.call_count_); + EXPECT_TRUE(device->IsConnecting()); + + message_loop.Run(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_); + + // Two changes for connecting, one for paired and one for trusted after + // pairing. The device should not be connected. + EXPECT_EQ(4, observer.device_changed_count_); + EXPECT_EQ(device, observer.last_device_); + + EXPECT_FALSE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + + EXPECT_TRUE(device->IsPaired()); + + // Pairing dialog should be dismissed + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.dismiss_count_); + + // Make sure the trusted property has been set to true still (since pairing + // worked). + FakeBluetoothDeviceClient::Properties* properties = + fake_bluetooth_device_client_->GetProperties( + dbus::ObjectPath( + FakeBluetoothDeviceClient::kUnconnectableDevicePath)); + EXPECT_TRUE(properties->trusted.value()); +} + +TEST_F(BluetoothChromeOSTest, PairingRejectedAtPinCode) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevices(); + + // Reject the pairing after we receive a request for the PIN code. + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kSonyHeadphonesAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.request_pincode_count_); + EXPECT_TRUE(device->IsConnecting()); + + // Reject the pairing. + device->RejectPairing(); + message_loop.Run(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, last_connect_error_); + + // Should be no changes except connecting going true and false. + EXPECT_EQ(2, observer.device_changed_count_); + EXPECT_FALSE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + EXPECT_FALSE(device->IsPaired()); + + // Pairing dialog should be dismissed + EXPECT_EQ(2, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.dismiss_count_); +} + +TEST_F(BluetoothChromeOSTest, PairingCancelledAtPinCode) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevices(); + + // Cancel the pairing after we receive a request for the PIN code. + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kSonyHeadphonesAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.request_pincode_count_); + EXPECT_TRUE(device->IsConnecting()); + + // Cancel the pairing. + device->CancelPairing(); + message_loop.Run(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_); + + // Should be no changes except connecting going true and false. + EXPECT_EQ(2, observer.device_changed_count_); + EXPECT_FALSE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + EXPECT_FALSE(device->IsPaired()); + + // Pairing dialog should be dismissed + EXPECT_EQ(2, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.dismiss_count_); +} + +TEST_F(BluetoothChromeOSTest, PairingRejectedAtPasskey) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevices(); + + // Reject the pairing after we receive a request for the passkey. + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kWeirdDeviceAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.request_passkey_count_); + EXPECT_TRUE(device->IsConnecting()); + + // Reject the pairing. + device->RejectPairing(); + message_loop.Run(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, last_connect_error_); + + // Should be no changes except connecting going true and false. + EXPECT_EQ(2, observer.device_changed_count_); + EXPECT_FALSE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + EXPECT_FALSE(device->IsPaired()); + + // Pairing dialog should be dismissed + EXPECT_EQ(2, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.dismiss_count_); +} + +TEST_F(BluetoothChromeOSTest, PairingCancelledAtPasskey) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevices(); + + // Cancel the pairing after we receive a request for the passkey. + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kWeirdDeviceAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.request_passkey_count_); + EXPECT_TRUE(device->IsConnecting()); + + // Cancel the pairing. + device->CancelPairing(); + message_loop.Run(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_); + + // Should be no changes except connecting going true and false. + EXPECT_EQ(2, observer.device_changed_count_); + EXPECT_FALSE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + EXPECT_FALSE(device->IsPaired()); + + // Pairing dialog should be dismissed + EXPECT_EQ(2, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.dismiss_count_); +} + +TEST_F(BluetoothChromeOSTest, PairingRejectedAtConfirmation) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevices(); + + // Reject the pairing after we receive a request for passkey confirmation. + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kPhoneAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_); + EXPECT_TRUE(device->IsConnecting()); + + // Reject the pairing. + device->RejectPairing(); + message_loop.Run(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, last_connect_error_); + + // Should be no changes except connecting going true and false. + EXPECT_EQ(2, observer.device_changed_count_); + EXPECT_FALSE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + EXPECT_FALSE(device->IsPaired()); + + // Pairing dialog should be dismissed + EXPECT_EQ(2, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.dismiss_count_); +} + +TEST_F(BluetoothChromeOSTest, PairingCancelledAtConfirmation) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevices(); + + // Cancel the pairing after we receive a request for the passkey. + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kPhoneAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_); + EXPECT_TRUE(device->IsConnecting()); + + // Cancel the pairing. + device->CancelPairing(); + message_loop.Run(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_); + + // Should be no changes except connecting going true and false. + EXPECT_EQ(2, observer.device_changed_count_); + EXPECT_FALSE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + EXPECT_FALSE(device->IsPaired()); + + // Pairing dialog should be dismissed + EXPECT_EQ(2, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.dismiss_count_); +} + +TEST_F(BluetoothChromeOSTest, PairingCancelledInFlight) { + base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); + fake_bluetooth_device_client_->SetSimulationIntervalMs(10); + + GetAdapter(); + DiscoverDevices(); + + // Cancel the pairing while we're waiting for the remote host. + BluetoothDevice* device = adapter_->GetDevice( + FakeBluetoothDeviceClient::kAppleMouseAddress); + ASSERT_TRUE(device != NULL); + ASSERT_FALSE(device->IsPaired()); + + TestObserver observer(adapter_); + adapter_->AddObserver(&observer); + + TestPairingDelegate pairing_delegate; + device->Connect( + &pairing_delegate, + base::Bind(&BluetoothChromeOSTest::Callback, + base::Unretained(this)), + base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(0, pairing_delegate.call_count_); + EXPECT_TRUE(device->IsConnecting()); + + // Cancel the pairing. + device->CancelPairing(); + message_loop.Run(); + + EXPECT_EQ(0, callback_count_); + EXPECT_EQ(1, error_callback_count_); + EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_); + + // Should be no changes except connecting going true and false. + EXPECT_EQ(2, observer.device_changed_count_); + EXPECT_FALSE(device->IsConnected()); + EXPECT_FALSE(device->IsConnecting()); + EXPECT_FALSE(device->IsPaired()); + + // Pairing dialog should be dismissed + EXPECT_EQ(1, pairing_delegate.call_count_); + EXPECT_EQ(1, pairing_delegate.dismiss_count_); +} + +} // namespace chromeos diff --git a/device/bluetooth/bluetooth_device_chromeos.cc b/device/bluetooth/bluetooth_device_chromeos.cc new file mode 100644 index 0000000..76e2097 --- /dev/null +++ b/device/bluetooth/bluetooth_device_chromeos.cc @@ -0,0 +1,832 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/bluetooth/bluetooth_device_chromeos.h" + +#include "base/bind.h" +#include "base/metrics/histogram.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "chromeos/dbus/bluetooth_adapter_client.h" +#include "chromeos/dbus/bluetooth_agent_manager_client.h" +#include "chromeos/dbus/bluetooth_agent_service_provider.h" +#include "chromeos/dbus/bluetooth_device_client.h" +#include "chromeos/dbus/bluetooth_input_client.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "dbus/bus.h" +#include "device/bluetooth/bluetooth_adapter_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" + +using device::BluetoothDevice; + +namespace { + +// The agent path is relatively meaningless since BlueZ only supports one +// at time and will fail in an attempt to register another with "Already Exists" +// (which we fail in OnRegisterAgentError with ERROR_INPROGRESS). +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 accordinly. + UMA_PAIRING_METHOD_COUNT +}; + +// Histogram enumerations for pairing results. +enum UMAPairingResult { + UMA_PAIRING_RESULT_SUCCESS, + UMA_PAIRING_RESULT_INPROGRESS, + UMA_PAIRING_RESULT_FAILED, + UMA_PAIRING_RESULT_AUTH_FAILED, + UMA_PAIRING_RESULT_AUTH_CANCELED, + UMA_PAIRING_RESULT_AUTH_REJECTED, + UMA_PAIRING_RESULT_AUTH_TIMEOUT, + UMA_PAIRING_RESULT_UNSUPPORTED_DEVICE, + UMA_PAIRING_RESULT_UNKNOWN_ERROR, + // NOTE: Add new pairing results immediately above this line. Make sure to + // update the enum list in tools/histogram/histograms.xml accordinly. + UMA_PAIRING_RESULT_COUNT +}; + +void ParseModalias(const dbus::ObjectPath& object_path, + uint16 *vendor_id, + uint16 *product_id, + uint16 *device_id) { + chromeos::BluetoothDeviceClient::Properties* properties = + chromeos::DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + GetProperties(object_path); + DCHECK(properties); + + std::string modalias = properties->modalias.value(); + if (StartsWithASCII(modalias, "usb:", false) && modalias.length() == 19) { + // usb:vXXXXpXXXXdXXXX + if (modalias[4] == 'v' && vendor_id != NULL) { + uint64 component = 0; + base::HexStringToUInt64(modalias.substr(5, 4), &component); + *vendor_id = component; + } + + if (modalias[9] == 'p' && product_id != NULL) { + uint64 component = 0; + base::HexStringToUInt64(modalias.substr(10, 4), &component); + *product_id = component; + } + + if (modalias[14] == 'd' && device_id != NULL) { + uint64 component = 0; + base::HexStringToUInt64(modalias.substr(15, 4), &component); + *device_id = component; + } + } +} + +void RecordPairingResult(BluetoothDevice::ConnectErrorCode error_code) { + UMAPairingResult pairing_result; + switch (error_code) { + case BluetoothDevice::ERROR_INPROGRESS: + pairing_result = UMA_PAIRING_RESULT_INPROGRESS; + break; + case BluetoothDevice::ERROR_FAILED: + pairing_result = UMA_PAIRING_RESULT_FAILED; + break; + case BluetoothDevice::ERROR_AUTH_FAILED: + pairing_result = UMA_PAIRING_RESULT_AUTH_FAILED; + break; + case BluetoothDevice::ERROR_AUTH_CANCELED: + pairing_result = UMA_PAIRING_RESULT_AUTH_CANCELED; + break; + case BluetoothDevice::ERROR_AUTH_REJECTED: + pairing_result = UMA_PAIRING_RESULT_AUTH_REJECTED; + break; + case BluetoothDevice::ERROR_AUTH_TIMEOUT: + pairing_result = UMA_PAIRING_RESULT_AUTH_TIMEOUT; + break; + case BluetoothDevice::ERROR_UNSUPPORTED_DEVICE: + pairing_result = UMA_PAIRING_RESULT_UNSUPPORTED_DEVICE; + break; + default: + pairing_result = UMA_PAIRING_RESULT_UNKNOWN_ERROR; + } + + UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingResult", + pairing_result, + UMA_PAIRING_RESULT_COUNT); +} + +} // namespace + +namespace chromeos { + +BluetoothDeviceChromeOS::BluetoothDeviceChromeOS( + BluetoothAdapterChromeOS* adapter, + const dbus::ObjectPath& object_path) + : adapter_(adapter), + object_path_(object_path), + num_connecting_calls_(0), + pairing_delegate_(NULL), + pairing_delegate_used_(false), + weak_ptr_factory_(this) { +} + +BluetoothDeviceChromeOS::~BluetoothDeviceChromeOS() { +} + +uint32 BluetoothDeviceChromeOS::GetBluetoothClass() const { + BluetoothDeviceClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + GetProperties(object_path_); + DCHECK(properties); + + return properties->bluetooth_class.value(); +} + +std::string BluetoothDeviceChromeOS::GetDeviceName() const { + BluetoothDeviceClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + GetProperties(object_path_); + DCHECK(properties); + + return properties->alias.value(); +} + +std::string BluetoothDeviceChromeOS::GetAddress() const { + BluetoothDeviceClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + GetProperties(object_path_); + DCHECK(properties); + + return properties->address.value(); +} + +uint16 BluetoothDeviceChromeOS::GetVendorID() const { + uint16 vendor_id = 0; + ParseModalias(object_path_, &vendor_id, NULL, NULL); + return vendor_id; +} + +uint16 BluetoothDeviceChromeOS::GetProductID() const { + uint16 product_id = 0; + ParseModalias(object_path_, NULL, &product_id, NULL); + return product_id; +} + +uint16 BluetoothDeviceChromeOS::GetDeviceID() const { + uint16 device_id = 0; + ParseModalias(object_path_, NULL, NULL, &device_id); + return device_id; +} + +bool BluetoothDeviceChromeOS::IsPaired() const { + BluetoothDeviceClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + GetProperties(object_path_); + DCHECK(properties); + + // Trusted devices are devices that don't support pairing but that the + // user has explicitly connected; it makes no sense for UI purposes to + // treat them differently from each other. + return properties->paired.value() || properties->trusted.value(); +} + +bool BluetoothDeviceChromeOS::IsConnected() const { + BluetoothDeviceClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + GetProperties(object_path_); + DCHECK(properties); + + return properties->connected.value(); +} + +bool BluetoothDeviceChromeOS::IsConnectable() const { + BluetoothInputClient::Properties* input_properties = + DBusThreadManager::Get()->GetBluetoothInputClient()-> + GetProperties(object_path_); + // GetProperties returns NULL when the device does not implement the given + // interface. Non HID devices are normally connectable. + if (!input_properties) + return true; + + return input_properties->reconnect_mode.value() != "device"; +} + +bool BluetoothDeviceChromeOS::IsConnecting() const { + return num_connecting_calls_ > 0; +} + +BluetoothDeviceChromeOS::ServiceList BluetoothDeviceChromeOS::GetServices() + const { + BluetoothDeviceClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + GetProperties(object_path_); + DCHECK(properties); + + return properties->uuids.value(); +} + +void BluetoothDeviceChromeOS::GetServiceRecords( + const ServiceRecordsCallback& callback, + const ErrorCallback& error_callback) { + // TODO(keybuk): not implemented; remove + error_callback.Run(); +} + +void BluetoothDeviceChromeOS::ProvidesServiceWithName( + const std::string& name, + const ProvidesServiceCallback& callback) { + // TODO(keybuk): not implemented; remove + callback.Run(false); +} + +bool BluetoothDeviceChromeOS::ExpectingPinCode() const { + return !pincode_callback_.is_null(); +} + +bool BluetoothDeviceChromeOS::ExpectingPasskey() const { + return !passkey_callback_.is_null(); +} + +bool BluetoothDeviceChromeOS::ExpectingConfirmation() const { + return !confirmation_callback_.is_null(); +} + +void BluetoothDeviceChromeOS::Connect( + BluetoothDevice::PairingDelegate* pairing_delegate, + const base::Closure& callback, + const ConnectErrorCallback& error_callback) { + if (num_connecting_calls_++ == 0) + adapter_->NotifyDeviceChanged(this); + + VLOG(1) << object_path_.value() << ": Connecting, " << num_connecting_calls_ + << " in progress"; + + if (IsPaired() || !pairing_delegate || !IsPairable()) { + // No need to pair, or unable to, skip straight to connection. + ConnectInternal(false, callback, error_callback); + } else { + // Initiate high-security connection with pairing. + DCHECK(!pairing_delegate_); + DCHECK(agent_.get() == NULL); + + pairing_delegate_ = pairing_delegate; + pairing_delegate_used_ = false; + + // The agent path is relatively meaningless since BlueZ only supports + // one per application at a time. + dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus(); + agent_.reset(BluetoothAgentServiceProvider::Create( + system_bus, dbus::ObjectPath(kAgentPath), this)); + DCHECK(agent_.get()); + + VLOG(1) << object_path_.value() << ": Registering agent for pairing"; + DBusThreadManager::Get()->GetBluetoothAgentManagerClient()-> + RegisterAgent( + dbus::ObjectPath(kAgentPath), + bluetooth_agent_manager::kKeyboardDisplayCapability, + base::Bind(&BluetoothDeviceChromeOS::OnRegisterAgent, + weak_ptr_factory_.GetWeakPtr(), + callback, + error_callback), + base::Bind(&BluetoothDeviceChromeOS::OnRegisterAgentError, + weak_ptr_factory_.GetWeakPtr(), + error_callback)); + } +} + +void BluetoothDeviceChromeOS::SetPinCode(const std::string& pincode) { + if (!agent_.get() || pincode_callback_.is_null()) + return; + + pincode_callback_.Run(SUCCESS, pincode); + pincode_callback_.Reset(); +} + +void BluetoothDeviceChromeOS::SetPasskey(uint32 passkey) { + if (!agent_.get() || passkey_callback_.is_null()) + return; + + passkey_callback_.Run(SUCCESS, passkey); + passkey_callback_.Reset(); +} + +void BluetoothDeviceChromeOS::ConfirmPairing() { + if (!agent_.get() || confirmation_callback_.is_null()) + return; + + confirmation_callback_.Run(SUCCESS); + confirmation_callback_.Reset(); +} + +void BluetoothDeviceChromeOS::RejectPairing() { + RunPairingCallbacks(REJECTED); +} + +void BluetoothDeviceChromeOS::CancelPairing() { + // If there wasn't a callback in progress that we can reply to then we + // have to send a CancelPairing() to the device instead. + if (!RunPairingCallbacks(CANCELLED)) { + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + CancelPairing( + object_path_, + base::Bind(&base::DoNothing), + base::Bind(&BluetoothDeviceChromeOS::OnCancelPairingError, + weak_ptr_factory_.GetWeakPtr())); + + // Since there's no calback to this method, it's possible that the pairing + // delegate is going to be freed before things complete. + UnregisterAgent(); + } +} + +void BluetoothDeviceChromeOS::Disconnect(const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << object_path_.value() << ": Disconnecting"; + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + Disconnect( + object_path_, + base::Bind(&BluetoothDeviceChromeOS::OnDisconnect, + weak_ptr_factory_.GetWeakPtr(), + callback), + base::Bind(&BluetoothDeviceChromeOS::OnDisconnectError, + weak_ptr_factory_.GetWeakPtr(), + error_callback)); +} + +void BluetoothDeviceChromeOS::Forget(const ErrorCallback& error_callback) { + VLOG(1) << object_path_.value() << ": Removing device"; + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + RemoveDevice( + adapter_->object_path_, + object_path_, + base::Bind(&base::DoNothing), + base::Bind(&BluetoothDeviceChromeOS::OnForgetError, + weak_ptr_factory_.GetWeakPtr(), + error_callback)); +} + +void BluetoothDeviceChromeOS::ConnectToService( + const std::string& service_uuid, + const SocketCallback& callback) { + // TODO(keybuk): implement + callback.Run(scoped_refptr()); +} + +void BluetoothDeviceChromeOS::ConnectToProfile( + device::BluetoothProfile* profile, + const base::Closure& callback, + const ErrorCallback& error_callback) { + BluetoothProfileChromeOS* profile_chromeos = + static_cast(profile); + VLOG(1) << object_path_.value() << ": Connecting profile: " + << profile_chromeos->uuid(); + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + ConnectProfile( + object_path_, + profile_chromeos->uuid(), + base::Bind( + &BluetoothDeviceChromeOS::OnConnectProfile, + weak_ptr_factory_.GetWeakPtr(), + profile, + callback), + base::Bind( + &BluetoothDeviceChromeOS::OnConnectProfileError, + weak_ptr_factory_.GetWeakPtr(), + profile, + error_callback)); +} + +void BluetoothDeviceChromeOS::SetOutOfBandPairingData( + const device::BluetoothOutOfBandPairingData& data, + const base::Closure& callback, + const ErrorCallback& error_callback) { + // TODO(keybuk): implement + error_callback.Run(); +} + +void BluetoothDeviceChromeOS::ClearOutOfBandPairingData( + const base::Closure& callback, + const ErrorCallback& error_callback) { + // TODO(keybuk): implement + error_callback.Run(); +} + + +void BluetoothDeviceChromeOS::Release() { + DCHECK(agent_.get()); + DCHECK(pairing_delegate_); + VLOG(1) << object_path_.value() << ": Release"; + + pincode_callback_.Reset(); + passkey_callback_.Reset(); + confirmation_callback_.Reset(); + + UnregisterAgent(); +} + +void BluetoothDeviceChromeOS::RequestPinCode( + const dbus::ObjectPath& device_path, + const PinCodeCallback& callback) { + DCHECK(agent_.get()); + DCHECK(device_path == object_path_); + VLOG(1) << object_path_.value() << ": RequestPinCode"; + + UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", + UMA_PAIRING_METHOD_REQUEST_PINCODE, + UMA_PAIRING_METHOD_COUNT); + + DCHECK(pairing_delegate_); + DCHECK(pincode_callback_.is_null()); + pincode_callback_ = callback; + pairing_delegate_->RequestPinCode(this); + pairing_delegate_used_ = true; +} + +void BluetoothDeviceChromeOS::DisplayPinCode( + const dbus::ObjectPath& device_path, + const std::string& pincode) { + DCHECK(agent_.get()); + DCHECK(device_path == object_path_); + VLOG(1) << object_path_.value() << ": DisplayPinCode: " << pincode; + + UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", + UMA_PAIRING_METHOD_DISPLAY_PINCODE, + UMA_PAIRING_METHOD_COUNT); + + DCHECK(pairing_delegate_); + pairing_delegate_->DisplayPinCode(this, pincode); + pairing_delegate_used_ = true; +} + +void BluetoothDeviceChromeOS::RequestPasskey( + const dbus::ObjectPath& device_path, + const PasskeyCallback& callback) { + DCHECK(agent_.get()); + DCHECK(device_path == object_path_); + VLOG(1) << object_path_.value() << ": RequestPasskey"; + + UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", + UMA_PAIRING_METHOD_REQUEST_PASSKEY, + UMA_PAIRING_METHOD_COUNT); + + DCHECK(pairing_delegate_); + DCHECK(passkey_callback_.is_null()); + passkey_callback_ = callback; + pairing_delegate_->RequestPasskey(this); + pairing_delegate_used_ = true; +} + +void BluetoothDeviceChromeOS::DisplayPasskey( + const dbus::ObjectPath& device_path, + uint32 passkey, + uint16 entered) { + DCHECK(agent_.get()); + DCHECK(device_path == object_path_); + VLOG(1) << object_path_.value() << ": DisplayPasskey: " << passkey + << " (" << entered << " entered)"; + + if (entered == 0) + UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", + UMA_PAIRING_METHOD_DISPLAY_PASSKEY, + UMA_PAIRING_METHOD_COUNT); + + DCHECK(pairing_delegate_); + if (entered == 0) + pairing_delegate_->DisplayPasskey(this, passkey); + pairing_delegate_->KeysEntered(this, entered); + pairing_delegate_used_ = true; +} + +void BluetoothDeviceChromeOS::RequestConfirmation( + const dbus::ObjectPath& device_path, + uint32 passkey, + const ConfirmationCallback& callback) { + DCHECK(agent_.get()); + DCHECK(device_path == object_path_); + VLOG(1) << object_path_.value() << ": RequestConfirmation: " << passkey; + + UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", + UMA_PAIRING_METHOD_CONFIRM_PASSKEY, + UMA_PAIRING_METHOD_COUNT); + + DCHECK(pairing_delegate_); + DCHECK(confirmation_callback_.is_null()); + confirmation_callback_ = callback; + pairing_delegate_->ConfirmPasskey(this, passkey); + pairing_delegate_used_ = true; +} + +void BluetoothDeviceChromeOS::RequestAuthorization( + const dbus::ObjectPath& device_path, + const ConfirmationCallback& callback) { + // TODO(keybuk): implement + callback.Run(CANCELLED); +} + +void BluetoothDeviceChromeOS::AuthorizeService( + const dbus::ObjectPath& device_path, + const std::string& uuid, + const ConfirmationCallback& callback) { + // TODO(keybuk): implement + callback.Run(CANCELLED); +} + +void BluetoothDeviceChromeOS::Cancel() { + DCHECK(agent_.get()); + VLOG(1) << object_path_.value() << ": Cancel"; + + DCHECK(pairing_delegate_); + pairing_delegate_->DismissDisplayOrConfirm(); +} + +void BluetoothDeviceChromeOS::ConnectInternal( + bool after_pairing, + const base::Closure& callback, + const ConnectErrorCallback& error_callback) { + VLOG(1) << object_path_.value() << ": Connecting"; + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + Connect( + object_path_, + base::Bind(&BluetoothDeviceChromeOS::OnConnect, + weak_ptr_factory_.GetWeakPtr(), + after_pairing, + callback), + base::Bind(&BluetoothDeviceChromeOS::OnConnectError, + weak_ptr_factory_.GetWeakPtr(), + after_pairing, + error_callback)); +} + +void BluetoothDeviceChromeOS::OnConnect(bool after_pairing, + const base::Closure& callback) { + if (--num_connecting_calls_ == 0) + adapter_->NotifyDeviceChanged(this); + + DCHECK(num_connecting_calls_ >= 0); + VLOG(1) << object_path_.value() << ": Connected, " << num_connecting_calls_ + << " still in progress"; + + SetTrusted(); + + if (after_pairing) + UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingResult", + UMA_PAIRING_RESULT_SUCCESS, + UMA_PAIRING_RESULT_COUNT); + + callback.Run(); +} + +void BluetoothDeviceChromeOS::OnConnectError( + bool after_pairing, + const ConnectErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message) { + if (--num_connecting_calls_ == 0) + adapter_->NotifyDeviceChanged(this); + + DCHECK(num_connecting_calls_ >= 0); + LOG(WARNING) << object_path_.value() << ": Failed to connect device: " + << error_name << ": " << error_message; + VLOG(1) << object_path_.value() << ": " << num_connecting_calls_ + << " still in progress"; + + // Determine the error code from error_name. + ConnectErrorCode error_code = ERROR_UNKNOWN; + if (error_name == bluetooth_device::kErrorFailed) { + error_code = ERROR_FAILED; + } else if (error_name == bluetooth_device::kErrorInProgress) { + error_code = ERROR_INPROGRESS; + } else if (error_name == bluetooth_device::kErrorNotSupported) { + error_code = ERROR_UNSUPPORTED_DEVICE; + } + + if (after_pairing) + RecordPairingResult(error_code); + error_callback.Run(error_code); +} + +void BluetoothDeviceChromeOS::OnRegisterAgent( + const base::Closure& callback, + const ConnectErrorCallback& error_callback) { + VLOG(1) << object_path_.value() << ": Agent registered, now pairing"; + + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + Pair(object_path_, + base::Bind(&BluetoothDeviceChromeOS::OnPair, + weak_ptr_factory_.GetWeakPtr(), + callback, error_callback), + base::Bind(&BluetoothDeviceChromeOS::OnPairError, + weak_ptr_factory_.GetWeakPtr(), + error_callback)); +} + +void BluetoothDeviceChromeOS::OnRegisterAgentError( + const ConnectErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message) { + if (--num_connecting_calls_ == 0) + adapter_->NotifyDeviceChanged(this); + + DCHECK(num_connecting_calls_ >= 0); + LOG(WARNING) << object_path_.value() << ": Failed to register agent: " + << error_name << ": " << error_message; + VLOG(1) << object_path_.value() << ": " << num_connecting_calls_ + << " still in progress"; + + UnregisterAgent(); + + // Determine the error code from error_name. + ConnectErrorCode error_code = ERROR_UNKNOWN; + if (error_name == bluetooth_agent_manager::kErrorAlreadyExists) + error_code = ERROR_INPROGRESS; + + RecordPairingResult(error_code); + error_callback.Run(error_code); +} + +void BluetoothDeviceChromeOS::OnPair( + const base::Closure& callback, + const ConnectErrorCallback& error_callback) { + VLOG(1) << object_path_.value() << ": Paired"; + + if (!pairing_delegate_used_) + UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", + UMA_PAIRING_METHOD_NONE, + UMA_PAIRING_METHOD_COUNT); + UnregisterAgent(); + SetTrusted(); + ConnectInternal(true, callback, error_callback); +} + +void BluetoothDeviceChromeOS::OnPairError( + const ConnectErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message) { + if (--num_connecting_calls_ == 0) + adapter_->NotifyDeviceChanged(this); + + DCHECK(num_connecting_calls_ >= 0); + LOG(WARNING) << object_path_.value() << ": Failed to pair device: " + << error_name << ": " << error_message; + VLOG(1) << object_path_.value() << ": " << num_connecting_calls_ + << " still in progress"; + + UnregisterAgent(); + + // Determine the error code from error_name. + ConnectErrorCode error_code = ERROR_UNKNOWN; + if (error_name == bluetooth_device::kErrorConnectionAttemptFailed) { + error_code = ERROR_FAILED; + } else if (error_name == bluetooth_device::kErrorFailed) { + error_code = ERROR_FAILED; + } else if (error_name == bluetooth_device::kErrorAuthenticationFailed) { + error_code = ERROR_AUTH_FAILED; + } else if (error_name == bluetooth_device::kErrorAuthenticationCanceled) { + error_code = ERROR_AUTH_CANCELED; + } else if (error_name == bluetooth_device::kErrorAuthenticationRejected) { + error_code = ERROR_AUTH_REJECTED; + } else if (error_name == bluetooth_device::kErrorAuthenticationTimeout) { + error_code = ERROR_AUTH_TIMEOUT; + } + + RecordPairingResult(error_code); + error_callback.Run(error_code); +} + +void BluetoothDeviceChromeOS::OnCancelPairingError( + const std::string& error_name, + const std::string& error_message) { + LOG(WARNING) << object_path_.value() << ": Failed to cancel pairing: " + << error_name << ": " << error_message; +} + +void BluetoothDeviceChromeOS::SetTrusted() { + // Unconditionally send the property change, rather than checking the value + // first; there's no harm in doing this and it solves any race conditions + // with the property becoming true or false and this call happening before + // we get the D-Bus signal about the earlier change. + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + GetProperties(object_path_)->trusted.Set( + true, + base::Bind(&BluetoothDeviceChromeOS::OnSetTrusted, + weak_ptr_factory_.GetWeakPtr())); +} + +void BluetoothDeviceChromeOS::OnSetTrusted(bool success) { + LOG_IF(WARNING, !success) << object_path_.value() + << ": Failed to set device as trusted"; +} + +void BluetoothDeviceChromeOS::UnregisterAgent() { + if (!agent_.get()) + return; + + DCHECK(pairing_delegate_); + + DCHECK(pincode_callback_.is_null()); + DCHECK(passkey_callback_.is_null()); + DCHECK(confirmation_callback_.is_null()); + + pairing_delegate_->DismissDisplayOrConfirm(); + pairing_delegate_ = NULL; + + agent_.reset(); + + // Clean up after ourselves. + VLOG(1) << object_path_.value() << ": Unregistering pairing agent"; + DBusThreadManager::Get()->GetBluetoothAgentManagerClient()-> + UnregisterAgent( + dbus::ObjectPath(kAgentPath), + base::Bind(&base::DoNothing), + base::Bind(&BluetoothDeviceChromeOS::OnUnregisterAgentError, + weak_ptr_factory_.GetWeakPtr())); +} + +void BluetoothDeviceChromeOS::OnUnregisterAgentError( + const std::string& error_name, + const std::string& error_message) { + LOG(WARNING) << object_path_.value() << ": Failed to unregister agent: " + << error_name << ": " << error_message; +} + +void BluetoothDeviceChromeOS::OnDisconnect(const base::Closure& callback) { + VLOG(1) << object_path_.value() << ": Disconnected"; + callback.Run(); +} + +void BluetoothDeviceChromeOS::OnDisconnectError( + const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message) { + LOG(WARNING) << object_path_.value() << ": Failed to disconnect device: " + << error_name << ": " << error_message; + error_callback.Run(); +} + +void BluetoothDeviceChromeOS::OnForgetError( + const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message) { + LOG(WARNING) << object_path_.value() << ": Failed to remove device: " + << error_name << ": " << error_message; + error_callback.Run(); +} + +bool BluetoothDeviceChromeOS::RunPairingCallbacks(Status status) { + if (!agent_.get()) + return false; + + 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; + } + + return callback_run; +} + +void BluetoothDeviceChromeOS::OnConnectProfile( + device::BluetoothProfile* profile, + const base::Closure& callback) { + BluetoothProfileChromeOS* profile_chromeos = + static_cast(profile); + VLOG(1) << object_path_.value() << ": Profile connected: " + << profile_chromeos->uuid(); + callback.Run(); +} + +void BluetoothDeviceChromeOS::OnConnectProfileError( + device::BluetoothProfile* profile, + const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message) { + BluetoothProfileChromeOS* profile_chromeos = + static_cast(profile); + VLOG(1) << object_path_.value() << ": Profile connection failed: " + << profile_chromeos->uuid() << ": " + << error_name << ": " << error_message; + error_callback.Run(); +} + +} // namespace chromeos diff --git a/device/bluetooth/bluetooth_device_chromeos.h b/device/bluetooth/bluetooth_device_chromeos.h new file mode 100644 index 0000000..fef7cfd --- /dev/null +++ b/device/bluetooth/bluetooth_device_chromeos.h @@ -0,0 +1,223 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_CHROMEOS_H +#define DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_CHROMEOS_H + +#include + +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "chromeos/dbus/bluetooth_agent_service_provider.h" +#include "chromeos/dbus/bluetooth_device_client.h" +#include "dbus/object_path.h" +#include "device/bluetooth/bluetooth_device.h" + +namespace chromeos { + +class BluetoothAdapterChromeOS; + +// The BluetoothDeviceChromeOS class implements BluetoothDevice for the +// Chrome OS platform. +class BluetoothDeviceChromeOS + : public device::BluetoothDevice, + private chromeos::BluetoothAgentServiceProvider::Delegate { + public: + // BluetoothDevice override + virtual uint32 GetBluetoothClass() const OVERRIDE; + virtual std::string GetAddress() const OVERRIDE; + virtual uint16 GetVendorID() const OVERRIDE; + virtual uint16 GetProductID() const OVERRIDE; + virtual uint16 GetDeviceID() const OVERRIDE; + virtual bool IsPaired() const OVERRIDE; + virtual bool IsConnected() const OVERRIDE; + virtual bool IsConnectable() const OVERRIDE; + virtual bool IsConnecting() const OVERRIDE; + virtual ServiceList GetServices() const OVERRIDE; + virtual void GetServiceRecords( + const ServiceRecordsCallback& callback, + const ErrorCallback& error_callback) OVERRIDE; + virtual void ProvidesServiceWithName( + const std::string& name, + const ProvidesServiceCallback& callback) OVERRIDE; + virtual bool ExpectingPinCode() const OVERRIDE; + virtual bool ExpectingPasskey() const OVERRIDE; + virtual bool ExpectingConfirmation() const OVERRIDE; + virtual void Connect( + device::BluetoothDevice::PairingDelegate* pairing_delegate, + const base::Closure& callback, + const ConnectErrorCallback& error_callback) OVERRIDE; + virtual void SetPinCode(const std::string& pincode) OVERRIDE; + virtual void SetPasskey(uint32 passkey) OVERRIDE; + virtual void ConfirmPairing() OVERRIDE; + virtual void RejectPairing() OVERRIDE; + virtual void CancelPairing() OVERRIDE; + virtual void Disconnect( + const base::Closure& callback, + const ErrorCallback& error_callback) OVERRIDE; + virtual void Forget(const ErrorCallback& error_callback) OVERRIDE; + virtual void ConnectToService( + const std::string& service_uuid, + const SocketCallback& callback) OVERRIDE; + virtual void ConnectToProfile( + device::BluetoothProfile* profile, + const base::Closure& callback, + const ErrorCallback& error_callback) OVERRIDE; + virtual void SetOutOfBandPairingData( + const device::BluetoothOutOfBandPairingData& data, + const base::Closure& callback, + const ErrorCallback& error_callback) OVERRIDE; + virtual void ClearOutOfBandPairingData( + const base::Closure& callback, + const ErrorCallback& error_callback) OVERRIDE; + + protected: + // BluetoothDevice override + virtual std::string GetDeviceName() const OVERRIDE; + + private: + friend class BluetoothAdapterChromeOS; + + BluetoothDeviceChromeOS(BluetoothAdapterChromeOS* adapter, + const dbus::ObjectPath& object_path); + virtual ~BluetoothDeviceChromeOS(); + + // BluetoothAgentServiceProvider::Delegate override. + virtual void Release() OVERRIDE; + virtual void RequestPinCode(const dbus::ObjectPath& device_path, + const PinCodeCallback& callback) OVERRIDE; + virtual void DisplayPinCode(const dbus::ObjectPath& device_path, + const std::string& pincode) OVERRIDE; + virtual void RequestPasskey(const dbus::ObjectPath& device_path, + const PasskeyCallback& callback) OVERRIDE; + virtual void DisplayPasskey(const dbus::ObjectPath& device_path, + uint32 passkey, uint16 entered) OVERRIDE; + virtual void RequestConfirmation(const dbus::ObjectPath& device_path, + uint32 passkey, + const ConfirmationCallback& callback) + OVERRIDE; + virtual void RequestAuthorization(const dbus::ObjectPath& device_path, + const ConfirmationCallback& callback) + OVERRIDE; + virtual void AuthorizeService(const dbus::ObjectPath& device_path, + const std::string& uuid, + const ConfirmationCallback& callback) OVERRIDE; + virtual void Cancel() OVERRIDE; + + // Internal method to initiate a connection to this device, and methods called + // by dbus:: on completion of the D-Bus method call. + void ConnectInternal(bool after_pairing, + const base::Closure& callback, + const ConnectErrorCallback& error_callback); + void OnConnect(bool after_pairing, + const base::Closure& callback); + void OnConnectError(bool after_pairing, + const ConnectErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message); + + // Called by dbus:: on completion of the D-Bus method call to register the + // pairing agent. + void OnRegisterAgent(const base::Closure& callback, + const ConnectErrorCallback& error_callback); + void OnRegisterAgentError(const ConnectErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message); + + // Called by dbus:: on completion of the D-Bus method call to pair the device. + void OnPair(const base::Closure& callback, + const ConnectErrorCallback& error_callback); + void OnPairError(const ConnectErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message); + + // Called by dbus:: on failure of the D-Bus method call to cancel pairing, + // there is no matching completion call since we don't do anything special + // in that case. + void OnCancelPairingError(const std::string& error_name, + const std::string& error_message); + + // Internal method to set the device as trusted. Trusted devices can connect + // to us automatically, and we can connect to them after rebooting; it also + // causes the device to be remembered by the stack even if not paired. + // |success| to the callback indicates whether or not the request succeeded. + void SetTrusted(); + void OnSetTrusted(bool success); + + // Internal method to unregister the pairing agent and method called by dbus:: + // on failure of the D-Bus method call. No completion call as success is + // ignored. + void UnregisterAgent(); + void OnUnregisterAgentError(const std::string& error_name, + const std::string& error_message); + + // Called by dbus:: on completion of the D-Bus method call to disconnect the + // device. + void OnDisconnect(const base::Closure& callback); + void OnDisconnectError(const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message); + + // Called by dbus:: on failure of the D-Bus method call to unpair the device; + // there is no matching completion call since this object is deleted in the + // process of unpairing. + void OnForgetError(const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message); + + // Run any outstanding pairing callbacks passing |status| as the result of + // pairing. Returns true if any callbacks were run, false if not. + bool RunPairingCallbacks(Status status); + + // Called by dbus:: on completion of the D-Bus method call to + // connect a peofile. + void OnConnectProfile(device::BluetoothProfile* profile, + const base::Closure& callback); + void OnConnectProfileError(device::BluetoothProfile* profile, + const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message); + + // Return 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. + BluetoothAdapterChromeOS* adapter_; + + // The dbus object path of the device object. + dbus::ObjectPath object_path_; + + // Number of ongoing calls to Connect(). + int num_connecting_calls_; + + // 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_; + + // Flag to indicate whether a pairing delegate method has been called during + // pairing. + bool pairing_delegate_used_; + + // During pairing this is set to an instance of a D-Bus agent object + // intialized with our own class as its delegate. + scoped_ptr agent_; + + // During pairing these callbacks are set to those provided by method calls + // made on us by |agent_| and are called by our own method calls such as + // SetPinCode() and SetPasskey(). + PinCodeCallback pincode_callback_; + PasskeyCallback passkey_callback_; + ConfirmationCallback confirmation_callback_; + + // Note: This should remain the last member so it'll be destroyed and + // invalidate its weak pointers before any other members are destroyed. + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothDeviceChromeOS); +}; + +} // namespace chromeos + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_CHROMEOS_H diff --git a/device/bluetooth/bluetooth_device_experimental_chromeos.cc b/device/bluetooth/bluetooth_device_experimental_chromeos.cc deleted file mode 100644 index b6932ba..0000000 --- a/device/bluetooth/bluetooth_device_experimental_chromeos.cc +++ /dev/null @@ -1,849 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "device/bluetooth/bluetooth_device_experimental_chromeos.h" - -#include "base/bind.h" -#include "base/metrics/histogram.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "chromeos/dbus/dbus_thread_manager.h" -#include "chromeos/dbus/experimental_bluetooth_adapter_client.h" -#include "chromeos/dbus/experimental_bluetooth_agent_manager_client.h" -#include "chromeos/dbus/experimental_bluetooth_agent_service_provider.h" -#include "chromeos/dbus/experimental_bluetooth_device_client.h" -#include "chromeos/dbus/experimental_bluetooth_input_client.h" -#include "dbus/bus.h" -#include "device/bluetooth/bluetooth_adapter_experimental_chromeos.h" -#include "device/bluetooth/bluetooth_profile_experimental_chromeos.h" -#include "device/bluetooth/bluetooth_socket.h" -#include "third_party/cros_system_api/dbus/service_constants.h" - -using device::BluetoothDevice; - -namespace { - -// The agent path is relatively meaningless since BlueZ only supports one -// at time and will fail in an attempt to register another with "Already Exists" -// (which we fail in OnRegisterAgentError with ERROR_INPROGRESS). -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 accordinly. - UMA_PAIRING_METHOD_COUNT -}; - -// Histogram enumerations for pairing results. -enum UMAPairingResult { - UMA_PAIRING_RESULT_SUCCESS, - UMA_PAIRING_RESULT_INPROGRESS, - UMA_PAIRING_RESULT_FAILED, - UMA_PAIRING_RESULT_AUTH_FAILED, - UMA_PAIRING_RESULT_AUTH_CANCELED, - UMA_PAIRING_RESULT_AUTH_REJECTED, - UMA_PAIRING_RESULT_AUTH_TIMEOUT, - UMA_PAIRING_RESULT_UNSUPPORTED_DEVICE, - UMA_PAIRING_RESULT_UNKNOWN_ERROR, - // NOTE: Add new pairing results immediately above this line. Make sure to - // update the enum list in tools/histogram/histograms.xml accordinly. - UMA_PAIRING_RESULT_COUNT -}; - -void ParseModalias(const dbus::ObjectPath& object_path, - uint16 *vendor_id, - uint16 *product_id, - uint16 *device_id) { - chromeos::ExperimentalBluetoothDeviceClient::Properties* properties = - chromeos::DBusThreadManager::Get()-> - GetExperimentalBluetoothDeviceClient()->GetProperties(object_path); - DCHECK(properties); - - std::string modalias = properties->modalias.value(); - if (StartsWithASCII(modalias, "usb:", false) && modalias.length() == 19) { - // usb:vXXXXpXXXXdXXXX - if (modalias[4] == 'v' && vendor_id != NULL) { - uint64 component = 0; - base::HexStringToUInt64(modalias.substr(5, 4), &component); - *vendor_id = component; - } - - if (modalias[9] == 'p' && product_id != NULL) { - uint64 component = 0; - base::HexStringToUInt64(modalias.substr(10, 4), &component); - *product_id = component; - } - - if (modalias[14] == 'd' && device_id != NULL) { - uint64 component = 0; - base::HexStringToUInt64(modalias.substr(15, 4), &component); - *device_id = component; - } - } -} - -void RecordPairingResult(BluetoothDevice::ConnectErrorCode error_code) { - UMAPairingResult pairing_result; - switch (error_code) { - case BluetoothDevice::ERROR_INPROGRESS: - pairing_result = UMA_PAIRING_RESULT_INPROGRESS; - break; - case BluetoothDevice::ERROR_FAILED: - pairing_result = UMA_PAIRING_RESULT_FAILED; - break; - case BluetoothDevice::ERROR_AUTH_FAILED: - pairing_result = UMA_PAIRING_RESULT_AUTH_FAILED; - break; - case BluetoothDevice::ERROR_AUTH_CANCELED: - pairing_result = UMA_PAIRING_RESULT_AUTH_CANCELED; - break; - case BluetoothDevice::ERROR_AUTH_REJECTED: - pairing_result = UMA_PAIRING_RESULT_AUTH_REJECTED; - break; - case BluetoothDevice::ERROR_AUTH_TIMEOUT: - pairing_result = UMA_PAIRING_RESULT_AUTH_TIMEOUT; - break; - case BluetoothDevice::ERROR_UNSUPPORTED_DEVICE: - pairing_result = UMA_PAIRING_RESULT_UNSUPPORTED_DEVICE; - break; - default: - pairing_result = UMA_PAIRING_RESULT_UNKNOWN_ERROR; - } - - UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingResult", - pairing_result, - UMA_PAIRING_RESULT_COUNT); -} - -} // namespace - -namespace chromeos { - -BluetoothDeviceExperimentalChromeOS::BluetoothDeviceExperimentalChromeOS( - BluetoothAdapterExperimentalChromeOS* adapter, - const dbus::ObjectPath& object_path) - : adapter_(adapter), - object_path_(object_path), - num_connecting_calls_(0), - pairing_delegate_(NULL), - pairing_delegate_used_(false), - weak_ptr_factory_(this) { -} - -BluetoothDeviceExperimentalChromeOS::~BluetoothDeviceExperimentalChromeOS() { -} - -uint32 BluetoothDeviceExperimentalChromeOS::GetBluetoothClass() const { - ExperimentalBluetoothDeviceClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - GetProperties(object_path_); - DCHECK(properties); - - return properties->bluetooth_class.value(); -} - -std::string BluetoothDeviceExperimentalChromeOS::GetDeviceName() const { - ExperimentalBluetoothDeviceClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - GetProperties(object_path_); - DCHECK(properties); - - return properties->alias.value(); -} - -std::string BluetoothDeviceExperimentalChromeOS::GetAddress() const { - ExperimentalBluetoothDeviceClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - GetProperties(object_path_); - DCHECK(properties); - - return properties->address.value(); -} - -uint16 BluetoothDeviceExperimentalChromeOS::GetVendorID() const { - uint16 vendor_id = 0; - ParseModalias(object_path_, &vendor_id, NULL, NULL); - return vendor_id; -} - -uint16 BluetoothDeviceExperimentalChromeOS::GetProductID() const { - uint16 product_id = 0; - ParseModalias(object_path_, NULL, &product_id, NULL); - return product_id; -} - -uint16 BluetoothDeviceExperimentalChromeOS::GetDeviceID() const { - uint16 device_id = 0; - ParseModalias(object_path_, NULL, NULL, &device_id); - return device_id; -} - -bool BluetoothDeviceExperimentalChromeOS::IsPaired() const { - ExperimentalBluetoothDeviceClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - GetProperties(object_path_); - DCHECK(properties); - - // Trusted devices are devices that don't support pairing but that the - // user has explicitly connected; it makes no sense for UI purposes to - // treat them differently from each other. - return properties->paired.value() || properties->trusted.value(); -} - -bool BluetoothDeviceExperimentalChromeOS::IsConnected() const { - ExperimentalBluetoothDeviceClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - GetProperties(object_path_); - DCHECK(properties); - - return properties->connected.value(); -} - -bool BluetoothDeviceExperimentalChromeOS::IsConnectable() const { - ExperimentalBluetoothInputClient::Properties* input_properties = - DBusThreadManager::Get()->GetExperimentalBluetoothInputClient()-> - GetProperties(object_path_); - // GetProperties returns NULL when the device does not implement the given - // interface. Non HID devices are normally connectable. - if (!input_properties) - return true; - - return input_properties->reconnect_mode.value() != "device"; -} - -bool BluetoothDeviceExperimentalChromeOS::IsConnecting() const { - return num_connecting_calls_ > 0; -} - -BluetoothDeviceExperimentalChromeOS::ServiceList -BluetoothDeviceExperimentalChromeOS::GetServices() const { - ExperimentalBluetoothDeviceClient::Properties* properties = - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - GetProperties(object_path_); - DCHECK(properties); - - return properties->uuids.value(); -} - -void BluetoothDeviceExperimentalChromeOS::GetServiceRecords( - const ServiceRecordsCallback& callback, - const ErrorCallback& error_callback) { - // TODO(keybuk): not implemented; remove - error_callback.Run(); -} - -void BluetoothDeviceExperimentalChromeOS::ProvidesServiceWithName( - const std::string& name, - const ProvidesServiceCallback& callback) { - // TODO(keybuk): not implemented; remove - callback.Run(false); -} - -bool BluetoothDeviceExperimentalChromeOS::ExpectingPinCode() const { - return !pincode_callback_.is_null(); -} - -bool BluetoothDeviceExperimentalChromeOS::ExpectingPasskey() const { - return !passkey_callback_.is_null(); -} - -bool BluetoothDeviceExperimentalChromeOS::ExpectingConfirmation() const { - return !confirmation_callback_.is_null(); -} - -void BluetoothDeviceExperimentalChromeOS::Connect( - BluetoothDevice::PairingDelegate* pairing_delegate, - const base::Closure& callback, - const ConnectErrorCallback& error_callback) { - if (num_connecting_calls_++ == 0) - adapter_->NotifyDeviceChanged(this); - - VLOG(1) << object_path_.value() << ": Connecting, " << num_connecting_calls_ - << " in progress"; - - if (IsPaired() || !pairing_delegate || !IsPairable()) { - // No need to pair, or unable to, skip straight to connection. - ConnectInternal(false, callback, error_callback); - } else { - // Initiate high-security connection with pairing. - DCHECK(!pairing_delegate_); - DCHECK(agent_.get() == NULL); - - pairing_delegate_ = pairing_delegate; - pairing_delegate_used_ = false; - - // The agent path is relatively meaningless since BlueZ only supports - // one per application at a time. - dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus(); - agent_.reset(ExperimentalBluetoothAgentServiceProvider::Create( - system_bus, dbus::ObjectPath(kAgentPath), this)); - DCHECK(agent_.get()); - - VLOG(1) << object_path_.value() << ": Registering agent for pairing"; - DBusThreadManager::Get()->GetExperimentalBluetoothAgentManagerClient()-> - RegisterAgent( - dbus::ObjectPath(kAgentPath), - bluetooth_agent_manager::kKeyboardDisplayCapability, - base::Bind( - &BluetoothDeviceExperimentalChromeOS::OnRegisterAgent, - weak_ptr_factory_.GetWeakPtr(), - callback, - error_callback), - base::Bind( - &BluetoothDeviceExperimentalChromeOS::OnRegisterAgentError, - weak_ptr_factory_.GetWeakPtr(), - error_callback)); - } -} - -void BluetoothDeviceExperimentalChromeOS::SetPinCode( - const std::string& pincode) { - if (!agent_.get() || pincode_callback_.is_null()) - return; - - pincode_callback_.Run(SUCCESS, pincode); - pincode_callback_.Reset(); -} - -void BluetoothDeviceExperimentalChromeOS::SetPasskey(uint32 passkey) { - if (!agent_.get() || passkey_callback_.is_null()) - return; - - passkey_callback_.Run(SUCCESS, passkey); - passkey_callback_.Reset(); -} - -void BluetoothDeviceExperimentalChromeOS::ConfirmPairing() { - if (!agent_.get() || confirmation_callback_.is_null()) - return; - - confirmation_callback_.Run(SUCCESS); - confirmation_callback_.Reset(); -} - -void BluetoothDeviceExperimentalChromeOS::RejectPairing() { - RunPairingCallbacks(REJECTED); -} - -void BluetoothDeviceExperimentalChromeOS::CancelPairing() { - // If there wasn't a callback in progress that we can reply to then we - // have to send a CancelPairing() to the device instead. - if (!RunPairingCallbacks(CANCELLED)) { - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - CancelPairing( - object_path_, - base::Bind(&base::DoNothing), - base::Bind( - &BluetoothDeviceExperimentalChromeOS::OnCancelPairingError, - weak_ptr_factory_.GetWeakPtr())); - - // Since there's no calback to this method, it's possible that the pairing - // delegate is going to be freed before things complete. - UnregisterAgent(); - } -} - -void BluetoothDeviceExperimentalChromeOS::Disconnect( - const base::Closure& callback, - const ErrorCallback& error_callback) { - VLOG(1) << object_path_.value() << ": Disconnecting"; - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - Disconnect( - object_path_, - base::Bind( - &BluetoothDeviceExperimentalChromeOS::OnDisconnect, - weak_ptr_factory_.GetWeakPtr(), - callback), - base::Bind( - &BluetoothDeviceExperimentalChromeOS::OnDisconnectError, - weak_ptr_factory_.GetWeakPtr(), - error_callback)); -} - -void BluetoothDeviceExperimentalChromeOS::Forget( - const ErrorCallback& error_callback) { - VLOG(1) << object_path_.value() << ": Removing device"; - DBusThreadManager::Get()->GetExperimentalBluetoothAdapterClient()-> - RemoveDevice( - adapter_->object_path_, - object_path_, - base::Bind(&base::DoNothing), - base::Bind( - &BluetoothDeviceExperimentalChromeOS::OnForgetError, - weak_ptr_factory_.GetWeakPtr(), - error_callback)); -} - -void BluetoothDeviceExperimentalChromeOS::ConnectToService( - const std::string& service_uuid, - const SocketCallback& callback) { - // TODO(keybuk): implement - callback.Run(scoped_refptr()); -} - -void BluetoothDeviceExperimentalChromeOS::ConnectToProfile( - device::BluetoothProfile* profile, - const base::Closure& callback, - const ErrorCallback& error_callback) { - BluetoothProfileExperimentalChromeOS* profile_chromeos = - static_cast(profile); - VLOG(1) << object_path_.value() << ": Connecting profile: " - << profile_chromeos->uuid(); - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - ConnectProfile( - object_path_, - profile_chromeos->uuid(), - base::Bind( - &BluetoothDeviceExperimentalChromeOS::OnConnectProfile, - weak_ptr_factory_.GetWeakPtr(), - profile, - callback), - base::Bind( - &BluetoothDeviceExperimentalChromeOS::OnConnectProfileError, - weak_ptr_factory_.GetWeakPtr(), - profile, - error_callback)); -} - -void BluetoothDeviceExperimentalChromeOS::SetOutOfBandPairingData( - const device::BluetoothOutOfBandPairingData& data, - const base::Closure& callback, - const ErrorCallback& error_callback) { - // TODO(keybuk): implement - error_callback.Run(); -} - -void BluetoothDeviceExperimentalChromeOS::ClearOutOfBandPairingData( - const base::Closure& callback, - const ErrorCallback& error_callback) { - // TODO(keybuk): implement - error_callback.Run(); -} - - -void BluetoothDeviceExperimentalChromeOS::Release() { - DCHECK(agent_.get()); - DCHECK(pairing_delegate_); - VLOG(1) << object_path_.value() << ": Release"; - - pincode_callback_.Reset(); - passkey_callback_.Reset(); - confirmation_callback_.Reset(); - - UnregisterAgent(); -} - -void BluetoothDeviceExperimentalChromeOS::RequestPinCode( - const dbus::ObjectPath& device_path, - const PinCodeCallback& callback) { - DCHECK(agent_.get()); - DCHECK(device_path == object_path_); - VLOG(1) << object_path_.value() << ": RequestPinCode"; - - UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", - UMA_PAIRING_METHOD_REQUEST_PINCODE, - UMA_PAIRING_METHOD_COUNT); - - DCHECK(pairing_delegate_); - DCHECK(pincode_callback_.is_null()); - pincode_callback_ = callback; - pairing_delegate_->RequestPinCode(this); - pairing_delegate_used_ = true; -} - -void BluetoothDeviceExperimentalChromeOS::DisplayPinCode( - const dbus::ObjectPath& device_path, - const std::string& pincode) { - DCHECK(agent_.get()); - DCHECK(device_path == object_path_); - VLOG(1) << object_path_.value() << ": DisplayPinCode: " << pincode; - - UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", - UMA_PAIRING_METHOD_DISPLAY_PINCODE, - UMA_PAIRING_METHOD_COUNT); - - DCHECK(pairing_delegate_); - pairing_delegate_->DisplayPinCode(this, pincode); - pairing_delegate_used_ = true; -} - -void BluetoothDeviceExperimentalChromeOS::RequestPasskey( - const dbus::ObjectPath& device_path, - const PasskeyCallback& callback) { - DCHECK(agent_.get()); - DCHECK(device_path == object_path_); - VLOG(1) << object_path_.value() << ": RequestPasskey"; - - UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", - UMA_PAIRING_METHOD_REQUEST_PASSKEY, - UMA_PAIRING_METHOD_COUNT); - - DCHECK(pairing_delegate_); - DCHECK(passkey_callback_.is_null()); - passkey_callback_ = callback; - pairing_delegate_->RequestPasskey(this); - pairing_delegate_used_ = true; -} - -void BluetoothDeviceExperimentalChromeOS::DisplayPasskey( - const dbus::ObjectPath& device_path, - uint32 passkey, - uint16 entered) { - DCHECK(agent_.get()); - DCHECK(device_path == object_path_); - VLOG(1) << object_path_.value() << ": DisplayPasskey: " << passkey - << " (" << entered << " entered)"; - - if (entered == 0) - UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", - UMA_PAIRING_METHOD_DISPLAY_PASSKEY, - UMA_PAIRING_METHOD_COUNT); - - DCHECK(pairing_delegate_); - if (entered == 0) - pairing_delegate_->DisplayPasskey(this, passkey); - pairing_delegate_->KeysEntered(this, entered); - pairing_delegate_used_ = true; -} - -void BluetoothDeviceExperimentalChromeOS::RequestConfirmation( - const dbus::ObjectPath& device_path, - uint32 passkey, - const ConfirmationCallback& callback) { - DCHECK(agent_.get()); - DCHECK(device_path == object_path_); - VLOG(1) << object_path_.value() << ": RequestConfirmation: " << passkey; - - UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", - UMA_PAIRING_METHOD_CONFIRM_PASSKEY, - UMA_PAIRING_METHOD_COUNT); - - DCHECK(pairing_delegate_); - DCHECK(confirmation_callback_.is_null()); - confirmation_callback_ = callback; - pairing_delegate_->ConfirmPasskey(this, passkey); - pairing_delegate_used_ = true; -} - -void BluetoothDeviceExperimentalChromeOS::RequestAuthorization( - const dbus::ObjectPath& device_path, - const ConfirmationCallback& callback) { - // TODO(keybuk): implement - callback.Run(CANCELLED); -} - -void BluetoothDeviceExperimentalChromeOS::AuthorizeService( - const dbus::ObjectPath& device_path, - const std::string& uuid, - const ConfirmationCallback& callback) { - // TODO(keybuk): implement - callback.Run(CANCELLED); -} - -void BluetoothDeviceExperimentalChromeOS::Cancel() { - DCHECK(agent_.get()); - VLOG(1) << object_path_.value() << ": Cancel"; - - DCHECK(pairing_delegate_); - pairing_delegate_->DismissDisplayOrConfirm(); -} - -void BluetoothDeviceExperimentalChromeOS::ConnectInternal( - bool after_pairing, - const base::Closure& callback, - const ConnectErrorCallback& error_callback) { - VLOG(1) << object_path_.value() << ": Connecting"; - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - Connect( - object_path_, - base::Bind( - &BluetoothDeviceExperimentalChromeOS::OnConnect, - weak_ptr_factory_.GetWeakPtr(), - after_pairing, - callback), - base::Bind( - &BluetoothDeviceExperimentalChromeOS::OnConnectError, - weak_ptr_factory_.GetWeakPtr(), - after_pairing, - error_callback)); -} - -void BluetoothDeviceExperimentalChromeOS::OnConnect( - bool after_pairing, - const base::Closure& callback) { - if (--num_connecting_calls_ == 0) - adapter_->NotifyDeviceChanged(this); - - DCHECK(num_connecting_calls_ >= 0); - VLOG(1) << object_path_.value() << ": Connected, " << num_connecting_calls_ - << " still in progress"; - - SetTrusted(); - - if (after_pairing) - UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingResult", - UMA_PAIRING_RESULT_SUCCESS, - UMA_PAIRING_RESULT_COUNT); - - callback.Run(); -} - -void BluetoothDeviceExperimentalChromeOS::OnConnectError( - bool after_pairing, - const ConnectErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message) { - if (--num_connecting_calls_ == 0) - adapter_->NotifyDeviceChanged(this); - - DCHECK(num_connecting_calls_ >= 0); - LOG(WARNING) << object_path_.value() << ": Failed to connect device: " - << error_name << ": " << error_message; - VLOG(1) << object_path_.value() << ": " << num_connecting_calls_ - << " still in progress"; - - // Determine the error code from error_name. - ConnectErrorCode error_code = ERROR_UNKNOWN; - if (error_name == bluetooth_device::kErrorFailed) { - error_code = ERROR_FAILED; - } else if (error_name == bluetooth_device::kErrorInProgress) { - error_code = ERROR_INPROGRESS; - } else if (error_name == bluetooth_device::kErrorNotSupported) { - error_code = ERROR_UNSUPPORTED_DEVICE; - } - - if (after_pairing) - RecordPairingResult(error_code); - error_callback.Run(error_code); -} - -void BluetoothDeviceExperimentalChromeOS::OnRegisterAgent( - const base::Closure& callback, - const ConnectErrorCallback& error_callback) { - VLOG(1) << object_path_.value() << ": Agent registered, now pairing"; - - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - Pair(object_path_, - base::Bind( - &BluetoothDeviceExperimentalChromeOS::OnPair, - weak_ptr_factory_.GetWeakPtr(), - callback, error_callback), - base::Bind( - &BluetoothDeviceExperimentalChromeOS::OnPairError, - weak_ptr_factory_.GetWeakPtr(), - error_callback)); -} - -void BluetoothDeviceExperimentalChromeOS::OnRegisterAgentError( - const ConnectErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message) { - if (--num_connecting_calls_ == 0) - adapter_->NotifyDeviceChanged(this); - - DCHECK(num_connecting_calls_ >= 0); - LOG(WARNING) << object_path_.value() << ": Failed to register agent: " - << error_name << ": " << error_message; - VLOG(1) << object_path_.value() << ": " << num_connecting_calls_ - << " still in progress"; - - UnregisterAgent(); - - // Determine the error code from error_name. - ConnectErrorCode error_code = ERROR_UNKNOWN; - if (error_name == bluetooth_agent_manager::kErrorAlreadyExists) - error_code = ERROR_INPROGRESS; - - RecordPairingResult(error_code); - error_callback.Run(error_code); -} - -void BluetoothDeviceExperimentalChromeOS::OnPair( - const base::Closure& callback, - const ConnectErrorCallback& error_callback) { - VLOG(1) << object_path_.value() << ": Paired"; - - if (!pairing_delegate_used_) - UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod", - UMA_PAIRING_METHOD_NONE, - UMA_PAIRING_METHOD_COUNT); - UnregisterAgent(); - SetTrusted(); - ConnectInternal(true, callback, error_callback); -} - -void BluetoothDeviceExperimentalChromeOS::OnPairError( - const ConnectErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message) { - if (--num_connecting_calls_ == 0) - adapter_->NotifyDeviceChanged(this); - - DCHECK(num_connecting_calls_ >= 0); - LOG(WARNING) << object_path_.value() << ": Failed to pair device: " - << error_name << ": " << error_message; - VLOG(1) << object_path_.value() << ": " << num_connecting_calls_ - << " still in progress"; - - UnregisterAgent(); - - // Determine the error code from error_name. - ConnectErrorCode error_code = ERROR_UNKNOWN; - if (error_name == bluetooth_device::kErrorConnectionAttemptFailed) { - error_code = ERROR_FAILED; - } else if (error_name == bluetooth_device::kErrorFailed) { - error_code = ERROR_FAILED; - } else if (error_name == bluetooth_device::kErrorAuthenticationFailed) { - error_code = ERROR_AUTH_FAILED; - } else if (error_name == bluetooth_device::kErrorAuthenticationCanceled) { - error_code = ERROR_AUTH_CANCELED; - } else if (error_name == bluetooth_device::kErrorAuthenticationRejected) { - error_code = ERROR_AUTH_REJECTED; - } else if (error_name == bluetooth_device::kErrorAuthenticationTimeout) { - error_code = ERROR_AUTH_TIMEOUT; - } - - RecordPairingResult(error_code); - error_callback.Run(error_code); -} - -void BluetoothDeviceExperimentalChromeOS::OnCancelPairingError( - const std::string& error_name, - const std::string& error_message) { - LOG(WARNING) << object_path_.value() << ": Failed to cancel pairing: " - << error_name << ": " << error_message; -} - -void BluetoothDeviceExperimentalChromeOS::SetTrusted() { - // Unconditionally send the property change, rather than checking the value - // first; there's no harm in doing this and it solves any race conditions - // with the property becoming true or false and this call happening before - // we get the D-Bus signal about the earlier change. - DBusThreadManager::Get()->GetExperimentalBluetoothDeviceClient()-> - GetProperties(object_path_)->trusted.Set( - true, - base::Bind( - &BluetoothDeviceExperimentalChromeOS::OnSetTrusted, - weak_ptr_factory_.GetWeakPtr())); -} - -void BluetoothDeviceExperimentalChromeOS::OnSetTrusted(bool success) { - LOG_IF(WARNING, !success) << object_path_.value() - << ": Failed to set device as trusted"; -} - -void BluetoothDeviceExperimentalChromeOS::UnregisterAgent() { - if (!agent_.get()) - return; - - DCHECK(pairing_delegate_); - - DCHECK(pincode_callback_.is_null()); - DCHECK(passkey_callback_.is_null()); - DCHECK(confirmation_callback_.is_null()); - - pairing_delegate_->DismissDisplayOrConfirm(); - pairing_delegate_ = NULL; - - agent_.reset(); - - // Clean up after ourselves. - VLOG(1) << object_path_.value() << ": Unregistering pairing agent"; - DBusThreadManager::Get()->GetExperimentalBluetoothAgentManagerClient()-> - UnregisterAgent( - dbus::ObjectPath(kAgentPath), - base::Bind(&base::DoNothing), - base::Bind( - &BluetoothDeviceExperimentalChromeOS::OnUnregisterAgentError, - weak_ptr_factory_.GetWeakPtr())); -} - -void BluetoothDeviceExperimentalChromeOS::OnUnregisterAgentError( - const std::string& error_name, - const std::string& error_message) { - LOG(WARNING) << object_path_.value() << ": Failed to unregister agent: " - << error_name << ": " << error_message; -} - -void BluetoothDeviceExperimentalChromeOS::OnDisconnect( - const base::Closure& callback) { - VLOG(1) << object_path_.value() << ": Disconnected"; - callback.Run(); -} - -void BluetoothDeviceExperimentalChromeOS::OnDisconnectError( - const ErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message) { - LOG(WARNING) << object_path_.value() << ": Failed to disconnect device: " - << error_name << ": " << error_message; - error_callback.Run(); -} - -void BluetoothDeviceExperimentalChromeOS::OnForgetError( - const ErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message) { - LOG(WARNING) << object_path_.value() << ": Failed to remove device: " - << error_name << ": " << error_message; - error_callback.Run(); -} - -bool BluetoothDeviceExperimentalChromeOS::RunPairingCallbacks(Status status) { - if (!agent_.get()) - return false; - - 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; - } - - return callback_run; -} - -void BluetoothDeviceExperimentalChromeOS::OnConnectProfile( - device::BluetoothProfile* profile, - const base::Closure& callback) { - BluetoothProfileExperimentalChromeOS* profile_chromeos = - static_cast(profile); - VLOG(1) << object_path_.value() << ": Profile connected: " - << profile_chromeos->uuid(); - callback.Run(); -} - -void BluetoothDeviceExperimentalChromeOS::OnConnectProfileError( - device::BluetoothProfile* profile, - const ErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message) { - BluetoothProfileExperimentalChromeOS* profile_chromeos = - static_cast(profile); - VLOG(1) << object_path_.value() << ": Profile connection failed: " - << profile_chromeos->uuid() << ": " - << error_name << ": " << error_message; - error_callback.Run(); -} - -} // namespace chromeos diff --git a/device/bluetooth/bluetooth_device_experimental_chromeos.h b/device/bluetooth/bluetooth_device_experimental_chromeos.h deleted file mode 100644 index 18b48e9..0000000 --- a/device/bluetooth/bluetooth_device_experimental_chromeos.h +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_EXPERIMENTAL_CHROMEOS_H -#define DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_EXPERIMENTAL_CHROMEOS_H - -#include - -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "chromeos/dbus/experimental_bluetooth_agent_service_provider.h" -#include "chromeos/dbus/experimental_bluetooth_device_client.h" -#include "dbus/object_path.h" -#include "device/bluetooth/bluetooth_device.h" - -namespace chromeos { - -class BluetoothAdapterExperimentalChromeOS; - -// The BluetoothDeviceExperimentalChromeOS class is an alternate implementation -// of BluetoothDevice for the Chrome OS platform using the Bluetooth Smart -// capable backend. It will become the sole implementation for Chrome OS, and -// be renamed to BluetoothDeviceChromeOS, once the backend is switched. -class BluetoothDeviceExperimentalChromeOS - : public device::BluetoothDevice, - private chromeos::ExperimentalBluetoothAgentServiceProvider::Delegate { - public: - // BluetoothDevice override - virtual uint32 GetBluetoothClass() const OVERRIDE; - virtual std::string GetAddress() const OVERRIDE; - virtual uint16 GetVendorID() const OVERRIDE; - virtual uint16 GetProductID() const OVERRIDE; - virtual uint16 GetDeviceID() const OVERRIDE; - virtual bool IsPaired() const OVERRIDE; - virtual bool IsConnected() const OVERRIDE; - virtual bool IsConnectable() const OVERRIDE; - virtual bool IsConnecting() const OVERRIDE; - virtual ServiceList GetServices() const OVERRIDE; - virtual void GetServiceRecords( - const ServiceRecordsCallback& callback, - const ErrorCallback& error_callback) OVERRIDE; - virtual void ProvidesServiceWithName( - const std::string& name, - const ProvidesServiceCallback& callback) OVERRIDE; - virtual bool ExpectingPinCode() const OVERRIDE; - virtual bool ExpectingPasskey() const OVERRIDE; - virtual bool ExpectingConfirmation() const OVERRIDE; - virtual void Connect( - device::BluetoothDevice::PairingDelegate* pairing_delegate, - const base::Closure& callback, - const ConnectErrorCallback& error_callback) OVERRIDE; - virtual void SetPinCode(const std::string& pincode) OVERRIDE; - virtual void SetPasskey(uint32 passkey) OVERRIDE; - virtual void ConfirmPairing() OVERRIDE; - virtual void RejectPairing() OVERRIDE; - virtual void CancelPairing() OVERRIDE; - virtual void Disconnect( - const base::Closure& callback, - const ErrorCallback& error_callback) OVERRIDE; - virtual void Forget(const ErrorCallback& error_callback) OVERRIDE; - virtual void ConnectToService( - const std::string& service_uuid, - const SocketCallback& callback) OVERRIDE; - virtual void ConnectToProfile( - device::BluetoothProfile* profile, - const base::Closure& callback, - const ErrorCallback& error_callback) OVERRIDE; - virtual void SetOutOfBandPairingData( - const device::BluetoothOutOfBandPairingData& data, - const base::Closure& callback, - const ErrorCallback& error_callback) OVERRIDE; - virtual void ClearOutOfBandPairingData( - const base::Closure& callback, - const ErrorCallback& error_callback) OVERRIDE; - - protected: - // BluetoothDevice override - virtual std::string GetDeviceName() const OVERRIDE; - - private: - friend class BluetoothAdapterExperimentalChromeOS; - - BluetoothDeviceExperimentalChromeOS( - BluetoothAdapterExperimentalChromeOS* adapter, - const dbus::ObjectPath& object_path); - virtual ~BluetoothDeviceExperimentalChromeOS(); - - // ExperimentalBluetoothAgentServiceProvider::Delegate override. - virtual void Release() OVERRIDE; - virtual void RequestPinCode(const dbus::ObjectPath& device_path, - const PinCodeCallback& callback) OVERRIDE; - virtual void DisplayPinCode(const dbus::ObjectPath& device_path, - const std::string& pincode) OVERRIDE; - virtual void RequestPasskey(const dbus::ObjectPath& device_path, - const PasskeyCallback& callback) OVERRIDE; - virtual void DisplayPasskey(const dbus::ObjectPath& device_path, - uint32 passkey, uint16 entered) OVERRIDE; - virtual void RequestConfirmation(const dbus::ObjectPath& device_path, - uint32 passkey, - const ConfirmationCallback& callback) - OVERRIDE; - virtual void RequestAuthorization(const dbus::ObjectPath& device_path, - const ConfirmationCallback& callback) - OVERRIDE; - virtual void AuthorizeService(const dbus::ObjectPath& device_path, - const std::string& uuid, - const ConfirmationCallback& callback) OVERRIDE; - virtual void Cancel() OVERRIDE; - - // Internal method to initiate a connection to this device, and methods called - // by dbus:: on completion of the D-Bus method call. - void ConnectInternal(bool after_pairing, - const base::Closure& callback, - const ConnectErrorCallback& error_callback); - void OnConnect(bool after_pairing, - const base::Closure& callback); - void OnConnectError(bool after_pairing, - const ConnectErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message); - - // Called by dbus:: on completion of the D-Bus method call to register the - // pairing agent. - void OnRegisterAgent(const base::Closure& callback, - const ConnectErrorCallback& error_callback); - void OnRegisterAgentError(const ConnectErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message); - - // Called by dbus:: on completion of the D-Bus method call to pair the device. - void OnPair(const base::Closure& callback, - const ConnectErrorCallback& error_callback); - void OnPairError(const ConnectErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message); - - // Called by dbus:: on failure of the D-Bus method call to cancel pairing, - // there is no matching completion call since we don't do anything special - // in that case. - void OnCancelPairingError(const std::string& error_name, - const std::string& error_message); - - // Internal method to set the device as trusted. Trusted devices can connect - // to us automatically, and we can connect to them after rebooting; it also - // causes the device to be remembered by the stack even if not paired. - // |success| to the callback indicates whether or not the request succeeded. - void SetTrusted(); - void OnSetTrusted(bool success); - - // Internal method to unregister the pairing agent and method called by dbus:: - // on failure of the D-Bus method call. No completion call as success is - // ignored. - void UnregisterAgent(); - void OnUnregisterAgentError(const std::string& error_name, - const std::string& error_message); - - // Called by dbus:: on completion of the D-Bus method call to disconnect the - // device. - void OnDisconnect(const base::Closure& callback); - void OnDisconnectError(const ErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message); - - // Called by dbus:: on failure of the D-Bus method call to unpair the device; - // there is no matching completion call since this object is deleted in the - // process of unpairing. - void OnForgetError(const ErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message); - - // Run any outstanding pairing callbacks passing |status| as the result of - // pairing. Returns true if any callbacks were run, false if not. - bool RunPairingCallbacks(Status status); - - // Called by dbus:: on completion of the D-Bus method call to - // connect a peofile. - void OnConnectProfile(device::BluetoothProfile* profile, - const base::Closure& callback); - void OnConnectProfileError(device::BluetoothProfile* profile, - const ErrorCallback& error_callback, - const std::string& error_name, - const std::string& error_message); - - // Return the object path of the device; used by - // BluetoothAdapterExperimentalChromeOS - const dbus::ObjectPath& object_path() const { return object_path_; } - - // The adapter that owns this device instance. - BluetoothAdapterExperimentalChromeOS* adapter_; - - // The dbus object path of the device object. - dbus::ObjectPath object_path_; - - // Number of ongoing calls to Connect(). - int num_connecting_calls_; - - // 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_; - - // Flag to indicate whether a pairing delegate method has been called during - // pairing. - bool pairing_delegate_used_; - - // During pairing this is set to an instance of a D-Bus agent object - // intialized with our own class as its delegate. - scoped_ptr agent_; - - // During pairing these callbacks are set to those provided by method calls - // made on us by |agent_| and are called by our own method calls such as - // SetPinCode() and SetPasskey(). - PinCodeCallback pincode_callback_; - PasskeyCallback passkey_callback_; - ConfirmationCallback confirmation_callback_; - - // Note: This should remain the last member so it'll be destroyed and - // invalidate its weak pointers before any other members are destroyed. - base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(BluetoothDeviceExperimentalChromeOS); -}; - -} // namespace chromeos - -#endif // DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_EXPERIMENTAL_CHROMEOS_H diff --git a/device/bluetooth/bluetooth_experimental_chromeos_unittest.cc b/device/bluetooth/bluetooth_experimental_chromeos_unittest.cc deleted file mode 100644 index a680a4b..0000000 --- a/device/bluetooth/bluetooth_experimental_chromeos_unittest.cc +++ /dev/null @@ -1,2027 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/message_loop.h" -#include "base/strings/utf_string_conversions.h" -#include "chromeos/dbus/fake_bluetooth_adapter_client.h" -#include "chromeos/dbus/fake_bluetooth_device_client.h" -#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" -#include "dbus/object_path.h" -#include "device/bluetooth/bluetooth_adapter.h" -#include "device/bluetooth/bluetooth_adapter_experimental_chromeos.h" -#include "device/bluetooth/bluetooth_adapter_factory.h" -#include "device/bluetooth/bluetooth_device.h" -#include "device/bluetooth/bluetooth_device_experimental_chromeos.h" -#include "testing/gtest/include/gtest/gtest.h" - -using device::BluetoothAdapter; -using device::BluetoothAdapterFactory; -using device::BluetoothDevice; - -namespace chromeos { - -class TestObserver : public BluetoothAdapter::Observer { - public: - TestObserver(scoped_refptr adapter) - : present_changed_count_(0), - powered_changed_count_(0), - discovering_changed_count_(0), - last_present_(false), - last_powered_(false), - last_discovering_(false), - device_added_count_(0), - device_changed_count_(0), - device_removed_count_(0), - last_device_(NULL), - adapter_(adapter) { - } - virtual ~TestObserver() {} - - virtual void AdapterPresentChanged(BluetoothAdapter* adapter, - bool present) OVERRIDE { - EXPECT_EQ(adapter_, adapter); - - ++present_changed_count_; - last_present_ = present; - } - - virtual void AdapterPoweredChanged(BluetoothAdapter* adapter, - bool powered) OVERRIDE { - EXPECT_EQ(adapter_, adapter); - - ++powered_changed_count_; - last_powered_ = powered; - } - - virtual void AdapterDiscoveringChanged(BluetoothAdapter* adapter, - bool discovering) OVERRIDE { - EXPECT_EQ(adapter_, adapter); - - ++discovering_changed_count_; - last_discovering_ = discovering; - } - - virtual void DeviceAdded(BluetoothAdapter* adapter, - BluetoothDevice* device) OVERRIDE { - EXPECT_EQ(adapter_, adapter); - - ++device_added_count_; - last_device_ = device; - last_device_address_ = device->GetAddress(); - - QuitMessageLoop(); - } - - virtual void DeviceChanged(BluetoothAdapter* adapter, - BluetoothDevice* device) OVERRIDE { - EXPECT_EQ(adapter_, adapter); - - ++device_changed_count_; - last_device_ = device; - last_device_address_ = device->GetAddress(); - - QuitMessageLoop(); - } - - virtual void DeviceRemoved(BluetoothAdapter* adapter, - BluetoothDevice* device) OVERRIDE { - EXPECT_EQ(adapter_, adapter); - - ++device_removed_count_; - // Can't save device, it may be freed - last_device_address_ = device->GetAddress(); - - QuitMessageLoop(); - } - - int present_changed_count_; - int powered_changed_count_; - int discovering_changed_count_; - bool last_present_; - bool last_powered_; - bool last_discovering_; - int device_added_count_; - int device_changed_count_; - int device_removed_count_; - BluetoothDevice* last_device_; - std::string last_device_address_; - - 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(); - } - - scoped_refptr adapter_; -}; - -class TestPairingDelegate : public BluetoothDevice::PairingDelegate { - public: - TestPairingDelegate() - : call_count_(0), - request_pincode_count_(0), - request_passkey_count_(0), - display_pincode_count_(0), - display_passkey_count_(0), - keys_entered_count_(0), - confirm_passkey_count_(0), - dismiss_count_(0), - last_passkey_(9999999U), - last_entered_(999U) {} - virtual ~TestPairingDelegate() {} - - virtual void RequestPinCode(BluetoothDevice* device) OVERRIDE { - ++call_count_; - ++request_pincode_count_; - QuitMessageLoop(); - } - - virtual void RequestPasskey(BluetoothDevice* device) OVERRIDE { - ++call_count_; - ++request_passkey_count_; - QuitMessageLoop(); - } - - virtual void DisplayPinCode(BluetoothDevice* device, - const std::string& pincode) OVERRIDE { - ++call_count_; - ++display_pincode_count_; - last_pincode_ = pincode; - QuitMessageLoop(); - } - - virtual void DisplayPasskey(BluetoothDevice* device, - uint32 passkey) OVERRIDE { - ++call_count_; - ++display_passkey_count_; - last_passkey_ = passkey; - QuitMessageLoop(); - } - - virtual void KeysEntered(BluetoothDevice* device, uint32 entered) OVERRIDE { - ++call_count_; - ++keys_entered_count_; - last_entered_ = entered; - QuitMessageLoop(); - } - - virtual void ConfirmPasskey(BluetoothDevice* device, - uint32 passkey) OVERRIDE { - ++call_count_; - ++confirm_passkey_count_; - last_passkey_ = passkey; - QuitMessageLoop(); - } - - virtual void DismissDisplayOrConfirm() OVERRIDE { - ++call_count_; - ++dismiss_count_; - QuitMessageLoop(); - } - - int call_count_; - int request_pincode_count_; - int request_passkey_count_; - int display_pincode_count_; - int display_passkey_count_; - int keys_entered_count_; - int confirm_passkey_count_; - int dismiss_count_; - uint32 last_passkey_; - uint32 last_entered_; - std::string last_pincode_; - - 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(); - } -}; - -class BluetoothExperimentalChromeOSTest : public testing::Test { - public: - virtual void SetUp() { - mock_dbus_thread_manager_ = - new MockDBusThreadManagerWithoutGMock(); - DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager_); - - fake_bluetooth_adapter_client_ = - mock_dbus_thread_manager_->fake_bluetooth_adapter_client(); - fake_bluetooth_device_client_ = - mock_dbus_thread_manager_->fake_bluetooth_device_client(); - - callback_count_ = 0; - error_callback_count_ = 0; - last_connect_error_ = BluetoothDevice::ERROR_UNKNOWN; - } - - virtual void TearDown() { - adapter_ = NULL; - DBusThreadManager::Shutdown(); - } - - // Generic callbacks - void Callback() { - ++callback_count_; - } - - void ErrorCallback() { - ++error_callback_count_; - } - - void ConnectErrorCallback(enum BluetoothDevice::ConnectErrorCode error) { - ++error_callback_count_; - last_connect_error_ = error; - } - - // Call to fill the adapter_ member with a BluetoothAdapter instance. - void GetAdapter() { - adapter_ = new BluetoothAdapterExperimentalChromeOS(); - ASSERT_TRUE(adapter_ != NULL); - ASSERT_TRUE(adapter_->IsInitialized()); - } - - // Run a discovery phase until the named device is detected, or if the named - // device is not created, the discovery process ends without finding it. - // - // The correct behavior of discovery is tested by the "Discovery" test case - // without using this function. - void DiscoverDevice(const std::string& address) { - ASSERT_TRUE(adapter_ != NULL); - - if (base::MessageLoop::current() == NULL) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - DiscoverDevices(); - return; - } - - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - adapter_->SetPowered( - true, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - adapter_->StartDiscovering( - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - ASSERT_EQ(2, callback_count_); - ASSERT_EQ(0, error_callback_count_); - callback_count_ = 0; - - ASSERT_TRUE(adapter_->IsPowered()); - ASSERT_TRUE(adapter_->IsDiscovering()); - - while (!observer.device_removed_count_ && - observer.last_device_address_ != address) - base::MessageLoop::current()->Run(); - - adapter_->StopDiscovering( - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - ASSERT_EQ(1, callback_count_); - ASSERT_EQ(0, error_callback_count_); - callback_count_ = 0; - - ASSERT_FALSE(adapter_->IsDiscovering()); - - adapter_->RemoveObserver(&observer); - } - - // Run a discovery phase so we have devices that can be paired with. - void DiscoverDevices() { - // Pass an invalid address for the device so that the discovery process - // completes with all devices. - DiscoverDevice("does not exist"); - } - - protected: - FakeBluetoothAdapterClient* fake_bluetooth_adapter_client_; - FakeBluetoothDeviceClient* fake_bluetooth_device_client_; - MockDBusThreadManagerWithoutGMock* mock_dbus_thread_manager_; - scoped_refptr adapter_; - - int callback_count_; - int error_callback_count_; - enum BluetoothDevice::ConnectErrorCode last_connect_error_; -}; - -TEST_F(BluetoothExperimentalChromeOSTest, AlreadyPresent) { - GetAdapter(); - - // This verifies that the class gets the list of adapters when created; - // and initializes with an existing adapter if there is one. - EXPECT_TRUE(adapter_->IsPresent()); - EXPECT_FALSE(adapter_->IsPowered()); - EXPECT_EQ(FakeBluetoothAdapterClient::kAdapterAddress, - adapter_->GetAddress()); - EXPECT_FALSE(adapter_->IsDiscovering()); - - // There should be a device - BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); - EXPECT_EQ(1U, devices.size()); - EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, - devices[0]->GetAddress()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, BecomePresent) { - fake_bluetooth_adapter_client_->SetVisible(false); - GetAdapter(); - ASSERT_FALSE(adapter_->IsPresent()); - - // Install an observer; expect the AdapterPresentChanged to be called - // with true, and IsPresent() to return true. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - fake_bluetooth_adapter_client_->SetVisible(true); - - EXPECT_EQ(1, observer.present_changed_count_); - EXPECT_TRUE(observer.last_present_); - - EXPECT_TRUE(adapter_->IsPresent()); - - // We should have had a device announced. - EXPECT_EQ(1, observer.device_added_count_); - EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, - observer.last_device_address_); - - // Other callbacks shouldn't be called if the values are false. - EXPECT_EQ(0, observer.powered_changed_count_); - EXPECT_EQ(0, observer.discovering_changed_count_); - EXPECT_FALSE(adapter_->IsPowered()); - EXPECT_FALSE(adapter_->IsDiscovering()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, BecomeNotPresent) { - GetAdapter(); - ASSERT_TRUE(adapter_->IsPresent()); - - // Install an observer; expect the AdapterPresentChanged to be called - // with false, and IsPresent() to return false. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - fake_bluetooth_adapter_client_->SetVisible(false); - - EXPECT_EQ(1, observer.present_changed_count_); - EXPECT_FALSE(observer.last_present_); - - EXPECT_FALSE(adapter_->IsPresent()); - - // We should have had a device removed. - EXPECT_EQ(1, observer.device_removed_count_); - EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, - observer.last_device_address_); - - // Other callbacks shouldn't be called since the values are false. - EXPECT_EQ(0, observer.powered_changed_count_); - EXPECT_EQ(0, observer.discovering_changed_count_); - EXPECT_FALSE(adapter_->IsPowered()); - EXPECT_FALSE(adapter_->IsDiscovering()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, SecondAdapter) { - GetAdapter(); - ASSERT_TRUE(adapter_->IsPresent()); - - // Install an observer, then add a second adapter. Nothing should change, - // we ignore the second adapter. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - fake_bluetooth_adapter_client_->SetSecondVisible(true); - - EXPECT_EQ(0, observer.present_changed_count_); - - EXPECT_TRUE(adapter_->IsPresent()); - EXPECT_EQ(FakeBluetoothAdapterClient::kAdapterAddress, - adapter_->GetAddress()); - - // Try removing the first adapter, we should now act as if the adapter - // is no longer present rather than fall back to the second. - fake_bluetooth_adapter_client_->SetVisible(false); - - EXPECT_EQ(1, observer.present_changed_count_); - EXPECT_FALSE(observer.last_present_); - - EXPECT_FALSE(adapter_->IsPresent()); - - // We should have had a device removed. - EXPECT_EQ(1, observer.device_removed_count_); - EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, - observer.last_device_address_); - - // Other callbacks shouldn't be called since the values are false. - EXPECT_EQ(0, observer.powered_changed_count_); - EXPECT_EQ(0, observer.discovering_changed_count_); - EXPECT_FALSE(adapter_->IsPowered()); - EXPECT_FALSE(adapter_->IsDiscovering()); - - observer.device_removed_count_ = 0; - - // Removing the second adapter shouldn't set anything either. - fake_bluetooth_adapter_client_->SetSecondVisible(false); - - EXPECT_EQ(0, observer.device_removed_count_); - EXPECT_EQ(0, observer.powered_changed_count_); - EXPECT_EQ(0, observer.discovering_changed_count_); -} - -TEST_F(BluetoothExperimentalChromeOSTest, BecomePowered) { - GetAdapter(); - ASSERT_FALSE(adapter_->IsPowered()); - - // Install an observer; expect the AdapterPoweredChanged to be called - // with true, and IsPowered() to return true. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - adapter_->SetPowered( - true, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - - EXPECT_EQ(1, observer.powered_changed_count_); - EXPECT_TRUE(observer.last_powered_); - - EXPECT_TRUE(adapter_->IsPowered()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, BecomeNotPowered) { - GetAdapter(); - adapter_->SetPowered( - true, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - callback_count_ = 0; - - ASSERT_TRUE(adapter_->IsPowered()); - - // Install an observer; expect the AdapterPoweredChanged to be called - // with false, and IsPowered() to return false. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - adapter_->SetPowered( - false, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - - EXPECT_EQ(1, observer.powered_changed_count_); - EXPECT_FALSE(observer.last_powered_); - - EXPECT_FALSE(adapter_->IsPowered()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, StopDiscovery) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - - GetAdapter(); - - adapter_->SetPowered( - true, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - adapter_->StartDiscovering( - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - EXPECT_EQ(2, callback_count_); - EXPECT_EQ(0, error_callback_count_); - callback_count_ = 0; - - ASSERT_TRUE(adapter_->IsPowered()); - ASSERT_TRUE(adapter_->IsDiscovering()); - - // Install an observer; aside from the callback, expect the - // AdapterDiscoveringChanged method to be called and no longer to be - // discovering, - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - adapter_->StopDiscovering( - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - - EXPECT_EQ(1, observer.discovering_changed_count_); - EXPECT_FALSE(observer.last_discovering_); - - EXPECT_FALSE(adapter_->IsDiscovering()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, StopDiscoveryAfterTwoStarts) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - - GetAdapter(); - - adapter_->SetPowered( - true, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - adapter_->StartDiscovering( - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - EXPECT_EQ(2, callback_count_); - EXPECT_EQ(0, error_callback_count_); - callback_count_ = 0; - - ASSERT_TRUE(adapter_->IsPowered()); - ASSERT_TRUE(adapter_->IsDiscovering()); - - // Install an observer and start discovering again; only the callback - // should be called since we were already discovering to begin with. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - adapter_->StartDiscovering( - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - callback_count_ = 0; - - EXPECT_EQ(0, observer.discovering_changed_count_); - - // Stop discovering; only the callback should be called since we're still - // discovering. The adapter should be still discovering. - adapter_->StopDiscovering( - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - callback_count_ = 0; - - EXPECT_EQ(0, observer.discovering_changed_count_); - - EXPECT_TRUE(adapter_->IsDiscovering()); - - // Stop discovering one more time; aside from the callback, expect the - // AdapterDiscoveringChanged method to be called and no longer to be - // discovering, - adapter_->StopDiscovering( - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - - EXPECT_EQ(1, observer.discovering_changed_count_); - EXPECT_FALSE(observer.last_discovering_); - - EXPECT_FALSE(adapter_->IsDiscovering()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, Discovery) { - // Test a simulated discovery session. - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - GetAdapter(); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - adapter_->SetPowered( - true, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - adapter_->StartDiscovering( - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - EXPECT_EQ(2, callback_count_); - EXPECT_EQ(0, error_callback_count_); - callback_count_ = 0; - - ASSERT_TRUE(adapter_->IsPowered()); - ASSERT_TRUE(adapter_->IsDiscovering()); - - // First device to appear should be an Apple Mouse. - message_loop.Run(); - - EXPECT_EQ(1, observer.device_added_count_); - EXPECT_EQ(FakeBluetoothDeviceClient::kAppleMouseAddress, - observer.last_device_address_); - - // Next we should get another two devices... - message_loop.Run(); - EXPECT_EQ(3, observer.device_added_count_); - - // Okay, let's run forward until a device is actually removed... - while (!observer.device_removed_count_) - message_loop.Run(); - - EXPECT_EQ(1, observer.device_removed_count_); - EXPECT_EQ(FakeBluetoothDeviceClient::kVanishingDeviceAddress, - observer.last_device_address_); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PoweredAndDiscovering) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - - GetAdapter(); - adapter_->SetPowered( - true, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - adapter_->StartDiscovering( - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - EXPECT_EQ(2, callback_count_); - EXPECT_EQ(0, error_callback_count_); - callback_count_ = 0; - - // Stop the timers that the simulation uses - fake_bluetooth_device_client_->EndDiscoverySimulation( - dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); - - ASSERT_TRUE(adapter_->IsPowered()); - ASSERT_TRUE(adapter_->IsDiscovering()); - - fake_bluetooth_adapter_client_->SetVisible(false); - ASSERT_FALSE(adapter_->IsPresent()); - - // Install an observer; expect the AdapterPresentChanged, - // AdapterPoweredChanged and AdapterDiscoveringChanged methods to be called - // with true, and IsPresent(), IsPowered() and IsDiscovering() to all - // return true. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - fake_bluetooth_adapter_client_->SetVisible(true); - - EXPECT_EQ(1, observer.present_changed_count_); - EXPECT_TRUE(observer.last_present_); - EXPECT_TRUE(adapter_->IsPresent()); - - EXPECT_EQ(1, observer.powered_changed_count_); - EXPECT_TRUE(observer.last_powered_); - EXPECT_TRUE(adapter_->IsPowered()); - - EXPECT_EQ(1, observer.discovering_changed_count_); - EXPECT_TRUE(observer.last_discovering_); - EXPECT_TRUE(adapter_->IsDiscovering()); - - observer.present_changed_count_ = 0; - observer.powered_changed_count_ = 0; - observer.discovering_changed_count_ = 0; - - // Now mark the adapter not present again. Expect the methods to be called - // again, to reset the properties back to false - fake_bluetooth_adapter_client_->SetVisible(false); - - EXPECT_EQ(1, observer.present_changed_count_); - EXPECT_FALSE(observer.last_present_); - EXPECT_FALSE(adapter_->IsPresent()); - - EXPECT_EQ(1, observer.powered_changed_count_); - EXPECT_FALSE(observer.last_powered_); - EXPECT_FALSE(adapter_->IsPowered()); - - EXPECT_EQ(1, observer.discovering_changed_count_); - EXPECT_FALSE(observer.last_discovering_); - EXPECT_FALSE(adapter_->IsDiscovering()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, DeviceProperties) { - GetAdapter(); - - BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); - ASSERT_EQ(1U, devices.size()); - ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, - devices[0]->GetAddress()); - - // Verify the other device properties. - EXPECT_EQ(UTF8ToUTF16(FakeBluetoothDeviceClient::kPairedDeviceName), - devices[0]->GetName()); - EXPECT_EQ(BluetoothDevice::DEVICE_COMPUTER, devices[0]->GetDeviceType()); - EXPECT_TRUE(devices[0]->IsPaired()); - EXPECT_FALSE(devices[0]->IsConnected()); - EXPECT_FALSE(devices[0]->IsConnecting()); - - // Non HID devices are always connectable. - EXPECT_TRUE(devices[0]->IsConnectable()); - - BluetoothDevice::ServiceList uuids = devices[0]->GetServices(); - ASSERT_EQ(2U, uuids.size()); - EXPECT_EQ(uuids[0], "00001800-0000-1000-8000-00805f9b34fb"); - EXPECT_EQ(uuids[1], "00001801-0000-1000-8000-00805f9b34fb"); - - EXPECT_EQ(0x05ac, devices[0]->GetVendorID()); - EXPECT_EQ(0x030d, devices[0]->GetProductID()); - EXPECT_EQ(0x0306, devices[0]->GetDeviceID()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, DeviceClassChanged) { - // Simulate a change of class of a device, as sometimes occurs - // during discovery. - GetAdapter(); - - BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); - ASSERT_EQ(1U, devices.size()); - ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, - devices[0]->GetAddress()); - ASSERT_EQ(BluetoothDevice::DEVICE_COMPUTER, devices[0]->GetDeviceType()); - - // Install an observer; expect the DeviceChanged method to be called when - // we change the class of the device. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - FakeBluetoothDeviceClient::Properties* properties = - fake_bluetooth_device_client_->GetProperties( - dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath)); - - properties->bluetooth_class.ReplaceValue(0x002580); - - EXPECT_EQ(1, observer.device_changed_count_); - EXPECT_EQ(devices[0], observer.last_device_); - - EXPECT_EQ(BluetoothDevice::DEVICE_MOUSE, devices[0]->GetDeviceType()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, DeviceNameChanged) { - // Simulate a change of name of a device. - GetAdapter(); - - BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); - ASSERT_EQ(1U, devices.size()); - ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, - devices[0]->GetAddress()); - ASSERT_EQ(UTF8ToUTF16(FakeBluetoothDeviceClient::kPairedDeviceName), - devices[0]->GetName()); - - // Install an observer; expect the DeviceChanged method to be called when - // we change the alias of the device. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - FakeBluetoothDeviceClient::Properties* properties = - fake_bluetooth_device_client_->GetProperties( - dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath)); - - static const std::string new_name("New Device Name"); - properties->alias.ReplaceValue(new_name); - - EXPECT_EQ(1, observer.device_changed_count_); - EXPECT_EQ(devices[0], observer.last_device_); - - EXPECT_EQ(UTF8ToUTF16(new_name), devices[0]->GetName()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, DeviceUuidsChanged) { - // Simulate a change of advertised services of a device. - GetAdapter(); - - BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); - ASSERT_EQ(1U, devices.size()); - ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, - devices[0]->GetAddress()); - - BluetoothDevice::ServiceList uuids = devices[0]->GetServices(); - ASSERT_EQ(2U, uuids.size()); - ASSERT_EQ(uuids[0], "00001800-0000-1000-8000-00805f9b34fb"); - ASSERT_EQ(uuids[1], "00001801-0000-1000-8000-00805f9b34fb"); - - // Install an observer; expect the DeviceChanged method to be called when - // we change the class of the device. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - FakeBluetoothDeviceClient::Properties* properties = - fake_bluetooth_device_client_->GetProperties( - dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath)); - - uuids.push_back("0000110c-0000-1000-8000-00805f9b34fb"); - uuids.push_back("0000110e-0000-1000-8000-00805f9b34fb"); - uuids.push_back("0000110a-0000-1000-8000-00805f9b34fb"); - - properties->uuids.ReplaceValue(uuids); - - EXPECT_EQ(1, observer.device_changed_count_); - EXPECT_EQ(devices[0], observer.last_device_); - - // Fetching the value should give the new one. - uuids = devices[0]->GetServices(); - ASSERT_EQ(5U, uuids.size()); - EXPECT_EQ(uuids[0], "00001800-0000-1000-8000-00805f9b34fb"); - EXPECT_EQ(uuids[1], "00001801-0000-1000-8000-00805f9b34fb"); - EXPECT_EQ(uuids[2], "0000110c-0000-1000-8000-00805f9b34fb"); - EXPECT_EQ(uuids[3], "0000110e-0000-1000-8000-00805f9b34fb"); - EXPECT_EQ(uuids[4], "0000110a-0000-1000-8000-00805f9b34fb"); -} - -TEST_F(BluetoothExperimentalChromeOSTest, ForgetDevice) { - GetAdapter(); - - BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); - ASSERT_EQ(1U, devices.size()); - ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress, - devices[0]->GetAddress()); - - std::string address = devices[0]->GetAddress(); - - // Install an observer; expect the DeviceRemoved method to be called - // with the device we remove. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - devices[0]->Forget( - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - EXPECT_EQ(0, error_callback_count_); - - EXPECT_EQ(1, observer.device_removed_count_); - EXPECT_EQ(address, observer.last_device_address_); - - // GetDevices shouldn't return the device either. - devices = adapter_->GetDevices(); - ASSERT_EQ(0U, devices.size()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, ForgetUnpairedDevice) { - GetAdapter(); - DiscoverDevices(); - - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kMicrosoftMouseAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - // Connect the device so it becomes trusted and remembered. - device->Connect( - NULL, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - ASSERT_EQ(1, callback_count_); - ASSERT_EQ(0, error_callback_count_); - callback_count_ = 0; - - ASSERT_TRUE(device->IsConnected()); - ASSERT_FALSE(device->IsConnecting()); - - // Make sure the trusted property has been set to true. - FakeBluetoothDeviceClient::Properties* properties = - fake_bluetooth_device_client_->GetProperties( - dbus::ObjectPath(FakeBluetoothDeviceClient::kMicrosoftMousePath)); - ASSERT_TRUE(properties->trusted.value()); - - // Install an observer; expect the DeviceRemoved method to be called - // with the device we remove. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - device->Forget( - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - EXPECT_EQ(0, error_callback_count_); - - EXPECT_EQ(1, observer.device_removed_count_); - EXPECT_EQ(FakeBluetoothDeviceClient::kMicrosoftMouseAddress, - observer.last_device_address_); - - // GetDevices shouldn't return the device either. - device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kMicrosoftMouseAddress); - EXPECT_FALSE(device != NULL); -} - -TEST_F(BluetoothExperimentalChromeOSTest, ConnectPairedDevice) { - GetAdapter(); - - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kPairedDeviceAddress); - ASSERT_TRUE(device != NULL); - ASSERT_TRUE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - // Connect without a pairing delegate; since the device is already Paired - // this should succeed and the device should become connected. - device->Connect( - NULL, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - - // Two changes for connecting, one for connected and one for for trusted - // after connecting. - EXPECT_EQ(4, observer.device_changed_count_); - EXPECT_EQ(device, observer.last_device_); - - EXPECT_TRUE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, ConnectUnpairableDevice) { - GetAdapter(); - DiscoverDevices(); - - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kMicrosoftMouseAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - // Connect without a pairing delegate; since the device does not require - // pairing, this should succeed and the device should become connected. - device->Connect( - NULL, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - - // Two changes for connecting, one for connected, one for for trusted after - // connection, and one for the reconnect mode (IsConnectable). - EXPECT_EQ(5, observer.device_changed_count_); - EXPECT_EQ(device, observer.last_device_); - - EXPECT_TRUE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - - // Make sure the trusted property has been set to true. - FakeBluetoothDeviceClient::Properties* properties = - fake_bluetooth_device_client_->GetProperties( - dbus::ObjectPath(FakeBluetoothDeviceClient::kMicrosoftMousePath)); - EXPECT_TRUE(properties->trusted.value()); - - // Verify is a HID device and is not connectable. - BluetoothDevice::ServiceList uuids = device->GetServices(); - ASSERT_EQ(1U, uuids.size()); - EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb"); - EXPECT_FALSE(device->IsConnectable()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, ConnectConnectedDevice) { - GetAdapter(); - - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kPairedDeviceAddress); - ASSERT_TRUE(device != NULL); - ASSERT_TRUE(device->IsPaired()); - - device->Connect( - NULL, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - ASSERT_EQ(1, callback_count_); - ASSERT_EQ(0, error_callback_count_); - callback_count_ = 0; - - ASSERT_TRUE(device->IsConnected()); - - // Connect again; since the device is already Connected, this shouldn't do - // anything to initiate the connection. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - device->Connect( - NULL, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - - // The observer will be called because Connecting will toggle true and false, - // and the trusted property will be updated to true. - EXPECT_EQ(3, observer.device_changed_count_); - - EXPECT_TRUE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, ConnectDeviceFails) { - GetAdapter(); - DiscoverDevices(); - - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kAppleMouseAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - // Connect without a pairing delegate; since the device requires pairing, - // this should fail with an error. - device->Connect( - NULL, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(0, callback_count_); - EXPECT_EQ(1, error_callback_count_); - EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_); - - EXPECT_EQ(2, observer.device_changed_count_); - - EXPECT_FALSE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, DisconnectDevice) { - GetAdapter(); - - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kPairedDeviceAddress); - ASSERT_TRUE(device != NULL); - ASSERT_TRUE(device->IsPaired()); - - device->Connect( - NULL, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - ASSERT_EQ(1, callback_count_); - ASSERT_EQ(0, error_callback_count_); - callback_count_ = 0; - - ASSERT_TRUE(device->IsConnected()); - ASSERT_FALSE(device->IsConnecting()); - - // Disconnect the device, we should see the observer method fire and the - // device get dropped. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - device->Disconnect( - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - - EXPECT_EQ(1, observer.device_changed_count_); - EXPECT_EQ(device, observer.last_device_); - - EXPECT_FALSE(device->IsConnected()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, DisconnectUnconnectedDevice) { - GetAdapter(); - - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kPairedDeviceAddress); - ASSERT_TRUE(device != NULL); - ASSERT_TRUE(device->IsPaired()); - ASSERT_FALSE(device->IsConnected()); - - // Disconnect the device, we should see the observer method fire and the - // device get dropped. - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - device->Disconnect( - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(0, callback_count_); - EXPECT_EQ(1, error_callback_count_); - - EXPECT_EQ(0, observer.device_changed_count_); - - EXPECT_FALSE(device->IsConnected()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairAppleMouse) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevices(); - - // The Apple Mouse requires no PIN or Passkey to pair; this is equivalent - // to Simple Secure Pairing or a device with a fixed 0000 PIN. - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kAppleMouseAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(0, pairing_delegate.call_count_); - EXPECT_TRUE(device->IsConnecting()); - - message_loop.Run(); - - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - - // Two changes for connecting, one change for connected, one for paired, - // two for trusted (after pairing and connection), and one for the reconnect - // mode (IsConnectable). - EXPECT_EQ(7, observer.device_changed_count_); - EXPECT_EQ(device, observer.last_device_); - - EXPECT_TRUE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - - EXPECT_TRUE(device->IsPaired()); - - // Verify is a HID device and is connectable. - BluetoothDevice::ServiceList uuids = device->GetServices(); - ASSERT_EQ(1U, uuids.size()); - EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb"); - EXPECT_TRUE(device->IsConnectable()); - - // Pairing dialog should be dismissed - EXPECT_EQ(1, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.dismiss_count_); - - // Make sure the trusted property has been set to true. - FakeBluetoothDeviceClient::Properties* properties = - fake_bluetooth_device_client_->GetProperties( - dbus::ObjectPath(FakeBluetoothDeviceClient::kAppleMousePath)); - EXPECT_TRUE(properties->trusted.value()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairAppleKeyboard) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevices(); - - // The Apple Keyboard requires that we display a randomly generated - // PIN on the screen. - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kAppleKeyboardAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(1, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.display_pincode_count_); - EXPECT_EQ("123456", pairing_delegate.last_pincode_); - EXPECT_TRUE(device->IsConnecting()); - - message_loop.Run(); - - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - - // Two changes for connecting, one change for connected, one for paired, - // two for trusted (after pairing and connection), and one for the reconnect - // mode (IsConnectable). - EXPECT_EQ(7, observer.device_changed_count_); - EXPECT_EQ(device, observer.last_device_); - - EXPECT_TRUE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - - EXPECT_TRUE(device->IsPaired()); - - // Verify is a HID device and is connectable. - BluetoothDevice::ServiceList uuids = device->GetServices(); - ASSERT_EQ(1U, uuids.size()); - EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb"); - EXPECT_TRUE(device->IsConnectable()); - - // Pairing dialog should be dismissed - EXPECT_EQ(1, pairing_delegate.dismiss_count_); - - // Make sure the trusted property has been set to true. - FakeBluetoothDeviceClient::Properties* properties = - fake_bluetooth_device_client_->GetProperties( - dbus::ObjectPath(FakeBluetoothDeviceClient::kAppleKeyboardPath)); - EXPECT_TRUE(properties->trusted.value()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairMotorolaKeyboard) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevices(); - - // The Motorola Keyboard requires that we display a randomly generated - // Passkey on the screen, and notifies us as it's typed in. - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kMotorolaKeyboardAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - // One call for DisplayPasskey() and one for KeysEntered(). - EXPECT_EQ(2, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.display_passkey_count_); - EXPECT_EQ(123456U, pairing_delegate.last_passkey_); - EXPECT_EQ(1, pairing_delegate.keys_entered_count_); - EXPECT_EQ(0U, pairing_delegate.last_entered_); - - EXPECT_TRUE(device->IsConnecting()); - - // One call to KeysEntered() for each key, including [enter]. - for(int i = 1; i <= 7; ++i) { - message_loop.Run(); - - EXPECT_EQ(2 + i, pairing_delegate.call_count_); - EXPECT_EQ(1 + i, pairing_delegate.keys_entered_count_); - EXPECT_EQ(static_cast(i), pairing_delegate.last_entered_); - } - - message_loop.Run(); - - // 8 KeysEntered notifications (0 to 7, inclusive). Two aditional calls for - // DisplayPasskey() and DismissDisplayOrConfirm(). - EXPECT_EQ(10, pairing_delegate.call_count_); - EXPECT_EQ(8, pairing_delegate.keys_entered_count_); - EXPECT_EQ(7U, pairing_delegate.last_entered_); - - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - - // Two changes for connecting, one change for connected, one for paired, - // two for trusted (after pairing and connection), and one for the reconnect - // mode (IsConnectable). - EXPECT_EQ(7, observer.device_changed_count_); - EXPECT_EQ(device, observer.last_device_); - - EXPECT_TRUE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - - EXPECT_TRUE(device->IsPaired()); - - // Verify is a HID device. - BluetoothDevice::ServiceList uuids = device->GetServices(); - ASSERT_EQ(1U, uuids.size()); - EXPECT_EQ(uuids[0], "00001124-0000-1000-8000-00805f9b34fb"); - - // Fake MotorolaKeyboard is not connectable. - EXPECT_FALSE(device->IsConnectable()); - - // Pairing dialog should be dismissed - EXPECT_EQ(1, pairing_delegate.dismiss_count_); - - // Make sure the trusted property has been set to true. - FakeBluetoothDeviceClient::Properties* properties = - fake_bluetooth_device_client_->GetProperties( - dbus::ObjectPath(FakeBluetoothDeviceClient::kMotorolaKeyboardPath)); - EXPECT_TRUE(properties->trusted.value()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairSonyHeadphones) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevices(); - - // The Sony Headphones fake requires that the user enters a PIN for them. - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kSonyHeadphonesAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(1, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.request_pincode_count_); - EXPECT_TRUE(device->IsConnecting()); - - // Set the PIN. - device->SetPinCode("1234"); - message_loop.Run(); - - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - - // Two changes for connecting, one change for connected, one for paired and - // two for trusted (after pairing and connection). - EXPECT_EQ(6, observer.device_changed_count_); - EXPECT_EQ(device, observer.last_device_); - - EXPECT_TRUE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - - EXPECT_TRUE(device->IsPaired()); - - // Verify is not a HID device. - BluetoothDevice::ServiceList uuids = device->GetServices(); - ASSERT_EQ(0U, uuids.size()); - - // Non HID devices are always connectable. - EXPECT_TRUE(device->IsConnectable()); - - // Pairing dialog should be dismissed - EXPECT_EQ(2, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.dismiss_count_); - - // Make sure the trusted property has been set to true. - FakeBluetoothDeviceClient::Properties* properties = - fake_bluetooth_device_client_->GetProperties( - dbus::ObjectPath(FakeBluetoothDeviceClient::kSonyHeadphonesPath)); - EXPECT_TRUE(properties->trusted.value()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairPhone) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevices(); - - // The fake phone requests that we confirm a displayed passkey. - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kPhoneAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - 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_); - EXPECT_TRUE(device->IsConnecting()); - - // Confirm the passkey. - device->ConfirmPairing(); - message_loop.Run(); - - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - - // Two changes for connecting, one change for connected, one for paired and - // two for trusted (after pairing and connection). - EXPECT_EQ(6, observer.device_changed_count_); - EXPECT_EQ(device, observer.last_device_); - - EXPECT_TRUE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - - EXPECT_TRUE(device->IsPaired()); - - // Non HID devices are always connectable. - EXPECT_TRUE(device->IsConnectable()); - - // Pairing dialog should be dismissed - EXPECT_EQ(2, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.dismiss_count_); - - // Make sure the trusted property has been set to true. - FakeBluetoothDeviceClient::Properties* properties = - fake_bluetooth_device_client_->GetProperties( - dbus::ObjectPath(FakeBluetoothDeviceClient::kPhonePath)); - EXPECT_TRUE(properties->trusted.value()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairWeirdDevice) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevices(); - - // Use the "weird device" fake that requires that the user enters a Passkey, - // this would be some kind of device that has a display, but doesn't use - // "just works" - maybe a car? - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kWeirdDeviceAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(1, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.request_passkey_count_); - EXPECT_TRUE(device->IsConnecting()); - - // Set the Passkey. - device->SetPasskey(1234); - message_loop.Run(); - - EXPECT_EQ(1, callback_count_); - EXPECT_EQ(0, error_callback_count_); - - // Two changes for connecting, one change for connected, one for paired and - // two for trusted (after pairing and connection). - EXPECT_EQ(6, observer.device_changed_count_); - EXPECT_EQ(device, observer.last_device_); - - EXPECT_TRUE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - - EXPECT_TRUE(device->IsPaired()); - - // Non HID devices are always connectable. - EXPECT_TRUE(device->IsConnectable()); - - // Pairing dialog should be dismissed - EXPECT_EQ(2, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.dismiss_count_); - - // Make sure the trusted property has been set to true. - FakeBluetoothDeviceClient::Properties* properties = - fake_bluetooth_device_client_->GetProperties( - dbus::ObjectPath(FakeBluetoothDeviceClient::kWeirdDevicePath)); - EXPECT_TRUE(properties->trusted.value()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairUnpairableDeviceFails) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevice(FakeBluetoothDeviceClient::kUnconnectableDeviceAddress); - - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kUnpairableDeviceAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(0, pairing_delegate.call_count_); - EXPECT_TRUE(device->IsConnecting()); - - // Run the loop to get the error.. - message_loop.Run(); - - EXPECT_EQ(0, callback_count_); - EXPECT_EQ(1, error_callback_count_); - - EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_); - - EXPECT_FALSE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - EXPECT_FALSE(device->IsPaired()); - - // Pairing dialog should be dismissed - EXPECT_EQ(1, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.dismiss_count_); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairingFails) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevice(FakeBluetoothDeviceClient::kVanishingDeviceAddress); - - // The vanishing device times out during pairing - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kVanishingDeviceAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(0, pairing_delegate.call_count_); - EXPECT_TRUE(device->IsConnecting()); - - // Run the loop to get the error.. - message_loop.Run(); - - EXPECT_EQ(0, callback_count_); - EXPECT_EQ(1, error_callback_count_); - - EXPECT_EQ(BluetoothDevice::ERROR_AUTH_TIMEOUT, last_connect_error_); - - EXPECT_FALSE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - EXPECT_FALSE(device->IsPaired()); - - // Pairing dialog should be dismissed - EXPECT_EQ(1, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.dismiss_count_); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairingFailsAtConnection) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevices(); - - // Everything seems to go according to plan with the unconnectable device; - // it pairs, but then you can't make connections to it after. - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kUnconnectableDeviceAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(0, pairing_delegate.call_count_); - EXPECT_TRUE(device->IsConnecting()); - - message_loop.Run(); - - EXPECT_EQ(0, callback_count_); - EXPECT_EQ(1, error_callback_count_); - EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_); - - // Two changes for connecting, one for paired and one for trusted after - // pairing. The device should not be connected. - EXPECT_EQ(4, observer.device_changed_count_); - EXPECT_EQ(device, observer.last_device_); - - EXPECT_FALSE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - - EXPECT_TRUE(device->IsPaired()); - - // Pairing dialog should be dismissed - EXPECT_EQ(1, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.dismiss_count_); - - // Make sure the trusted property has been set to true still (since pairing - // worked). - FakeBluetoothDeviceClient::Properties* properties = - fake_bluetooth_device_client_->GetProperties( - dbus::ObjectPath( - FakeBluetoothDeviceClient::kUnconnectableDevicePath)); - EXPECT_TRUE(properties->trusted.value()); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairingRejectedAtPinCode) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevices(); - - // Reject the pairing after we receive a request for the PIN code. - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kSonyHeadphonesAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(1, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.request_pincode_count_); - EXPECT_TRUE(device->IsConnecting()); - - // Reject the pairing. - device->RejectPairing(); - message_loop.Run(); - - EXPECT_EQ(0, callback_count_); - EXPECT_EQ(1, error_callback_count_); - EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, last_connect_error_); - - // Should be no changes except connecting going true and false. - EXPECT_EQ(2, observer.device_changed_count_); - EXPECT_FALSE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - EXPECT_FALSE(device->IsPaired()); - - // Pairing dialog should be dismissed - EXPECT_EQ(2, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.dismiss_count_); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairingCancelledAtPinCode) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevices(); - - // Cancel the pairing after we receive a request for the PIN code. - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kSonyHeadphonesAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(1, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.request_pincode_count_); - EXPECT_TRUE(device->IsConnecting()); - - // Cancel the pairing. - device->CancelPairing(); - message_loop.Run(); - - EXPECT_EQ(0, callback_count_); - EXPECT_EQ(1, error_callback_count_); - EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_); - - // Should be no changes except connecting going true and false. - EXPECT_EQ(2, observer.device_changed_count_); - EXPECT_FALSE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - EXPECT_FALSE(device->IsPaired()); - - // Pairing dialog should be dismissed - EXPECT_EQ(2, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.dismiss_count_); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairingRejectedAtPasskey) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevices(); - - // Reject the pairing after we receive a request for the passkey. - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kWeirdDeviceAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(1, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.request_passkey_count_); - EXPECT_TRUE(device->IsConnecting()); - - // Reject the pairing. - device->RejectPairing(); - message_loop.Run(); - - EXPECT_EQ(0, callback_count_); - EXPECT_EQ(1, error_callback_count_); - EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, last_connect_error_); - - // Should be no changes except connecting going true and false. - EXPECT_EQ(2, observer.device_changed_count_); - EXPECT_FALSE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - EXPECT_FALSE(device->IsPaired()); - - // Pairing dialog should be dismissed - EXPECT_EQ(2, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.dismiss_count_); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairingCancelledAtPasskey) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevices(); - - // Cancel the pairing after we receive a request for the passkey. - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kWeirdDeviceAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(1, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.request_passkey_count_); - EXPECT_TRUE(device->IsConnecting()); - - // Cancel the pairing. - device->CancelPairing(); - message_loop.Run(); - - EXPECT_EQ(0, callback_count_); - EXPECT_EQ(1, error_callback_count_); - EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_); - - // Should be no changes except connecting going true and false. - EXPECT_EQ(2, observer.device_changed_count_); - EXPECT_FALSE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - EXPECT_FALSE(device->IsPaired()); - - // Pairing dialog should be dismissed - EXPECT_EQ(2, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.dismiss_count_); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairingRejectedAtConfirmation) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevices(); - - // Reject the pairing after we receive a request for passkey confirmation. - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kPhoneAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(1, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_); - EXPECT_TRUE(device->IsConnecting()); - - // Reject the pairing. - device->RejectPairing(); - message_loop.Run(); - - EXPECT_EQ(0, callback_count_); - EXPECT_EQ(1, error_callback_count_); - EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, last_connect_error_); - - // Should be no changes except connecting going true and false. - EXPECT_EQ(2, observer.device_changed_count_); - EXPECT_FALSE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - EXPECT_FALSE(device->IsPaired()); - - // Pairing dialog should be dismissed - EXPECT_EQ(2, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.dismiss_count_); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairingCancelledAtConfirmation) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevices(); - - // Cancel the pairing after we receive a request for the passkey. - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kPhoneAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(1, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_); - EXPECT_TRUE(device->IsConnecting()); - - // Cancel the pairing. - device->CancelPairing(); - message_loop.Run(); - - EXPECT_EQ(0, callback_count_); - EXPECT_EQ(1, error_callback_count_); - EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_); - - // Should be no changes except connecting going true and false. - EXPECT_EQ(2, observer.device_changed_count_); - EXPECT_FALSE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - EXPECT_FALSE(device->IsPaired()); - - // Pairing dialog should be dismissed - EXPECT_EQ(2, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.dismiss_count_); -} - -TEST_F(BluetoothExperimentalChromeOSTest, PairingCancelledInFlight) { - base::MessageLoop message_loop(base::MessageLoop::TYPE_DEFAULT); - fake_bluetooth_device_client_->SetSimulationIntervalMs(10); - - GetAdapter(); - DiscoverDevices(); - - // Cancel the pairing while we're waiting for the remote host. - BluetoothDevice* device = adapter_->GetDevice( - FakeBluetoothDeviceClient::kAppleMouseAddress); - ASSERT_TRUE(device != NULL); - ASSERT_FALSE(device->IsPaired()); - - TestObserver observer(adapter_); - adapter_->AddObserver(&observer); - - TestPairingDelegate pairing_delegate; - device->Connect( - &pairing_delegate, - base::Bind(&BluetoothExperimentalChromeOSTest::Callback, - base::Unretained(this)), - base::Bind(&BluetoothExperimentalChromeOSTest::ConnectErrorCallback, - base::Unretained(this))); - - EXPECT_EQ(0, pairing_delegate.call_count_); - EXPECT_TRUE(device->IsConnecting()); - - // Cancel the pairing. - device->CancelPairing(); - message_loop.Run(); - - EXPECT_EQ(0, callback_count_); - EXPECT_EQ(1, error_callback_count_); - EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_); - - // Should be no changes except connecting going true and false. - EXPECT_EQ(2, observer.device_changed_count_); - EXPECT_FALSE(device->IsConnected()); - EXPECT_FALSE(device->IsConnecting()); - EXPECT_FALSE(device->IsPaired()); - - // Pairing dialog should be dismissed - EXPECT_EQ(1, pairing_delegate.call_count_); - EXPECT_EQ(1, pairing_delegate.dismiss_count_); -} - -} // namespace chromeos diff --git a/device/bluetooth/bluetooth_profile.cc b/device/bluetooth/bluetooth_profile.cc index 6c59e5b..0d9a757 100644 --- a/device/bluetooth/bluetooth_profile.cc +++ b/device/bluetooth/bluetooth_profile.cc @@ -5,7 +5,7 @@ #include "device/bluetooth/bluetooth_profile.h" #if defined(OS_CHROMEOS) -#include "device/bluetooth/bluetooth_profile_experimental_chromeos.h" +#include "device/bluetooth/bluetooth_profile_chromeos.h" #elif defined(OS_MACOSX) #include "base/mac/mac_util.h" #include "device/bluetooth/bluetooth_profile_mac.h" @@ -46,8 +46,8 @@ void BluetoothProfile::Register(const std::string& uuid, const Options& options, const ProfileCallback& callback) { #if defined(OS_CHROMEOS) - chromeos::BluetoothProfileExperimentalChromeOS* profile = NULL; - profile = new chromeos::BluetoothProfileExperimentalChromeOS(); + chromeos::BluetoothProfileChromeOS* profile = NULL; + profile = new chromeos::BluetoothProfileChromeOS(); profile->Init(uuid, options, callback); #elif defined(OS_MACOSX) BluetoothProfile* profile = NULL; diff --git a/device/bluetooth/bluetooth_profile_chromeos.cc b/device/bluetooth/bluetooth_profile_chromeos.cc new file mode 100644 index 0000000..f4b8ccc --- /dev/null +++ b/device/bluetooth/bluetooth_profile_chromeos.cc @@ -0,0 +1,253 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/bluetooth/bluetooth_profile_chromeos.h" + +#include + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/message_loop.h" +#include "base/strings/string_util.h" +#include "base/task_runner_util.h" +#include "base/threading/thread_restrictions.h" +#include "base/threading/worker_pool.h" +#include "chromeos/dbus/bluetooth_profile_manager_client.h" +#include "chromeos/dbus/bluetooth_profile_service_provider.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "dbus/bus.h" +#include "dbus/file_descriptor.h" +#include "dbus/object_path.h" +#include "device/bluetooth/bluetooth_adapter_chromeos.h" +#include "device/bluetooth/bluetooth_adapter_factory.h" +#include "device/bluetooth/bluetooth_device.h" +#include "device/bluetooth/bluetooth_device_chromeos.h" +#include "device/bluetooth/bluetooth_profile.h" +#include "device/bluetooth/bluetooth_socket.h" +#include "device/bluetooth/bluetooth_socket_chromeos.h" + +using device::BluetoothAdapter; +using device::BluetoothAdapterFactory; +using device::BluetoothDevice; +using device::BluetoothProfile; +using device::BluetoothSocket; + +namespace { + +// Check the validity of a file descriptor received from D-Bus. Must be run +// on a thread where i/o is permitted. +scoped_ptr CheckValidity( + scoped_ptr fd) { + base::ThreadRestrictions::AssertIOAllowed(); + fd->CheckValidity(); + return fd.Pass(); +} + +} // namespace + + +namespace chromeos { + +BluetoothProfileChromeOS::BluetoothProfileChromeOS() + : weak_ptr_factory_(this) { +} + +BluetoothProfileChromeOS::~BluetoothProfileChromeOS() { + DCHECK(object_path_.value().empty()); + DCHECK(profile_.get() == NULL); +} + +void BluetoothProfileChromeOS::Init( + const std::string& uuid, + const device::BluetoothProfile::Options& options, + const ProfileCallback& callback) { + DCHECK(object_path_.value().empty()); + DCHECK(profile_.get() == NULL); + + if (!BluetoothDevice::IsUUIDValid(uuid)) { + callback.Run(NULL); + return; + } + + uuid_ = uuid; + + BluetoothProfileManagerClient::Options bluetooth_options; + bluetooth_options.name = options.name; + bluetooth_options.service = uuid; + bluetooth_options.channel = options.channel; + bluetooth_options.psm = options.psm; + bluetooth_options.require_authentication = options.require_authentication; + bluetooth_options.require_authorization = options.require_authorization; + bluetooth_options.auto_connect = options.auto_connect; + bluetooth_options.version = options.version; + bluetooth_options.features = options.features; + + // The object path is relatively meaningless, but has to be unique, so we + // use the UUID of the profile. + std::string uuid_path; + ReplaceChars(uuid, ":-", "_", &uuid_path); + + object_path_ = dbus::ObjectPath("/org/chromium/bluetooth_profile/" + + uuid_path); + + dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus(); + profile_.reset(BluetoothProfileServiceProvider::Create( + system_bus, object_path_, this)); + DCHECK(profile_.get()); + + VLOG(1) << object_path_.value() << ": Register profile"; + DBusThreadManager::Get()->GetBluetoothProfileManagerClient()-> + RegisterProfile( + object_path_, + uuid, + bluetooth_options, + base::Bind(&BluetoothProfileChromeOS::OnRegisterProfile, + weak_ptr_factory_.GetWeakPtr(), + callback), + base::Bind(&BluetoothProfileChromeOS::OnRegisterProfileError, + weak_ptr_factory_.GetWeakPtr(), + callback)); +} + +void BluetoothProfileChromeOS::Unregister() { + DCHECK(!object_path_.value().empty()); + DCHECK(profile_.get()); + + profile_.reset(); + + VLOG(1) << object_path_.value() << ": Unregister profile"; + DBusThreadManager::Get()->GetBluetoothProfileManagerClient()-> + UnregisterProfile( + object_path_, + base::Bind(&BluetoothProfileChromeOS::OnUnregisterProfile, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothProfileChromeOS::OnUnregisterProfileError, + weak_ptr_factory_.GetWeakPtr())); +} + +void BluetoothProfileChromeOS::SetConnectionCallback( + const ConnectionCallback& callback) { + connection_callback_ = callback; +} + +void BluetoothProfileChromeOS::Release() { + VLOG(1) << object_path_.value() << ": Release"; +} + +void BluetoothProfileChromeOS::NewConnection( + const dbus::ObjectPath& device_path, + scoped_ptr fd, + const BluetoothProfileServiceProvider::Delegate::Options& options, + const ConfirmationCallback& callback) { + VLOG(1) << object_path_.value() << ": New connection from device: " + << device_path.value();; + if (connection_callback_.is_null()) { + callback.Run(REJECTED); + return; + } + + // Punt descriptor validity check to a worker thread where i/o is permitted; + // on return we'll fetch the adapter and then call the connection callback. + // + // base::Passed is used to take ownership of the file descriptor during the + // CheckValidity() call and pass that ownership to the GetAdapter() call. + base::PostTaskAndReplyWithResult( + base::WorkerPool::GetTaskRunner(false), + FROM_HERE, + base::Bind(&CheckValidity, base::Passed(&fd)), + base::Bind(&BluetoothProfileChromeOS::GetAdapter, + weak_ptr_factory_.GetWeakPtr(), + device_path, + options, + callback)); +} + +void BluetoothProfileChromeOS::RequestDisconnection( + const dbus::ObjectPath& device_path, + const ConfirmationCallback& callback) { + VLOG(1) << object_path_.value() << ": Request disconnection"; + callback.Run(SUCCESS); +} + +void BluetoothProfileChromeOS::Cancel() { + VLOG(1) << object_path_.value() << ": Cancel"; +} + +void BluetoothProfileChromeOS::OnRegisterProfile( + const ProfileCallback& callback) { + VLOG(1) << object_path_.value() << ": Profile registered"; + callback.Run(this); +} + +void BluetoothProfileChromeOS::OnRegisterProfileError( + const ProfileCallback& callback, + const std::string& error_name, + const std::string& error_message) { + LOG(WARNING) << object_path_.value() << ": Failed to register profile: " + << error_name << ": " << error_message; + callback.Run(NULL); + + Unregister(); +} + +void BluetoothProfileChromeOS::OnUnregisterProfile() { + VLOG(1) << object_path_.value() << ": Profile unregistered"; + object_path_ = dbus::ObjectPath(""); + delete this; +} + +void BluetoothProfileChromeOS::OnUnregisterProfileError( + const std::string& error_name, + const std::string& error_message) { + LOG(WARNING) << object_path_.value() << ": Failed to unregister profile: " + << error_name << ": " << error_message; + object_path_ = dbus::ObjectPath(""); + delete this; +} + +void BluetoothProfileChromeOS::GetAdapter( + const dbus::ObjectPath& device_path, + const BluetoothProfileServiceProvider::Delegate::Options& options, + const ConfirmationCallback& callback, + scoped_ptr fd) { + VLOG(1) << object_path_.value() << ": Validity check complete"; + if (!fd->is_valid()) { + callback.Run(REJECTED); + return; + } + + BluetoothAdapterFactory::GetAdapter( + base::Bind(&BluetoothProfileChromeOS::OnGetAdapter, + weak_ptr_factory_.GetWeakPtr(), + device_path, + options, + callback, + base::Passed(&fd))); +} + +void BluetoothProfileChromeOS::OnGetAdapter( + const dbus::ObjectPath& device_path, + const BluetoothProfileServiceProvider::Delegate::Options& + options, + const ConfirmationCallback& callback, + scoped_ptr fd, + scoped_refptr adapter) { + VLOG(1) << object_path_.value() << ": Obtained adapter reference"; + callback.Run(SUCCESS); + + BluetoothDeviceChromeOS* device = + static_cast(adapter.get())-> + GetDeviceWithPath(device_path); + DCHECK(device); + + scoped_refptr socket(( + BluetoothSocketChromeOS::Create(fd.get()))); + connection_callback_.Run(device, socket); +} + +} // namespace chromeos diff --git a/device/bluetooth/bluetooth_profile_chromeos.h b/device/bluetooth/bluetooth_profile_chromeos.h new file mode 100644 index 0000000..c13951e --- /dev/null +++ b/device/bluetooth/bluetooth_profile_chromeos.h @@ -0,0 +1,127 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_CHROMEOS_H_ +#define DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_CHROMEOS_H_ + +#include + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_profile_service_provider.h" +#include "dbus/object_path.h" +#include "device/bluetooth/bluetooth_profile.h" + +namespace dbus { + +class FileDescriptor; + +} // namespace dbus + +namespace device { + +class BluetoothAdapter; + +} // namespace device + +namespace chromeos { + +// The BluetoothProfileChromeOS class implements BluetoothProfile for the +// Chrome OS platform. +class CHROMEOS_EXPORT BluetoothProfileChromeOS + : public device::BluetoothProfile, + private BluetoothProfileServiceProvider::Delegate { + public: + // BluetoothProfile override. + virtual void Unregister() OVERRIDE; + virtual void SetConnectionCallback( + const ConnectionCallback& callback) OVERRIDE; + + // Return the UUID of the profile. + const std::string& uuid() const { return uuid_; } + + private: + friend class BluetoothProfile; + + BluetoothProfileChromeOS(); + virtual ~BluetoothProfileChromeOS(); + + // Called by BluetoothProfile::Register to initialize the profile object + // asynchronously. |uuid|, |options| and |callback| are the arguments to + // BluetoothProfile::Register. + void Init(const std::string& uuid, + const device::BluetoothProfile::Options& options, + const ProfileCallback& callback); + + // BluetoothProfileServiceProvider::Delegate override. + virtual void Release() OVERRIDE; + virtual void NewConnection( + const dbus::ObjectPath& device_path, + scoped_ptr fd, + const BluetoothProfileServiceProvider::Delegate::Options& options, + const ConfirmationCallback& callback) OVERRIDE; + virtual void RequestDisconnection( + const dbus::ObjectPath& device_path, + const ConfirmationCallback& callback) OVERRIDE; + virtual void Cancel() OVERRIDE; + + // Called by dbus:: on completion of the D-Bus method call to register the + // profile object. + void OnRegisterProfile(const ProfileCallback& callback); + void OnRegisterProfileError(const ProfileCallback& callback, + const std::string& error_name, + const std::string& error_message); + + // Called by dbus:: on completion of the D-Bus method call to unregister + // the profile object. + void OnUnregisterProfile(); + void OnUnregisterProfileError(const std::string& error_name, + const std::string& error_message); + + // Method run once the file descriptor has been validated in order to get + // the default adapter, and method run once the default adapter has been + // obtained in order to get the device object to be passed to the connection + // callback. + // + // The |fd| argument is moved compared to the NewConnection() call since it + // becomes the result of a PostTaskAndReplyWithResult() call. + void GetAdapter( + const dbus::ObjectPath& device_path, + const BluetoothProfileServiceProvider::Delegate::Options& options, + const ConfirmationCallback& callback, + scoped_ptr fd); + void OnGetAdapter( + const dbus::ObjectPath& device_path, + const BluetoothProfileServiceProvider::Delegate::Options& options, + const ConfirmationCallback& callback, + scoped_ptr fd, + scoped_refptr); + + // UUID of the profile passed during initialization. + std::string uuid_; + + // Object path of the local profile D-Bus object. + dbus::ObjectPath object_path_; + + // Local profile D-Bus object used for receiving profile delegate methods + // from BlueZ. + scoped_ptr profile_; + + // Callback used on both outgoing and incoming connections to pass the + // connected socket to profile object owner. + ConnectionCallback connection_callback_; + + // Note: This should remain the last member so it'll be destroyed and + // invalidate its weak pointers before any other members are destroyed. + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothProfileChromeOS); +}; + +} // namespace chromeos + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_CHROMEOS_H_ diff --git a/device/bluetooth/bluetooth_profile_chromeos_unittest.cc b/device/bluetooth/bluetooth_profile_chromeos_unittest.cc index 140ba2d..271fe46 100644 --- a/device/bluetooth/bluetooth_profile_chromeos_unittest.cc +++ b/device/bluetooth/bluetooth_profile_chromeos_unittest.cc @@ -8,14 +8,14 @@ #include "chromeos/dbus/fake_bluetooth_profile_service_provider.h" #include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h" #include "device/bluetooth/bluetooth_adapter.h" -#include "device/bluetooth/bluetooth_adapter_experimental_chromeos.h" +#include "device/bluetooth/bluetooth_adapter_chromeos.h" #include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/bluetooth_device.h" -#include "device/bluetooth/bluetooth_device_experimental_chromeos.h" +#include "device/bluetooth/bluetooth_device_chromeos.h" #include "device/bluetooth/bluetooth_profile.h" -#include "device/bluetooth/bluetooth_profile_experimental_chromeos.h" +#include "device/bluetooth/bluetooth_profile_chromeos.h" #include "device/bluetooth/bluetooth_socket.h" -#include "device/bluetooth/bluetooth_socket_experimental_chromeos.h" +#include "device/bluetooth/bluetooth_socket_chromeos.h" #include "net/base/io_buffer.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/device/bluetooth/bluetooth_profile_experimental_chromeos.cc b/device/bluetooth/bluetooth_profile_experimental_chromeos.cc deleted file mode 100644 index 57a6a43..0000000 --- a/device/bluetooth/bluetooth_profile_experimental_chromeos.cc +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "device/bluetooth/bluetooth_profile_experimental_chromeos.h" - -#include - -#include "base/basictypes.h" -#include "base/bind.h" -#include "base/callback.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop.h" -#include "base/strings/string_util.h" -#include "base/task_runner_util.h" -#include "base/threading/thread_restrictions.h" -#include "base/threading/worker_pool.h" -#include "chromeos/dbus/dbus_thread_manager.h" -#include "chromeos/dbus/experimental_bluetooth_profile_manager_client.h" -#include "chromeos/dbus/experimental_bluetooth_profile_service_provider.h" -#include "dbus/bus.h" -#include "dbus/file_descriptor.h" -#include "dbus/object_path.h" -#include "device/bluetooth/bluetooth_adapter_experimental_chromeos.h" -#include "device/bluetooth/bluetooth_adapter_factory.h" -#include "device/bluetooth/bluetooth_device.h" -#include "device/bluetooth/bluetooth_device_experimental_chromeos.h" -#include "device/bluetooth/bluetooth_profile.h" -#include "device/bluetooth/bluetooth_socket.h" -#include "device/bluetooth/bluetooth_socket_experimental_chromeos.h" - -using device::BluetoothAdapter; -using device::BluetoothAdapterFactory; -using device::BluetoothDevice; -using device::BluetoothProfile; -using device::BluetoothSocket; - -namespace { - -// Check the validity of a file descriptor received from D-Bus. Must be run -// on a thread where i/o is permitted. -scoped_ptr CheckValidity( - scoped_ptr fd) { - base::ThreadRestrictions::AssertIOAllowed(); - fd->CheckValidity(); - return fd.Pass(); -} - -} // namespace - - -namespace chromeos { - -BluetoothProfileExperimentalChromeOS::BluetoothProfileExperimentalChromeOS() - : weak_ptr_factory_(this) { -} - -BluetoothProfileExperimentalChromeOS::~BluetoothProfileExperimentalChromeOS() { - DCHECK(object_path_.value().empty()); - DCHECK(profile_.get() == NULL); -} - -void BluetoothProfileExperimentalChromeOS::Init( - const std::string& uuid, - const device::BluetoothProfile::Options& options, - const ProfileCallback& callback) { - DCHECK(object_path_.value().empty()); - DCHECK(profile_.get() == NULL); - - if (!BluetoothDevice::IsUUIDValid(uuid)) { - callback.Run(NULL); - return; - } - - uuid_ = uuid; - - ExperimentalBluetoothProfileManagerClient::Options bluetooth_options; - bluetooth_options.name = options.name; - bluetooth_options.service = uuid; - bluetooth_options.channel = options.channel; - bluetooth_options.psm = options.psm; - bluetooth_options.require_authentication = options.require_authentication; - bluetooth_options.require_authorization = options.require_authorization; - bluetooth_options.auto_connect = options.auto_connect; - bluetooth_options.version = options.version; - bluetooth_options.features = options.features; - - // The object path is relatively meaningless, but has to be unique, so we - // use the UUID of the profile. - std::string uuid_path; - ReplaceChars(uuid, ":-", "_", &uuid_path); - - object_path_ = dbus::ObjectPath("/org/chromium/bluetooth_profile/" + - uuid_path); - - dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus(); - profile_.reset(ExperimentalBluetoothProfileServiceProvider::Create( - system_bus, object_path_, this)); - DCHECK(profile_.get()); - - VLOG(1) << object_path_.value() << ": Register profile"; - DBusThreadManager::Get()->GetExperimentalBluetoothProfileManagerClient()-> - RegisterProfile( - object_path_, - uuid, - bluetooth_options, - base::Bind( - &BluetoothProfileExperimentalChromeOS::OnRegisterProfile, - weak_ptr_factory_.GetWeakPtr(), - callback), - base::Bind( - &BluetoothProfileExperimentalChromeOS::OnRegisterProfileError, - weak_ptr_factory_.GetWeakPtr(), - callback)); -} - -void BluetoothProfileExperimentalChromeOS::Unregister() { - DCHECK(!object_path_.value().empty()); - DCHECK(profile_.get()); - - profile_.reset(); - - VLOG(1) << object_path_.value() << ": Unregister profile"; - DBusThreadManager::Get()->GetExperimentalBluetoothProfileManagerClient()-> - UnregisterProfile( - object_path_, - base::Bind( - &BluetoothProfileExperimentalChromeOS::OnUnregisterProfile, - weak_ptr_factory_.GetWeakPtr()), - base::Bind( - &BluetoothProfileExperimentalChromeOS::OnUnregisterProfileError, - weak_ptr_factory_.GetWeakPtr())); -} - -void BluetoothProfileExperimentalChromeOS::SetConnectionCallback( - const ConnectionCallback& callback) { - connection_callback_ = callback; -} - -void BluetoothProfileExperimentalChromeOS::Release() { - VLOG(1) << object_path_.value() << ": Release"; -} - -void BluetoothProfileExperimentalChromeOS::NewConnection( - const dbus::ObjectPath& device_path, - scoped_ptr fd, - const ExperimentalBluetoothProfileServiceProvider::Delegate::Options& - options, - const ConfirmationCallback& callback) { - VLOG(1) << object_path_.value() << ": New connection from device: " - << device_path.value();; - if (connection_callback_.is_null()) { - callback.Run(REJECTED); - return; - } - - // Punt descriptor validity check to a worker thread where i/o is permitted; - // on return we'll fetch the adapter and then call the connection callback. - // - // base::Passed is used to take ownership of the file descriptor during the - // CheckValidity() call and pass that ownership to the GetAdapter() call. - base::PostTaskAndReplyWithResult( - base::WorkerPool::GetTaskRunner(false), - FROM_HERE, - base::Bind(&CheckValidity, base::Passed(&fd)), - base::Bind(&BluetoothProfileExperimentalChromeOS::GetAdapter, - weak_ptr_factory_.GetWeakPtr(), - device_path, - options, - callback)); -} - -void BluetoothProfileExperimentalChromeOS::RequestDisconnection( - const dbus::ObjectPath& device_path, - const ConfirmationCallback& callback) { - VLOG(1) << object_path_.value() << ": Request disconnection"; - callback.Run(SUCCESS); -} - -void BluetoothProfileExperimentalChromeOS::Cancel() { - VLOG(1) << object_path_.value() << ": Cancel"; -} - -void BluetoothProfileExperimentalChromeOS::OnRegisterProfile( - const ProfileCallback& callback) { - VLOG(1) << object_path_.value() << ": Profile registered"; - callback.Run(this); -} - -void BluetoothProfileExperimentalChromeOS::OnRegisterProfileError( - const ProfileCallback& callback, - const std::string& error_name, - const std::string& error_message) { - LOG(WARNING) << object_path_.value() << ": Failed to register profile: " - << error_name << ": " << error_message; - callback.Run(NULL); - - Unregister(); -} - -void BluetoothProfileExperimentalChromeOS::OnUnregisterProfile() { - VLOG(1) << object_path_.value() << ": Profile unregistered"; - object_path_ = dbus::ObjectPath(""); - delete this; -} - -void BluetoothProfileExperimentalChromeOS::OnUnregisterProfileError( - const std::string& error_name, - const std::string& error_message) { - LOG(WARNING) << object_path_.value() << ": Failed to unregister profile: " - << error_name << ": " << error_message; - object_path_ = dbus::ObjectPath(""); - delete this; -} - -void BluetoothProfileExperimentalChromeOS::GetAdapter( - const dbus::ObjectPath& device_path, - const ExperimentalBluetoothProfileServiceProvider::Delegate::Options& - options, - const ConfirmationCallback& callback, - scoped_ptr fd) { - VLOG(1) << object_path_.value() << ": Validity check complete"; - if (!fd->is_valid()) { - callback.Run(REJECTED); - return; - } - - BluetoothAdapterFactory::GetAdapter( - base::Bind(&BluetoothProfileExperimentalChromeOS::OnGetAdapter, - weak_ptr_factory_.GetWeakPtr(), - device_path, - options, - callback, - base::Passed(&fd))); -} - -void BluetoothProfileExperimentalChromeOS::OnGetAdapter( - const dbus::ObjectPath& device_path, - const ExperimentalBluetoothProfileServiceProvider::Delegate::Options& - options, - const ConfirmationCallback& callback, - scoped_ptr fd, - scoped_refptr adapter) { - VLOG(1) << object_path_.value() << ": Obtained adapter reference"; - callback.Run(SUCCESS); - - BluetoothDeviceExperimentalChromeOS* device = - static_cast(adapter.get())-> - GetDeviceWithPath(device_path); - DCHECK(device); - - scoped_refptr socket(( - BluetoothSocketExperimentalChromeOS::Create(fd.get()))); - connection_callback_.Run(device, socket); -} - -} // namespace chromeos diff --git a/device/bluetooth/bluetooth_profile_experimental_chromeos.h b/device/bluetooth/bluetooth_profile_experimental_chromeos.h deleted file mode 100644 index 61d4c44..0000000 --- a/device/bluetooth/bluetooth_profile_experimental_chromeos.h +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_EXPERIMENTAL_CHROMEOS_H_ -#define DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_EXPERIMENTAL_CHROMEOS_H_ - -#include - -#include "base/basictypes.h" -#include "base/callback.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "chromeos/chromeos_export.h" -#include "chromeos/dbus/experimental_bluetooth_profile_service_provider.h" -#include "dbus/object_path.h" -#include "device/bluetooth/bluetooth_profile.h" - -namespace dbus { - -class FileDescriptor; - -} // namespace dbus - -namespace device { - -class BluetoothAdapter; - -} // namespace device - -namespace chromeos { - -// The BluetoothProfileExperimentalChromeOS class implements BluetoothProfile -// for the Chrome OS platform using the Bluetooth Smart capable backend (there -// is no implementation for the older backend). It will be renamed to -// BluetoothProfileChromeOS, once the backend is the sole implementation. -class CHROMEOS_EXPORT BluetoothProfileExperimentalChromeOS - : public device::BluetoothProfile, - private ExperimentalBluetoothProfileServiceProvider::Delegate { - public: - // BluetoothProfile override. - virtual void Unregister() OVERRIDE; - virtual void SetConnectionCallback( - const ConnectionCallback& callback) OVERRIDE; - - // Return the UUID of the profile. - const std::string& uuid() const { return uuid_; } - - private: - friend class BluetoothProfile; - - BluetoothProfileExperimentalChromeOS(); - virtual ~BluetoothProfileExperimentalChromeOS(); - - // Called by BluetoothProfile::Register to initialize the profile object - // asynchronously. |uuid|, |options| and |callback| are the arguments to - // BluetoothProfile::Register. - void Init(const std::string& uuid, - const device::BluetoothProfile::Options& options, - const ProfileCallback& callback); - - // ExperimentalBluetoothProfileServiceProvider::Delegate override. - virtual void Release() OVERRIDE; - virtual void NewConnection( - const dbus::ObjectPath& device_path, - scoped_ptr fd, - const ExperimentalBluetoothProfileServiceProvider::Delegate::Options& - options, - const ConfirmationCallback& callback) OVERRIDE; - virtual void RequestDisconnection( - const dbus::ObjectPath& device_path, - const ConfirmationCallback& callback) OVERRIDE; - virtual void Cancel() OVERRIDE; - - // Called by dbus:: on completion of the D-Bus method call to register the - // profile object. - void OnRegisterProfile(const ProfileCallback& callback); - void OnRegisterProfileError(const ProfileCallback& callback, - const std::string& error_name, - const std::string& error_message); - - // Called by dbus:: on completion of the D-Bus method call to unregister - // the profile object. - void OnUnregisterProfile(); - void OnUnregisterProfileError(const std::string& error_name, - const std::string& error_message); - - // Method run once the file descriptor has been validated in order to get - // the default adapter, and method run once the default adapter has been - // obtained in order to get the device object to be passed to the connection - // callback. - // - // The |fd| argument is moved compared to the NewConnection() call since it - // becomes the result of a PostTaskAndReplyWithResult() call. - void GetAdapter( - const dbus::ObjectPath& device_path, - const ExperimentalBluetoothProfileServiceProvider::Delegate::Options& - options, - const ConfirmationCallback& callback, - scoped_ptr fd); - void OnGetAdapter( - const dbus::ObjectPath& device_path, - const ExperimentalBluetoothProfileServiceProvider::Delegate::Options& - options, - const ConfirmationCallback& callback, - scoped_ptr fd, - scoped_refptr); - - // UUID of the profile passed during initialization. - std::string uuid_; - - // Object path of the local profile D-Bus object. - dbus::ObjectPath object_path_; - - // Local profile D-Bus object used for receiving profile delegate methods - // from BlueZ. - scoped_ptr profile_; - - // Callback used on both outgoing and incoming connections to pass the - // connected socket to profile object owner. - ConnectionCallback connection_callback_; - - // Note: This should remain the last member so it'll be destroyed and - // invalidate its weak pointers before any other members are destroyed. - base::WeakPtrFactory weak_ptr_factory_; - - DISALLOW_COPY_AND_ASSIGN(BluetoothProfileExperimentalChromeOS); -}; - -} // namespace chromeos - -#endif // DEVICE_BLUETOOTH_BLUETOOTH_PROFILE_EXPERIMENTAL_CHROMEOS_H_ diff --git a/device/bluetooth/bluetooth_socket_chromeos.cc b/device/bluetooth/bluetooth_socket_chromeos.cc new file mode 100644 index 0000000..81395dc --- /dev/null +++ b/device/bluetooth/bluetooth_socket_chromeos.cc @@ -0,0 +1,167 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/bluetooth/bluetooth_socket_chromeos.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/posix/eintr_wrapper.h" +#include "base/safe_strerror_posix.h" +#include "base/threading/thread_restrictions.h" +#include "dbus/file_descriptor.h" +#include "device/bluetooth/bluetooth_socket.h" +#include "net/base/io_buffer.h" + +namespace chromeos { + +BluetoothSocketChromeOS::BluetoothSocketChromeOS(int fd) + : fd_(fd) { + // Fetch the socket type so we read from it correctly. + int optval; + socklen_t opt_len = sizeof optval; + if (getsockopt(fd_, SOL_SOCKET, SO_TYPE, &optval, &opt_len) < 0) { + // Sequenced packet is the safest assumption since it won't result in + // truncated packets. + LOG(WARNING) << "Unable to get socket type: " << safe_strerror(errno); + optval = SOCK_SEQPACKET; + } + + if (optval == SOCK_DGRAM || optval == SOCK_SEQPACKET) { + socket_type_ = L2CAP; + } else { + socket_type_ = RFCOMM; + } +} + +BluetoothSocketChromeOS::~BluetoothSocketChromeOS() { + HANDLE_EINTR(close(fd_)); +} + +bool BluetoothSocketChromeOS::Receive(net::GrowableIOBuffer *buffer) { + base::ThreadRestrictions::AssertIOAllowed(); + + if (socket_type_ == L2CAP) { + int count; + if (ioctl(fd_, FIONREAD, &count) < 0) { + error_message_ = safe_strerror(errno); + LOG(WARNING) << "Unable to get waiting data size: " << error_message_; + return true; + } + + // No bytes waiting can mean either nothing to read, or the other end has + // been closed, and reading zero bytes always returns zero. + // + // We can't do a short read for fear of a race where data arrives between + // calls and we trunctate it. So use poll() to check for the POLLHUP flag. + if (count == 0) { + struct pollfd pollfd; + + pollfd.fd = fd_; + pollfd.events = 0; + pollfd.revents = 0; + + // Timeout parameter set to 0 so this call will not block. + if (HANDLE_EINTR(poll(&pollfd, 1, 0)) < 0) { + error_message_ = safe_strerror(errno); + LOG(WARNING) << "Unable to check whether socket is closed: " + << error_message_; + return false; + } + + if (pollfd.revents & POLLHUP) { + // TODO(keybuk, youngki): Agree a common way to flag disconnected. + error_message_ = "Disconnected"; + return false; + } + } + + buffer->SetCapacity(count); + } else { + buffer->SetCapacity(1024); + } + + ssize_t bytes_read; + do { + if (buffer->RemainingCapacity() == 0) + buffer->SetCapacity(buffer->capacity() * 2); + bytes_read = + HANDLE_EINTR(read(fd_, buffer->data(), buffer->RemainingCapacity())); + if (bytes_read > 0) + buffer->set_offset(buffer->offset() + bytes_read); + } while (socket_type_ == RFCOMM && bytes_read > 0); + + // Ignore an error if at least one read() call succeeded; it'll be returned + // the next read() call. + if (buffer->offset() > 0) + return true; + + if (bytes_read < 0) { + if (errno == ECONNRESET || errno == ENOTCONN) { + // TODO(keybuk, youngki): Agree a common way to flag disconnected. + error_message_ = "Disconnected"; + return false; + } else if (errno != EAGAIN && errno != EWOULDBLOCK) { + error_message_ = safe_strerror(errno); + return false; + } + } + + if (bytes_read == 0 && socket_type_ == RFCOMM) { + // TODO(keybuk, youngki): Agree a common way to flag disconnected. + error_message_ = "Disconnected"; + return false; + } + + return true; +} + +bool BluetoothSocketChromeOS::Send(net::DrainableIOBuffer *buffer) { + base::ThreadRestrictions::AssertIOAllowed(); + + ssize_t bytes_written; + do { + bytes_written = + HANDLE_EINTR(write(fd_, buffer->data(), buffer->BytesRemaining())); + if (bytes_written > 0) + buffer->DidConsume(bytes_written); + } while (buffer->BytesRemaining() > 0 && bytes_written > 0); + + if (bytes_written < 0) { + if (errno == EPIPE || errno == ECONNRESET || errno == ENOTCONN) { + // TODO(keybuk, youngki): Agree a common way to flag disconnected. + error_message_ = "Disconnected"; + return false; + } else if (errno != EAGAIN && errno != EWOULDBLOCK) { + error_message_ = safe_strerror(errno); + return false; + } + } + + return true; +} + +std::string BluetoothSocketChromeOS::GetLastErrorMessage() const { + return error_message_; +} + +// static +scoped_refptr BluetoothSocketChromeOS::Create( + dbus::FileDescriptor* fd) { + DCHECK(fd->is_valid()); + + BluetoothSocketChromeOS* bluetooth_socket = + new BluetoothSocketChromeOS(fd->TakeValue());; + return scoped_refptr(bluetooth_socket); +} + +} // namespace chromeos diff --git a/device/bluetooth/bluetooth_socket_chromeos.h b/device/bluetooth/bluetooth_socket_chromeos.h new file mode 100644 index 0000000..1c52c67 --- /dev/null +++ b/device/bluetooth/bluetooth_socket_chromeos.h @@ -0,0 +1,73 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_CHROMEOS_H_ +#define DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_CHROMEOS_H_ + +#include + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "chromeos/chromeos_export.h" +#include "device/bluetooth/bluetooth_socket.h" + +namespace dbus { + +class FileDescriptor; + +} // namespace dbus + +namespace net { + +class DrainableIOBuffer; +class GrowableIOBuffer; + +} // namespace net + +namespace chromeos { + +// The BluetoothSocketChromeOS class implements BluetoothSocket for the +// Chrome OS platform. +class CHROMEOS_EXPORT BluetoothSocketChromeOS + : public device::BluetoothSocket { + public: + // BluetoothSocket override. + virtual bool Receive(net::GrowableIOBuffer* buffer) OVERRIDE; + virtual bool Send(net::DrainableIOBuffer* buffer) OVERRIDE; + virtual std::string GetLastErrorMessage() const OVERRIDE; + + // Create an instance of a BluetoothSocket from the passed file descriptor + // received over D-Bus in |fd|, the descriptor will be taken from that object + // and ownership passed to the returned object. + static scoped_refptr Create( + dbus::FileDescriptor* fd); + + protected: + virtual ~BluetoothSocketChromeOS(); + + private: + BluetoothSocketChromeOS(int fd); + + // The different socket types have different reading patterns; l2cap sockets + // have to be read with boundaries between datagrams preserved while rfcomm + // sockets do not. + enum SocketType { + L2CAP, + RFCOMM + }; + + // File descriptor and socket type of the socket. + const int fd_; + SocketType socket_type_; + + // Last error message, set during Receive() and Send() and retrieved using + // GetLastErrorMessage(). + std::string error_message_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothSocketChromeOS); +}; + +} // namespace chromeos + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_CHROMEOS_H_ diff --git a/device/bluetooth/bluetooth_socket_experimental_chromeos.cc b/device/bluetooth/bluetooth_socket_experimental_chromeos.cc deleted file mode 100644 index 21d281a..0000000 --- a/device/bluetooth/bluetooth_socket_experimental_chromeos.cc +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "device/bluetooth/bluetooth_socket_experimental_chromeos.h" - -#include -#include -#include -#include -#include -#include - -#include - -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/posix/eintr_wrapper.h" -#include "base/safe_strerror_posix.h" -#include "base/threading/thread_restrictions.h" -#include "dbus/file_descriptor.h" -#include "device/bluetooth/bluetooth_socket.h" -#include "net/base/io_buffer.h" - -namespace chromeos { - -BluetoothSocketExperimentalChromeOS::BluetoothSocketExperimentalChromeOS( - int fd) - : fd_(fd) { - // Fetch the socket type so we read from it correctly. - int optval; - socklen_t opt_len = sizeof optval; - if (getsockopt(fd_, SOL_SOCKET, SO_TYPE, &optval, &opt_len) < 0) { - // Sequenced packet is the safest assumption since it won't result in - // truncated packets. - LOG(WARNING) << "Unable to get socket type: " << safe_strerror(errno); - optval = SOCK_SEQPACKET; - } - - if (optval == SOCK_DGRAM || optval == SOCK_SEQPACKET) { - socket_type_ = L2CAP; - } else { - socket_type_ = RFCOMM; - } -} - -BluetoothSocketExperimentalChromeOS::~BluetoothSocketExperimentalChromeOS() { - HANDLE_EINTR(close(fd_)); -} - -bool BluetoothSocketExperimentalChromeOS::Receive( - net::GrowableIOBuffer *buffer) { - base::ThreadRestrictions::AssertIOAllowed(); - - if (socket_type_ == L2CAP) { - int count; - if (ioctl(fd_, FIONREAD, &count) < 0) { - error_message_ = safe_strerror(errno); - LOG(WARNING) << "Unable to get waiting data size: " << error_message_; - return true; - } - - // No bytes waiting can mean either nothing to read, or the other end has - // been closed, and reading zero bytes always returns zero. - // - // We can't do a short read for fear of a race where data arrives between - // calls and we trunctate it. So use poll() to check for the POLLHUP flag. - if (count == 0) { - struct pollfd pollfd; - - pollfd.fd = fd_; - pollfd.events = 0; - pollfd.revents = 0; - - // Timeout parameter set to 0 so this call will not block. - if (HANDLE_EINTR(poll(&pollfd, 1, 0)) < 0) { - error_message_ = safe_strerror(errno); - LOG(WARNING) << "Unable to check whether socket is closed: " - << error_message_; - return false; - } - - if (pollfd.revents & POLLHUP) { - // TODO(keybuk, youngki): Agree a common way to flag disconnected. - error_message_ = "Disconnected"; - return false; - } - } - - buffer->SetCapacity(count); - } else { - buffer->SetCapacity(1024); - } - - ssize_t bytes_read; - do { - if (buffer->RemainingCapacity() == 0) - buffer->SetCapacity(buffer->capacity() * 2); - bytes_read = - HANDLE_EINTR(read(fd_, buffer->data(), buffer->RemainingCapacity())); - if (bytes_read > 0) - buffer->set_offset(buffer->offset() + bytes_read); - } while (socket_type_ == RFCOMM && bytes_read > 0); - - // Ignore an error if at least one read() call succeeded; it'll be returned - // the next read() call. - if (buffer->offset() > 0) - return true; - - if (bytes_read < 0) { - if (errno == ECONNRESET || errno == ENOTCONN) { - // TODO(keybuk, youngki): Agree a common way to flag disconnected. - error_message_ = "Disconnected"; - return false; - } else if (errno != EAGAIN && errno != EWOULDBLOCK) { - error_message_ = safe_strerror(errno); - return false; - } - } - - if (bytes_read == 0 && socket_type_ == RFCOMM) { - // TODO(keybuk, youngki): Agree a common way to flag disconnected. - error_message_ = "Disconnected"; - return false; - } - - return true; -} - -bool BluetoothSocketExperimentalChromeOS::Send( - net::DrainableIOBuffer *buffer) { - base::ThreadRestrictions::AssertIOAllowed(); - - ssize_t bytes_written; - do { - bytes_written = - HANDLE_EINTR(write(fd_, buffer->data(), buffer->BytesRemaining())); - if (bytes_written > 0) - buffer->DidConsume(bytes_written); - } while (buffer->BytesRemaining() > 0 && bytes_written > 0); - - if (bytes_written < 0) { - if (errno == EPIPE || errno == ECONNRESET || errno == ENOTCONN) { - // TODO(keybuk, youngki): Agree a common way to flag disconnected. - error_message_ = "Disconnected"; - return false; - } else if (errno != EAGAIN && errno != EWOULDBLOCK) { - error_message_ = safe_strerror(errno); - return false; - } - } - - return true; -} - -std::string BluetoothSocketExperimentalChromeOS::GetLastErrorMessage() const { - return error_message_; -} - -// static -scoped_refptr -BluetoothSocketExperimentalChromeOS::Create(dbus::FileDescriptor* fd) { - DCHECK(fd->is_valid()); - - BluetoothSocketExperimentalChromeOS* bluetooth_socket = - new BluetoothSocketExperimentalChromeOS(fd->TakeValue());; - return scoped_refptr(bluetooth_socket); -} - -} // namespace chromeos diff --git a/device/bluetooth/bluetooth_socket_experimental_chromeos.h b/device/bluetooth/bluetooth_socket_experimental_chromeos.h deleted file mode 100644 index c0e1af0..0000000 --- a/device/bluetooth/bluetooth_socket_experimental_chromeos.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_EXPERIMENTAL_CHROMEOS_H_ -#define DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_EXPERIMENTAL_CHROMEOS_H_ - -#include - -#include "base/basictypes.h" -#include "base/memory/ref_counted.h" -#include "chromeos/chromeos_export.h" -#include "device/bluetooth/bluetooth_socket.h" - -namespace dbus { - -class FileDescriptor; - -} // namespace dbus - -namespace net { - -class DrainableIOBuffer; -class GrowableIOBuffer; - -} // namespace net - -namespace chromeos { - -// The BluetoothSocketExperimentalChromeOS class is an alternate implementation -// of BluetoothSocket for the Chrome OS platform using the Bluetooth Smart -// capable backend. It will become the sole implementation for Chrome OS, and -// be renamed to BluetoothSocketChromeOS, once the backend is switched. -class CHROMEOS_EXPORT BluetoothSocketExperimentalChromeOS - : public device::BluetoothSocket { - public: - // BluetoothSocket override. - virtual bool Receive(net::GrowableIOBuffer* buffer) OVERRIDE; - virtual bool Send(net::DrainableIOBuffer* buffer) OVERRIDE; - virtual std::string GetLastErrorMessage() const OVERRIDE; - - // Create an instance of a BluetoothSocket from the passed file descriptor - // received over D-Bus in |fd|, the descriptor will be taken from that object - // and ownership passed to the returned object. - static scoped_refptr Create( - dbus::FileDescriptor* fd); - - protected: - virtual ~BluetoothSocketExperimentalChromeOS(); - - private: - BluetoothSocketExperimentalChromeOS(int fd); - - // The different socket types have different reading patterns; l2cap sockets - // have to be read with boundaries between datagrams preserved while rfcomm - // sockets do not. - enum SocketType { - L2CAP, - RFCOMM - }; - - // File descriptor and socket type of the socket. - const int fd_; - SocketType socket_type_; - - // Last error message, set during Receive() and Send() and retrieved using - // GetLastErrorMessage(). - std::string error_message_; - - DISALLOW_COPY_AND_ASSIGN(BluetoothSocketExperimentalChromeOS); -}; - -} // namespace chromeos - -#endif // DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_EXPERIMENTAL_CHROMEOS_H_ diff --git a/device/device.gyp b/device/device.gyp index a7c520b..bdc387c 100644 --- a/device/device.gyp +++ b/device/device.gyp @@ -22,7 +22,7 @@ 'bluetooth/bluetooth_adapter_mac_unittest.mm', 'bluetooth/bluetooth_adapter_win_unittest.cc', 'bluetooth/bluetooth_device_win_unittest.cc', - 'bluetooth/bluetooth_experimental_chromeos_unittest.cc', + 'bluetooth/bluetooth_chromeos_unittest.cc', 'bluetooth/bluetooth_profile_chromeos_unittest.cc', 'bluetooth/bluetooth_service_record_mac_unittest.mm', 'bluetooth/bluetooth_service_record_win_unittest.cc', -- cgit v1.1