diff options
author | youngki@chromium.org <youngki@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-18 22:15:23 +0000 |
---|---|---|
committer | youngki@chromium.org <youngki@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-18 22:15:23 +0000 |
commit | cab208eb0702d3541a7fa430889c26a3d1182120 (patch) | |
tree | a5ddeb04c32edd7ca0288ea8614d33caf2bb01e1 /device | |
parent | 2b392e59ab9a72858d224bad9e422fe40a696a69 (diff) | |
download | chromium_src-cab208eb0702d3541a7fa430889c26a3d1182120.zip chromium_src-cab208eb0702d3541a7fa430889c26a3d1182120.tar.gz chromium_src-cab208eb0702d3541a7fa430889c26a3d1182120.tar.bz2 |
Moved bluetooth adapter files from chrome/browser/chromeos/bluetooth/ to device/bluetooth/. device/bluetooth/ is a new directory to host the bluetooth related files.
I also changed the namespace from chromeos to bluetooth under new directory.
BUG=135470
Review URL: https://chromiumcodereview.appspot.com/11075006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@162811 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'device')
38 files changed, 5428 insertions, 0 deletions
diff --git a/device/DEPS b/device/DEPS new file mode 100644 index 0000000..2beaf5d --- /dev/null +++ b/device/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+content/public/common", + "+content/public/test", +] diff --git a/device/bluetooth/DEPS b/device/bluetooth/DEPS new file mode 100644 index 0000000..730c30e --- /dev/null +++ b/device/bluetooth/DEPS @@ -0,0 +1,11 @@ +include_rules = [ + "+chrome/common", + "+chrome/test/base", + "+content/public/test", + "+chromeos/dbus", + "+dbus", + "+grit", + "+ui/base/l10n", + "+third_party/cros_system_api/dbus", + "+third_party/libxml/chromium", +] diff --git a/device/bluetooth/OWNERS b/device/bluetooth/OWNERS new file mode 100644 index 0000000..47f6cfa --- /dev/null +++ b/device/bluetooth/OWNERS @@ -0,0 +1,2 @@ +keybuk@chromium.org +bryeung@chromium.org diff --git a/device/bluetooth/bluetooth_adapter.cc b/device/bluetooth/bluetooth_adapter.cc new file mode 100644 index 0000000..b36cd0b --- /dev/null +++ b/device/bluetooth/bluetooth_adapter.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2012 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.h" + +#include "device/bluetooth/bluetooth_device.h" + +namespace device { + +BluetoothAdapter::~BluetoothAdapter() { +} + +const std::string& BluetoothAdapter::address() const { + return address_; +} + +const std::string& BluetoothAdapter::name() const { + return name_; +} + +BluetoothAdapter::DeviceList BluetoothAdapter::GetDevices() { + ConstDeviceList const_devices = + const_cast<const BluetoothAdapter *>(this)->GetDevices(); + + DeviceList devices; + for (ConstDeviceList::const_iterator i = const_devices.begin(); + i != const_devices.end(); ++i) + devices.push_back(const_cast<BluetoothDevice *>(*i)); + + return devices; +} + +} // namespace device diff --git a/device/bluetooth/bluetooth_adapter.h b/device/bluetooth/bluetooth_adapter.h new file mode 100644 index 0000000..86de361 --- /dev/null +++ b/device/bluetooth/bluetooth_adapter.h @@ -0,0 +1,155 @@ +// Copyright (c) 2012 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_H_ +#define DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/memory/ref_counted.h" + +namespace device { + +class BluetoothDevice; + +struct BluetoothOutOfBandPairingData; + +// BluetoothAdapter represents a local Bluetooth adapter which may be used to +// interact with remote Bluetooth devices. As well as providing support for +// determining whether an adapter is present, and whether the radio is powered, +// this class also provides support for obtaining the list of remote devices +// known to the adapter, discovering new devices, and providing notification of +// updates to device information. +class BluetoothAdapter : public base::RefCounted<BluetoothAdapter> { + public: + // Interface for observing changes from bluetooth adapters. + class Observer { + public: + virtual ~Observer() {} + + // Called when the presence of the adapter |adapter| changes, when + // |present| is true the adapter is now present, false means the adapter + // has been removed from the system. + virtual void AdapterPresentChanged(BluetoothAdapter* adapter, + bool present) {} + + // Called when the radio power state of the adapter |adapter| changes, + // when |powered| is true the adapter radio is powered, false means the + // adapter radio is off. + virtual void AdapterPoweredChanged(BluetoothAdapter* adapter, + bool powered) {} + + // Called when the discovering state of the adapter |adapter| changes, + // when |discovering| is true the adapter is seeking new devices, false + // means it is not. Note that device discovery involves both states when + // the adapter is seeking new devices and states when it is not because + // it is interrogating the devices it found. + virtual void AdapterDiscoveringChanged(BluetoothAdapter* adapter, + bool discovering) {} + + // Called when a new device |device| is added to the adapter |adapter|, + // either because it has been discovered or a connection made. |device| + // should not be cached, instead copy its address. + virtual void DeviceAdded(BluetoothAdapter* adapter, + BluetoothDevice* device) {} + + // Called when properties of the device |device| known to the adapter + // |adapter| change. |device| should not be cached, instead copy its + // address. + virtual void DeviceChanged(BluetoothAdapter* adapter, + BluetoothDevice* device) {} + + // Called when the device |device| is removed from the adapter |adapter|, + // either as a result of a discovered device being lost between discovering + // phases or pairing information deleted. |device| should not be cached. + virtual void DeviceRemoved(BluetoothAdapter* adapter, + BluetoothDevice* device) {} + }; + + // The ErrorCallback is used for methods that can fail in which case it + // is called, in the success case the callback is simply not called. + typedef base::Callback<void()> ErrorCallback; + + // The BluetoothOutOfBandPairingDataCallback is used to return + // BluetoothOutOfBandPairingData to the caller. + typedef base::Callback<void(const BluetoothOutOfBandPairingData& data)> + BluetoothOutOfBandPairingDataCallback; + + // Adds and removes observers for events on this bluetooth adapter, + // if monitoring multiple adapters check the |adapter| parameter of + // observer methods to determine which adapter is issuing the event. + virtual void AddObserver(BluetoothAdapter::Observer* observer) = 0; + virtual void RemoveObserver( + BluetoothAdapter::Observer* observer) = 0; + + // The address of this adapter. The address format is "XX:XX:XX:XX:XX:XX", + // where each XX is a hexadecimal number. + virtual const std::string& address() const; + + // The name of the adapter. + virtual const std::string& name() const; + + // Indicates whether the adapter is actually present on the system, for + // the default adapter this indicates whether any adapter is present. An + // adapter is only considered present if the address has been obtained. + virtual bool IsPresent() const = 0; + + // Indicates whether the adapter radio is powered. + virtual bool IsPowered() const = 0; + + // Requests a change to the adapter radio power, setting |powered| to true + // will turn on the radio and false will turn it off. On success, callback + // will be called. On failure, |error_callback| will be called. + virtual void SetPowered(bool powered, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Indicates whether the adapter is currently discovering new devices, + // note that a typical discovery process has phases of this being true + // followed by phases of being false when the adapter interrogates the + // devices found. + virtual bool IsDiscovering() const = 0; + + // Requests that the adapter either begin discovering new devices when + // |discovering| is true, or cease any discovery when false. On success, + // callback will be called. On failure, |error_callback| will be called. + virtual void SetDiscovering(bool discovering, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Requests the list of devices from the adapter, all are returned + // including those currently connected and those paired. Use the + // returned device pointers to determine which they are. + typedef std::vector<BluetoothDevice*> DeviceList; + virtual DeviceList GetDevices(); + typedef std::vector<const BluetoothDevice*> ConstDeviceList; + virtual ConstDeviceList GetDevices() const = 0; + + // Returns a pointer to the device with the given address |address| or + // NULL if no such device is known. + virtual BluetoothDevice* GetDevice(const std::string& address) = 0; + virtual const BluetoothDevice* GetDevice( + const std::string& address) const = 0; + + // Requests the local Out Of Band pairing data. + virtual void ReadLocalOutOfBandPairingData( + const BluetoothOutOfBandPairingDataCallback& callback, + const ErrorCallback& error_callback) = 0; + + protected: + friend class base::RefCounted<BluetoothAdapter>; + virtual ~BluetoothAdapter(); + + // Address of the adapter. + std::string address_; + + // Name of the adapter. + std::string name_; +}; + +} // namespace device + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_H_ diff --git a/device/bluetooth/bluetooth_adapter_chromeos.cc b/device/bluetooth/bluetooth_adapter_chromeos.cc new file mode 100644 index 0000000..36699e5 --- /dev/null +++ b/device/bluetooth/bluetooth_adapter_chromeos.cc @@ -0,0 +1,539 @@ +// Copyright (c) 2012 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 <string> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/values.h" +#include "chromeos/dbus/bluetooth_adapter_client.h" +#include "chromeos/dbus/bluetooth_device_client.h" +#include "chromeos/dbus/bluetooth_manager_client.h" +#include "chromeos/dbus/bluetooth_out_of_band_client.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "dbus/object_path.h" +#include "device/bluetooth/bluetooth_device_chromeos.h" +#include "device/bluetooth/bluetooth_out_of_band_pairing_data.h" + +using device::BluetoothAdapter; +using device::BluetoothDevice; +using device::BluetoothOutOfBandPairingData; + +namespace chromeos { + +BluetoothAdapterChromeOs::BluetoothAdapterChromeOs() : track_default_(false), + powered_(false), + discovering_(false), + weak_ptr_factory_(this) { + DBusThreadManager::Get()->GetBluetoothManagerClient()-> + AddObserver(this); + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + AddObserver(this); + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + AddObserver(this); +} + +BluetoothAdapterChromeOs::~BluetoothAdapterChromeOs() { + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + RemoveObserver(this); + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + RemoveObserver(this); + DBusThreadManager::Get()->GetBluetoothManagerClient()-> + RemoveObserver(this); + + STLDeleteValues(&devices_); +} + +void BluetoothAdapterChromeOs::AddObserver( + BluetoothAdapter::Observer* observer) { + DCHECK(observer); + observers_.AddObserver(observer); +} + +void BluetoothAdapterChromeOs::RemoveObserver( + BluetoothAdapter::Observer* observer) { + DCHECK(observer); + observers_.RemoveObserver(observer); +} + +bool BluetoothAdapterChromeOs::IsPresent() const { + return !object_path_.value().empty() && !address_.empty(); +} + +bool BluetoothAdapterChromeOs::IsPowered() const { + return powered_; +} + +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 { + return discovering_; +} + +void BluetoothAdapterChromeOs::SetDiscovering( + bool discovering, + const base::Closure& callback, + const ErrorCallback& error_callback) { + if (discovering) { + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + StartDiscovery(object_path_, + base::Bind(&BluetoothAdapterChromeOs::OnStartDiscovery, + weak_ptr_factory_.GetWeakPtr(), + callback, + error_callback)); + } else { + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + StopDiscovery(object_path_, + base::Bind(&BluetoothAdapterChromeOs::OnStopDiscovery, + weak_ptr_factory_.GetWeakPtr(), + callback, + error_callback)); + } +} + +BluetoothAdapter::ConstDeviceList BluetoothAdapterChromeOs::GetDevices() const { + ConstDeviceList devices; + for (DevicesMap::const_iterator iter = devices_.begin(); + iter != devices_.end(); + ++iter) + devices.push_back(iter->second); + + return devices; +} + +BluetoothDevice* BluetoothAdapterChromeOs::GetDevice( + const std::string& address) { + return const_cast<BluetoothDevice *>( + const_cast<const BluetoothAdapterChromeOs *>(this)->GetDevice(address)); +} + +const BluetoothDevice* BluetoothAdapterChromeOs::GetDevice( + const std::string& address) const { + DevicesMap::const_iterator iter = devices_.find(address); + if (iter != devices_.end()) + return iter->second; + + return NULL; +} + +void BluetoothAdapterChromeOs::ReadLocalOutOfBandPairingData( + const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback, + const ErrorCallback& error_callback) { + DBusThreadManager::Get()->GetBluetoothOutOfBandClient()-> + ReadLocalData(object_path_, + base::Bind(&BluetoothAdapterChromeOs::OnReadLocalData, + weak_ptr_factory_.GetWeakPtr(), + callback, + error_callback)); +} + +void BluetoothAdapterChromeOs::TrackDefaultAdapter() { + DVLOG(1) << "Tracking default adapter"; + track_default_ = true; + DBusThreadManager::Get()->GetBluetoothManagerClient()-> + DefaultAdapter(base::Bind(&BluetoothAdapterChromeOs::AdapterCallback, + weak_ptr_factory_.GetWeakPtr())); +} + +void BluetoothAdapterChromeOs::FindAdapter(const std::string& address) { + DVLOG(1) << "Using adapter " << address; + track_default_ = false; + DBusThreadManager::Get()->GetBluetoothManagerClient()-> + FindAdapter(address, + base::Bind(&BluetoothAdapterChromeOs::AdapterCallback, + weak_ptr_factory_.GetWeakPtr())); +} + +void BluetoothAdapterChromeOs::AdapterCallback( + const dbus::ObjectPath& adapter_path, + bool success) { + if (success) { + ChangeAdapter(adapter_path); + } else if (!object_path_.value().empty()) { + RemoveAdapter(); + } +} + +void BluetoothAdapterChromeOs::DefaultAdapterChanged( + const dbus::ObjectPath& adapter_path) { + if (track_default_) + ChangeAdapter(adapter_path); +} + +void BluetoothAdapterChromeOs::AdapterRemoved( + const dbus::ObjectPath& adapter_path) { + if (adapter_path == object_path_) + RemoveAdapter(); +} + +void BluetoothAdapterChromeOs::ChangeAdapter( + const dbus::ObjectPath& adapter_path) { + if (object_path_.value().empty()) { + DVLOG(1) << "Adapter path initialized to " << adapter_path.value(); + } else if (object_path_.value() != adapter_path.value()) { + DVLOG(1) << "Adapter path changed from " << object_path_.value() + << " to " << adapter_path.value(); + + RemoveAdapter(); + } else { + DVLOG(1) << "Adapter address updated"; + } + + object_path_ = adapter_path; + + // Update properties to their new values. + BluetoothAdapterClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + GetProperties(object_path_); + + address_ = properties->address.value(); + name_ = properties->name.value(); + + // Delay announcing a new adapter until we have an address. + if (address_.empty()) { + DVLOG(1) << "Adapter address not yet known"; + return; + } + + PoweredChanged(properties->powered.value()); + DiscoveringChanged(properties->discovering.value()); + DevicesChanged(properties->devices.value()); + + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + AdapterPresentChanged(this, true)); +} + +void BluetoothAdapterChromeOs::RemoveAdapter() { + const bool adapter_was_present = IsPresent(); + + DVLOG(1) << "Adapter lost."; + PoweredChanged(false); + DiscoveringChanged(false); + ClearDevices(); + + object_path_ = dbus::ObjectPath(""); + address_.clear(); + name_.clear(); + + if (adapter_was_present) + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + AdapterPresentChanged(this, false)); +} + +void BluetoothAdapterChromeOs::OnSetPowered(const base::Closure& callback, + const ErrorCallback& error_callback, + bool success) { + if (success) + callback.Run(); + else + error_callback.Run(); +} + +void BluetoothAdapterChromeOs::PoweredChanged(bool powered) { + if (powered == powered_) + return; + + powered_ = powered; + + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + AdapterPoweredChanged(this, powered_)); +} + +void BluetoothAdapterChromeOs::OnStartDiscovery( + const base::Closure& callback, + const ErrorCallback& error_callback, + const dbus::ObjectPath& adapter_path, + bool success) { + if (success) { + DVLOG(1) << object_path_.value() << ": started discovery."; + + // Clear devices found in previous discovery attempts + ClearDiscoveredDevices(); + callback.Run(); + } else { + // TODO(keybuk): in future, don't run the callback if the error was just + // that we were already discovering. + error_callback.Run(); + } +} + +void BluetoothAdapterChromeOs::OnStopDiscovery( + const base::Closure& callback, + const ErrorCallback& error_callback, + const dbus::ObjectPath& adapter_path, + bool success) { + if (success) { + DVLOG(1) << object_path_.value() << ": stopped discovery."; + callback.Run(); + // Leave found devices available for perusing. + } else { + // TODO(keybuk): in future, don't run the callback if the error was just + // that we weren't discovering. + error_callback.Run(); + } +} + +void BluetoothAdapterChromeOs::DiscoveringChanged(bool discovering) { + if (discovering == discovering_) + return; + + discovering_ = discovering; + + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + AdapterDiscoveringChanged(this, discovering_)); +} + +void BluetoothAdapterChromeOs::OnReadLocalData( + const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback, + const ErrorCallback& error_callback, + const BluetoothOutOfBandPairingData& data, + bool success) { + if (success) + callback.Run(data); + else + error_callback.Run(); +} + +void BluetoothAdapterChromeOs::AdapterPropertyChanged( + const dbus::ObjectPath& adapter_path, + const std::string& property_name) { + if (adapter_path != object_path_) + return; + + BluetoothAdapterClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + GetProperties(object_path_); + + if (property_name == properties->address.name()) { + ChangeAdapter(object_path_); + + } else if (!address_.empty()) { + if (property_name == properties->powered.name()) { + PoweredChanged(properties->powered.value()); + + } else if (property_name == properties->discovering.name()) { + DiscoveringChanged(properties->discovering.value()); + + } else if (property_name == properties->devices.name()) { + DevicesChanged(properties->devices.value()); + + } else if (property_name == properties->name.name()) { + name_ = properties->name.value(); + + } + } +} + +void BluetoothAdapterChromeOs::DevicePropertyChanged( + const dbus::ObjectPath& device_path, + const std::string& property_name) { + UpdateDevice(device_path); +} + +void BluetoothAdapterChromeOs::UpdateDevice( + const dbus::ObjectPath& device_path) { + BluetoothDeviceClient::Properties* properties = + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + GetProperties(device_path); + + // When we first see a device, we may not know the address yet and need to + // wait for the DevicePropertyChanged signal before adding the device. + const std::string address = properties->address.value(); + if (address.empty()) + return; + + // The device may be already known to us, either because this is an update + // to properties, or the device going from discovered to connected and + // pairing gaining an object path in the process. In any case, we want + // to update the existing object, not create a new one. + DevicesMap::iterator iter = devices_.find(address); + BluetoothDeviceChromeOs* device; + const bool update_device = (iter != devices_.end()); + if (update_device) { + device = iter->second; + } else { + device = BluetoothDeviceChromeOs::Create(this); + devices_[address] = device; + } + + const bool was_paired = device->IsPaired(); + if (!was_paired) { + DVLOG(1) << "Assigned object path " << device_path.value() << " to device " + << address; + device->SetObjectPath(device_path); + } + device->Update(properties, true); + + if (update_device) { + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceChanged(this, device)); + } else { + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceAdded(this, device)); + } +} + +void BluetoothAdapterChromeOs::ClearDevices() { + DevicesMap replace; + devices_.swap(replace); + for (DevicesMap::iterator iter = replace.begin(); + iter != replace.end(); ++iter) { + BluetoothDeviceChromeOs* device = iter->second; + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceRemoved(this, device)); + + delete device; + } +} + +void BluetoothAdapterChromeOs::DeviceCreated( + const dbus::ObjectPath& adapter_path, + const dbus::ObjectPath& device_path) { + if (adapter_path != object_path_) + return; + + UpdateDevice(device_path); +} + +void BluetoothAdapterChromeOs::DeviceRemoved( + const dbus::ObjectPath& adapter_path, + const dbus::ObjectPath& device_path) { + if (adapter_path != object_path_) + return; + + DevicesMap::iterator iter = devices_.begin(); + while (iter != devices_.end()) { + BluetoothDeviceChromeOs* device = iter->second; + DevicesMap::iterator temp = iter; + ++iter; + + if (device->object_path_ != device_path) + continue; + + // DeviceRemoved can also be called to indicate a device that is visible + // during discovery has disconnected, but it is still visible to the + // adapter, so don't remove in that case and only clear the object path. + if (!device->IsVisible()) { + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceRemoved(this, device)); + + DVLOG(1) << "Removed device " << device->address(); + + delete device; + devices_.erase(temp); + } else { + DVLOG(1) << "Removed object path from device " << device->address(); + device->RemoveObjectPath(); + + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceChanged(this, device)); + } + } +} + +void BluetoothAdapterChromeOs::DevicesChanged( + const std::vector<dbus::ObjectPath>& devices) { + for (std::vector<dbus::ObjectPath>::const_iterator iter = + devices.begin(); iter != devices.end(); ++iter) + UpdateDevice(*iter); +} + +void BluetoothAdapterChromeOs::ClearDiscoveredDevices() { + DevicesMap::iterator iter = devices_.begin(); + while (iter != devices_.end()) { + BluetoothDeviceChromeOs* device = iter->second; + DevicesMap::iterator temp = iter; + ++iter; + + if (!device->IsPaired()) { + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceRemoved(this, device)); + + delete device; + devices_.erase(temp); + } + } +} + +void BluetoothAdapterChromeOs::DeviceFound( + const dbus::ObjectPath& adapter_path, + const std::string& address, + const BluetoothDeviceClient::Properties& properties) { + if (adapter_path != object_path_) + return; + + // DeviceFound can also be called to indicate that a device we've + // paired with is now visible to the adapter during discovery, in which + // case we want to update the existing object, not create a new one. + BluetoothDeviceChromeOs* device; + DevicesMap::iterator iter = devices_.find(address); + const bool update_device = (iter != devices_.end()); + if (update_device) { + device = iter->second; + } else { + device = BluetoothDeviceChromeOs::Create(this); + devices_[address] = device; + } + + DVLOG(1) << "Device " << address << " is visible to the adapter"; + device->SetVisible(true); + device->Update(&properties, false); + + if (update_device) { + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceChanged(this, device)); + } else { + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceAdded(this, device)); + } +} + +void BluetoothAdapterChromeOs::DeviceDisappeared( + const dbus::ObjectPath& adapter_path, + const std::string& address) { + if (adapter_path != object_path_) + return; + + DevicesMap::iterator iter = devices_.find(address); + if (iter == devices_.end()) + return; + + BluetoothDeviceChromeOs* device = iter->second; + + // DeviceDisappeared can also be called to indicate that a device we've + // paired with is no longer visible to the adapter, so don't remove + // in that case and only clear the visible flag. + if (!device->IsPaired()) { + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceRemoved(this, device)); + + DVLOG(1) << "Discovered device " << device->address() + << " is no longer visible to the adapter"; + + delete device; + devices_.erase(iter); + } else { + DVLOG(1) << "Paired device " << device->address() + << " is no longer visible to the adapter"; + device->SetVisible(false); + + FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, + DeviceChanged(this, device)); + } +} + +} // namespace chromeos diff --git a/device/bluetooth/bluetooth_adapter_chromeos.h b/device/bluetooth/bluetooth_adapter_chromeos.h new file mode 100644 index 0000000..18cb7aa --- /dev/null +++ b/device/bluetooth/bluetooth_adapter_chromeos.h @@ -0,0 +1,245 @@ +// Copyright (c) 2012 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 <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/observer_list.h" +#include "chromeos/dbus/bluetooth_adapter_client.h" +#include "chromeos/dbus/bluetooth_device_client.h" +#include "chromeos/dbus/bluetooth_manager_client.h" +#include "dbus/object_path.h" +#include "device/bluetooth/bluetooth_adapter.h" + +namespace device { + +class BluetoothAdapterFactory; +class MockBluetoothAdapter; +struct BluetoothOutOfBandPairingData; + +} // namespace device + +namespace chromeos { + +class BluetoothDeviceChromeOs; + +// The BluetoothAdapterChromeOs class is an implementation of BluetoothAdapter +// for Chrome OS platform. +class BluetoothAdapterChromeOs + : public device::BluetoothAdapter, + public BluetoothManagerClient::Observer, + public BluetoothAdapterClient::Observer, + public BluetoothDeviceClient::Observer { + public: + // BluetoothAdapter override + virtual void AddObserver( + device::BluetoothAdapter::Observer* observer) OVERRIDE; + virtual void RemoveObserver( + device::BluetoothAdapter::Observer* observer) 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 SetDiscovering( + bool discovering, + const base::Closure& callback, + const ErrorCallback& error_callback) OVERRIDE; + virtual ConstDeviceList GetDevices() const OVERRIDE; + virtual device::BluetoothDevice* GetDevice( + const std::string& address) OVERRIDE; + virtual const device::BluetoothDevice* GetDevice( + const std::string& address) const OVERRIDE; + virtual void ReadLocalOutOfBandPairingData( + const device::BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& + callback, + const ErrorCallback& error_callback) OVERRIDE; + + private: + friend class BluetoothDeviceChromeOs; + friend class device::BluetoothAdapterFactory; + friend class device::MockBluetoothAdapter; + + BluetoothAdapterChromeOs(); + virtual ~BluetoothAdapterChromeOs(); + + // Obtains the default adapter object path from the Bluetooth Daemon + // and tracks future changes to it. + void TrackDefaultAdapter(); + + // Obtains the object paht for the adapter named by |address| from the + // Bluetooth Daemon. + void FindAdapter(const std::string& address); + + // Called by dbus:: in response to the method call sent by both + // DefaultAdapter() and FindAdapter(), |object_path| will contain the + // dbus object path of the requested adapter when |success| is true. + void AdapterCallback(const dbus::ObjectPath& adapter_path, bool success); + + // BluetoothManagerClient::Observer override. + // + // Called when the default local bluetooth adapter changes. + // |object_path| is the dbus object path of the new default adapter. + // Not called if all adapters are removed. + virtual void DefaultAdapterChanged(const dbus::ObjectPath& adapter_path) + OVERRIDE; + + // BluetoothManagerClient::Observer override. + // + // Called when a local bluetooth adapter is removed. + // |object_path| is the dbus object path of the adapter. + virtual void AdapterRemoved(const dbus::ObjectPath& adapter_path) OVERRIDE; + + // Changes the tracked adapter to the dbus object path |adapter_path|, + // clearing information from the previously tracked adapter and updating + // to the new adapter. + void ChangeAdapter(const dbus::ObjectPath& adapter_path); + + // Clears the tracked adapter information. + void RemoveAdapter(); + + // Called by dbus:: in response to the method call send by SetPowered(). + // |callback| and |error_callback| are the callbacks passed to SetPowered(). + void OnSetPowered(const base::Closure& callback, + const ErrorCallback& error_callback, + bool success); + + // Updates the tracked state of the adapter's radio power to |powered| + // and notifies observers. Called on receipt of a property changed signal, + // and directly using values obtained from properties. + void PoweredChanged(bool powered); + + // Called by dbus:: in response to the method calls send by SetDiscovering(). + // |callback| and |error_callback| are the callbacks passed to + // SetDiscovering(). + void OnStartDiscovery(const base::Closure& callback, + const ErrorCallback& error_callback, + const dbus::ObjectPath& adapter_path, + bool success); + void OnStopDiscovery(const base::Closure& callback, + const ErrorCallback& error_callback, + const dbus::ObjectPath& adapter_path, + bool success); + + // Updates the tracked state of the adapter's discovering state to + // |discovering| and notifies observers. Called on receipt of a property + // changed signal, and directly using values obtained from properties. + void DiscoveringChanged(bool discovering); + + // Called by dbus:: in response to the ReadLocalData method call. + void OnReadLocalData( + const device::BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& + callback, + const ErrorCallback& error_callback, + const device::BluetoothOutOfBandPairingData& data, + bool success); + + // BluetoothAdapterClient::Observer override. + // + // Called when the adapter with object path |adapter_path| has a + // change in value of the property named |property_name|. + virtual void AdapterPropertyChanged(const dbus::ObjectPath& adapter_path, + const std::string& property_name) + OVERRIDE; + + // BluetoothDeviceClient::Observer override. + // + // Called when the device with object path |device_path| has a + // change in value of the property named |property_name|. + virtual void DevicePropertyChanged(const dbus::ObjectPath& device_path, + const std::string& property_name) + OVERRIDE; + + // Updates information on the device with object path |device_path|, + // adding it to the |devices_| map if not already present. + void UpdateDevice(const dbus::ObjectPath& device_path); + + // Clears the |devices_| list, notifying obsevers of the device removal. + void ClearDevices(); + + // BluetoothAdapterClient::Observer override. + // + // Called when the adapter with object path |object_path| has a + // new known device with object path |object_path|. + virtual void DeviceCreated(const dbus::ObjectPath& adapter_path, + const dbus::ObjectPath& device_path) OVERRIDE; + + // BluetoothAdapterClient::Observer override. + // + // Called when the adapter with object path |object_path| removes + // the known device with object path |object_path|. + virtual void DeviceRemoved(const dbus::ObjectPath& adapter_path, + const dbus::ObjectPath& device_path) OVERRIDE; + + // Updates the adapter |devices_| list, adding or updating devices using + // the object paths in the|devices| list. This doesn't remove devices, + // relying instead on the DeviceRemoved() signal for that. Called on + // receipt of a property changed signal, and directly using values obtained + // from properties. + void DevicesChanged(const std::vector<dbus::ObjectPath>& devices); + + // Clears discovered devices from the |devices_| list, notifying + // observers, and leaving only those devices with a dbus object path. + void ClearDiscoveredDevices(); + + // BluetoothAdapterClient::Observer override. + // + // Called when the adapter with object path |object_path| discovers + // a new remote device with address |address| and properties + // |properties|, there is no device object path until connected. + // + // |properties| supports only value() calls, not Get() or Set(), and + // should be copied if needed. + virtual void DeviceFound( + const dbus::ObjectPath& adapter_path, + const std::string& address, + const BluetoothDeviceClient::Properties& properties) OVERRIDE; + + // BluetoothAdapterClient::Observer override. + // + // Called when the adapter with object path |object_path| can no + // longer communicate with the discovered removed device with + // address |address|. + virtual void DeviceDisappeared(const dbus::ObjectPath& object_path, + const std::string& address) OVERRIDE; + + // List of observers interested in event notifications from us. + ObserverList<device::BluetoothAdapter::Observer> observers_; + + // Object path of adapter for this instance, this is fixed at creation time + // unless |track_default_| is true in which case we update it to always + // point at the default adapter. + bool track_default_; + dbus::ObjectPath object_path_; + + // Tracked adapter state, cached locally so we only send change notifications + // to observers on a genuine change. + bool powered_; + bool discovering_; + + // Devices paired with, connected to, discovered by, or visible to the + // adapter. The key is the Bluetooth address of the device and the value + // is the BluetoothDeviceChromeOs object whose lifetime is managed by the + // adapter instance. + typedef std::map<const std::string, BluetoothDeviceChromeOs*> DevicesMap; + DevicesMap devices_; + + // 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<BluetoothAdapterChromeOs> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothAdapterChromeOs); +}; + +} // namespace chromeos + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_CHROMEOS_H_ diff --git a/device/bluetooth/bluetooth_adapter_chromeos_devices_unittest.cc b/device/bluetooth/bluetooth_adapter_chromeos_devices_unittest.cc new file mode 100644 index 0000000..0ff8d3d --- /dev/null +++ b/device/bluetooth/bluetooth_adapter_chromeos_devices_unittest.cc @@ -0,0 +1,168 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/mock_bluetooth_adapter_client.h" +#include "chromeos/dbus/mock_bluetooth_device_client.h" +#include "chromeos/dbus/mock_bluetooth_manager_client.h" +#include "chromeos/dbus/mock_dbus_thread_manager.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/test/mock_bluetooth_adapter.h" +#include "testing/gtest/include/gtest/gtest.h" + +using device::BluetoothAdapter; +using device::BluetoothAdapterFactory; +using device::BluetoothDevice; +using device::MockBluetoothAdapter; +using ::testing::_; +using ::testing::Mock; +using ::testing::Return; +using ::testing::SaveArg; + +namespace chromeos { + +class BluetoothAdapterChromeOsDevicesTest : public testing::Test { + public: + virtual void SetUp() { + MockDBusThreadManager* mock_dbus_thread_manager = new MockDBusThreadManager; + + EXPECT_CALL(*mock_dbus_thread_manager, GetSystemBus()) + .WillRepeatedly(Return(reinterpret_cast<dbus::Bus*>(NULL))); + DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager); + + mock_manager_client_ = + mock_dbus_thread_manager->mock_bluetooth_manager_client(); + mock_adapter_client_ = + mock_dbus_thread_manager->mock_bluetooth_adapter_client(); + mock_device_client_ = + mock_dbus_thread_manager->mock_bluetooth_device_client(); + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + EXPECT_CALL(*mock_manager_client_, AddObserver(_)) + .Times(1); + EXPECT_CALL(*mock_adapter_client_, AddObserver(_)) + .Times(1); + + adapter_ = BluetoothAdapterFactory::DefaultAdapter(); + ASSERT_TRUE(adapter_.get() != NULL); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.address.ReplaceValue(adapter_address_); + adapter_properties.powered.ReplaceValue(true); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path_)) + .WillRepeatedly(Return(&adapter_properties)); + + // Add an observer to the adapter; expect the usual set of changes to + // an adapter becoming present and then clear to clean up for the test. + adapter_->AddObserver(&adapter_observer_); + + EXPECT_CALL(adapter_observer_, AdapterPresentChanged(adapter_.get(), true)) + .Times(1); + EXPECT_CALL(adapter_observer_, AdapterPoweredChanged(adapter_.get(), true)) + .Times(1); + + adapter_callback.Run(adapter_path_, true); + + Mock::VerifyAndClearExpectations(mock_manager_client_); + Mock::VerifyAndClearExpectations(mock_adapter_client_); + Mock::VerifyAndClearExpectations(mock_device_client_); + Mock::VerifyAndClearExpectations(&adapter_observer_); + } + + virtual void TearDown() { + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter_.get()); + EXPECT_CALL(*mock_device_client_, RemoveObserver(adapter_chromeos)) + .Times(1); + EXPECT_CALL(*mock_adapter_client_, RemoveObserver(adapter_chromeos)) + .Times(1); + EXPECT_CALL(*mock_manager_client_, RemoveObserver(adapter_chromeos)) + .Times(1); + + adapter_ = NULL; + DBusThreadManager::Shutdown(); + } + + protected: + MockBluetoothManagerClient* mock_manager_client_; + MockBluetoothAdapterClient* mock_adapter_client_; + MockBluetoothDeviceClient* mock_device_client_; + + static const dbus::ObjectPath adapter_path_; + static const std::string adapter_address_; + scoped_refptr<BluetoothAdapter> adapter_; + + MockBluetoothAdapter::Observer adapter_observer_; +}; + +const dbus::ObjectPath BluetoothAdapterChromeOsDevicesTest::adapter_path_( + "/fake/hci0"); +const std::string BluetoothAdapterChromeOsDevicesTest::adapter_address_ = + "CA:FE:4A:C0:FE:FE"; + +TEST_F(BluetoothAdapterChromeOsDevicesTest, DeviceRemovedAfterFound) { + const dbus::ObjectPath device_path("/fake/hci0/dev_ba_c0_11_00_00_01"); + const std::string device_address = "BA:C0:11:00:00:01"; + + MockBluetoothDeviceClient::Properties device_properties; + device_properties.address.ReplaceValue(device_address); + device_properties.name.ReplaceValue("Fake Keyboard"); + device_properties.bluetooth_class.ReplaceValue(0x2540); + + // Inform the adapter that the device has been found; + // BluetoothAdapterClient::Observer::DeviceAdded will be called, passing + // the device object. + BluetoothDevice* device; + EXPECT_CALL(adapter_observer_, DeviceAdded(adapter_.get(), _)) + .Times(1) + .WillOnce(SaveArg<1>(&device)); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter_.get()); + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->DeviceFound(adapter_path_, device_address, device_properties); + + // Now inform the adapter that the device has been added and assigned an + // object path; BluetoothDeviceClient::GetProperties will be called to + // obtain the property set; and + // BluetoothAdapterClient::Observer::DeviceChanged will be called passing + // the same device object as before. + EXPECT_CALL(*mock_device_client_, GetProperties(device_path)) + .WillRepeatedly(Return(&device_properties)); + + EXPECT_CALL(adapter_observer_, DeviceChanged(adapter_chromeos, device)) + .Times(1); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->DeviceCreated(adapter_path_, device_path); + + // Finally remove the adapter again; + // BluetoothAdapterClient::Observer::DeviceRemoved should be not called, + // instead BluetoothAdapterClient::Observer::DeviceChanged will be called. + EXPECT_CALL(adapter_observer_, DeviceRemoved(adapter_.get(), device)) + .Times(0); + EXPECT_CALL(adapter_observer_, DeviceChanged(adapter_.get(), device)) + .Times(1); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->DeviceRemoved(adapter_path_, device_path); + + // Verify that the device is still visible, just no longer paired. + EXPECT_TRUE(device->IsVisible()); + EXPECT_FALSE(device->IsPaired()); +} + +} // namespace chromeos diff --git a/device/bluetooth/bluetooth_adapter_chromeos_unittest.cc b/device/bluetooth/bluetooth_adapter_chromeos_unittest.cc new file mode 100644 index 0000000..890d241 --- /dev/null +++ b/device/bluetooth/bluetooth_adapter_chromeos_unittest.cc @@ -0,0 +1,1557 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/mock_bluetooth_adapter_client.h" +#include "chromeos/dbus/mock_bluetooth_manager_client.h" +#include "chromeos/dbus/mock_dbus_thread_manager.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/test/mock_bluetooth_adapter.h" +#include "testing/gtest/include/gtest/gtest.h" + +using device::BluetoothAdapter; +using device::BluetoothAdapterFactory; +using device::MockBluetoothAdapter; +using ::testing::_; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::SaveArg; + +namespace chromeos { + +class BluetoothAdapterChromeOsTest : public testing::Test { + public: + virtual void SetUp() { + MockDBusThreadManager* mock_dbus_thread_manager = new MockDBusThreadManager; + + EXPECT_CALL(*mock_dbus_thread_manager, GetSystemBus()) + .WillRepeatedly(Return(reinterpret_cast<dbus::Bus*>(NULL))); + DBusThreadManager::InitializeForTesting(mock_dbus_thread_manager); + + mock_manager_client_ = + mock_dbus_thread_manager->mock_bluetooth_manager_client(); + mock_adapter_client_ = + mock_dbus_thread_manager->mock_bluetooth_adapter_client(); + + set_callback_called_ = false; + error_callback_called_ = false; + } + + virtual void TearDown() { + DBusThreadManager::Shutdown(); + } + + void SetCallback() { + set_callback_called_ = true; + } + + void ErrorCallback() { + error_callback_called_ = true; + } + + protected: + MockBluetoothManagerClient* mock_manager_client_; + MockBluetoothAdapterClient* mock_adapter_client_; + + bool set_callback_called_; + bool error_callback_called_; +}; + +TEST_F(BluetoothAdapterChromeOsTest, DefaultAdapterNotPresent) { + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + ASSERT_TRUE(adapter.get() != NULL); + + // Call the adapter callback; make out it failed. + // BluetoothAdapter::Observer::AdapterPresentChanged must not be called. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), _)) + .Times(0); + + adapter_callback.Run(dbus::ObjectPath(""), false); + + // Adapter should not be present. + EXPECT_FALSE(adapter->IsPresent()); +} + +TEST_F(BluetoothAdapterChromeOsTest, DefaultAdapterWithAddress) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.address.ReplaceValue(adapter_address); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + // BluetoothAdapter::Observer::AdapterPresentChanged will be called to + // indicate the adapter is now present. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + + adapter_callback.Run(adapter_path, true); + + // Adapter should be present with the given address. + EXPECT_TRUE(adapter->IsPresent()); + EXPECT_EQ(adapter_address, adapter->address()); +} + +TEST_F(BluetoothAdapterChromeOsTest, DefaultAdapterWithoutAddress) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + // BluetoothAdapter::Observer::AdapterPresentChanged must not be called + // yet. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), _)) + .Times(0); + + adapter_callback.Run(adapter_path, true); + + // Adapter should not be present yet. + EXPECT_FALSE(adapter->IsPresent()); + + // Tell the adapter the address now; + // BluetoothAdapter::Observer::AdapterPresentChanged now must be called. + adapter_properties.address.ReplaceValue(adapter_address); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->AdapterPropertyChanged(adapter_path, + adapter_properties.address.name()); + + // Adapter should be present with the given address. + EXPECT_TRUE(adapter->IsPresent()); + EXPECT_EQ(adapter_address, adapter->address()); +} + +TEST_F(BluetoothAdapterChromeOsTest, DefaultAdapterBecomesPresentWithAddress) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; make out it failed. + adapter_callback.Run(dbus::ObjectPath(""), false); + + // Tell the adapter the default adapter changed; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.address.ReplaceValue(adapter_address); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + // BluetoothAdapter::Observer::AdapterPresentChanged must be called. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothManagerClient::Observer*>(adapter_chromeos) + ->DefaultAdapterChanged(adapter_path); + + // Adapter should be present with the new address. + EXPECT_TRUE(adapter->IsPresent()); + EXPECT_EQ(adapter_address, adapter->address()); +} + +TEST_F(BluetoothAdapterChromeOsTest, DefaultAdapterReplacedWithAddress) { + const dbus::ObjectPath initial_adapter_path("/fake/hci0"); + const dbus::ObjectPath new_adapter_path("/fake/hci1"); + const std::string initial_adapter_address = "CA:FE:4A:C0:FE:FE"; + const std::string new_adapter_address = "BA:C0:11:CO:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties initial_adapter_properties; + initial_adapter_properties.address.ReplaceValue(initial_adapter_address); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(initial_adapter_path)) + .WillRepeatedly(Return(&initial_adapter_properties)); + + adapter_callback.Run(initial_adapter_path, true); + + // Tell the adapter the default adapter changed; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties new_adapter_properties; + new_adapter_properties.address.ReplaceValue(new_adapter_address); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(new_adapter_path)) + .WillRepeatedly(Return(&new_adapter_properties)); + + // BluetoothAdapter::Observer::AdapterPresentChanged must be called once + // with false to indicate the old adapter being removed and once with true + // to announce the new adapter. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), false)) + .Times(1); + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothManagerClient::Observer*>(adapter_chromeos) + ->DefaultAdapterChanged(new_adapter_path); + + // Adapter should be present with the new address. + EXPECT_TRUE(adapter->IsPresent()); + EXPECT_EQ(new_adapter_address, adapter->address()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterBecomesPresentWithoutAddress) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; make out it failed. + adapter_callback.Run(dbus::ObjectPath(""), false); + + // Tell the adapter the default adapter changed; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + // BluetoothAdapter::Observer::AdapterPresentChanged must not be called. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), _)) + .Times(0); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothManagerClient::Observer*>(adapter_chromeos) + ->DefaultAdapterChanged(adapter_path); + + // Adapter should not be present yet. + EXPECT_FALSE(adapter->IsPresent()); + + // Tell the adapter the address now; + // BluetoothAdapter::Observer::AdapterPresentChanged now must be called. + adapter_properties.address.ReplaceValue(adapter_address); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->AdapterPropertyChanged(adapter_path, + adapter_properties.address.name()); + + // Adapter should be present with the new address. + EXPECT_TRUE(adapter->IsPresent()); + EXPECT_EQ(adapter_address, adapter->address()); +} + +TEST_F(BluetoothAdapterChromeOsTest, DefaultAdapterReplacedWithoutAddress) { + const dbus::ObjectPath initial_adapter_path("/fake/hci0"); + const dbus::ObjectPath new_adapter_path("/fake/hci1"); + const std::string initial_adapter_address = "CA:FE:4A:C0:FE:FE"; + const std::string new_adapter_address = "BA:C0:11:CO:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties initial_adapter_properties; + initial_adapter_properties.address.ReplaceValue(initial_adapter_address); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(initial_adapter_path)) + .WillRepeatedly(Return(&initial_adapter_properties)); + + adapter_callback.Run(initial_adapter_path, true); + + // Tell the adapter the default adapter changed; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties new_adapter_properties; + + EXPECT_CALL(*mock_adapter_client_, GetProperties(new_adapter_path)) + .WillRepeatedly(Return(&new_adapter_properties)); + + // BluetoothAdapter::Observer::AdapterPresentChanged must be called to + // indicate the adapter has gone away. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), false)) + .Times(1); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothManagerClient::Observer*>(adapter_chromeos) + ->DefaultAdapterChanged(new_adapter_path); + + // Adapter should be now marked not present. + EXPECT_FALSE(adapter->IsPresent()); + + // Tell the adapter the address now; + // BluetoothAdapter::Observer::AdapterPresentChanged now must be called. + new_adapter_properties.address.ReplaceValue(new_adapter_address); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->AdapterPropertyChanged(new_adapter_path, + new_adapter_properties.address.name()); + + // Adapter should be present with the new address. + EXPECT_TRUE(adapter->IsPresent()); + EXPECT_EQ(new_adapter_address, adapter->address()); +} + +TEST_F(BluetoothAdapterChromeOsTest, DefaultAdapterRemoved) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.address.ReplaceValue(adapter_address); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + adapter_callback.Run(adapter_path, true); + + // Report that the adapter has been removed; + // BluetoothAdapter::Observer::AdapterPresentChanged will be called to + // indicate the adapter is no longer present. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), false)) + .Times(1); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothManagerClient::Observer*>(adapter_chromeos) + ->AdapterRemoved(adapter_path); + + // Adapter should be no longer present. + EXPECT_FALSE(adapter->IsPresent()); +} + +TEST_F(BluetoothAdapterChromeOsTest, DefaultAdapterWithoutAddressRemoved) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + adapter_callback.Run(adapter_path, true); + + // Report that the adapter has been removed; + // BluetoothAdapter::Observer::AdapterPresentChanged must not be called + // since we never should have announced it in the first place. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), _)) + .Times(0); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothManagerClient::Observer*>(adapter_chromeos) + ->AdapterRemoved(adapter_path); + + // Adapter should be still no longer present. + EXPECT_FALSE(adapter->IsPresent()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterPoweredPropertyInitiallyFalse) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.address.ReplaceValue(adapter_address); + adapter_properties.powered.ReplaceValue(false); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + adapter_callback.Run(adapter_path, true); + + // Adapter should have the correct property value. + EXPECT_FALSE(adapter->IsPowered()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterPoweredPropertyInitiallyTrue) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.address.ReplaceValue(adapter_address); + adapter_properties.powered.ReplaceValue(true); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + // BluetoothAdapter::Observer::AdapterPoweredChanged will be called. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + + EXPECT_CALL(adapter_observer, AdapterPoweredChanged(adapter.get(), true)) + .Times(1); + + adapter_callback.Run(adapter_path, true); + + // Adapter should have the correct property value. + EXPECT_TRUE(adapter->IsPowered()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterPoweredPropertyInitiallyTrueWithoutAddress) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set but BluetoothAdapter::Observer::AdapterPoweredChanged + // should not yet be called. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.powered.ReplaceValue(true); + + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + EXPECT_CALL(adapter_observer, AdapterPoweredChanged(adapter.get(), _)) + .Times(0); + + adapter_callback.Run(adapter_path, true); + + // Adapter should not yet have the property value. + EXPECT_FALSE(adapter->IsPowered()); + + // Tell the adapter the address now, + // BluetoothAdapter::Observer::AdapterPresentChanged and + // BluetoothAdapter::Observer::AdapterPoweredChanged now must be called. + adapter_properties.address.ReplaceValue(adapter_address); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + + EXPECT_CALL(adapter_observer, AdapterPoweredChanged(adapter.get(), true)) + .Times(1); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->AdapterPropertyChanged(adapter_path, + adapter_properties.address.name()); + + // Adapter should have the correct property value. + EXPECT_TRUE(adapter->IsPowered()); +} + +TEST_F(BluetoothAdapterChromeOsTest, DefaultAdapterPoweredPropertyChanged) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.address.ReplaceValue(adapter_address); + adapter_properties.powered.ReplaceValue(false); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + adapter_callback.Run(adapter_path, true); + + // Adapter should have the correct property value. + EXPECT_FALSE(adapter->IsPowered()); + + // Report that the property has been changed; + // BluetoothAdapter::Observer::AdapterPoweredChanged will be called. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPoweredChanged(adapter.get(), true)) + .Times(1); + + adapter_properties.powered.ReplaceValue(true); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->AdapterPropertyChanged(adapter_path, + adapter_properties.powered.name()); + + // Adapter should have the new property values. + EXPECT_TRUE(adapter->IsPowered()); +} + +TEST_F(BluetoothAdapterChromeOsTest, DefaultAdapterPoweredPropertyUnchanged) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.address.ReplaceValue(adapter_address); + adapter_properties.powered.ReplaceValue(true); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + adapter_callback.Run(adapter_path, true); + + // Adapter should have the correct property value. + EXPECT_TRUE(adapter->IsPowered()); + + // Report that the property has been changed, but don't change the value; + // BluetoothAdapter::Observer::AdapterPoweredChanged should not be called. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPoweredChanged(adapter.get(), _)) + .Times(0); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->AdapterPropertyChanged(adapter_path, + adapter_properties.powered.name()); + + // Adapter should still have the same property values. + EXPECT_TRUE(adapter->IsPowered()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterPoweredPropertyChangedWithoutAddress) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set but BluetoothAdapter::Observer::AdapterPoweredChanged + // should not yet be called. + MockBluetoothAdapterClient::Properties adapter_properties; + + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + EXPECT_CALL(adapter_observer, AdapterPoweredChanged(adapter.get(), _)) + .Times(0); + + adapter_callback.Run(adapter_path, true); + + // Tell the adapter that its powered property changed, the observer + // method should still not be called because there is no address for + // the adapter so it is not present. + adapter_properties.powered.ReplaceValue(true); + + EXPECT_CALL(adapter_observer, AdapterPoweredChanged(adapter.get(), _)) + .Times(0); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->AdapterPropertyChanged(adapter_path, + adapter_properties.powered.name()); + + // Adapter should not yet have the property value. + EXPECT_FALSE(adapter->IsPowered()); + + // Tell the adapter the address now, + // BluetoothAdapter::Observer::AdapterPresentChanged and + // BluetoothAdapter::Observer::AdapterPoweredChanged now must be called. + adapter_properties.address.ReplaceValue(adapter_address); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + + EXPECT_CALL(adapter_observer, AdapterPoweredChanged(adapter.get(), true)) + .Times(1); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->AdapterPropertyChanged(adapter_path, + adapter_properties.address.name()); + + // Adapter should now have the correct property value. + EXPECT_TRUE(adapter->IsPowered()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterPoweredPropertyResetOnReplace) { + const dbus::ObjectPath initial_adapter_path("/fake/hci0"); + const dbus::ObjectPath new_adapter_path("/fake/hci1"); + const std::string initial_adapter_address = "CA:FE:4A:C0:FE:FE"; + const std::string new_adapter_address = "00:C0:11:CO:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties initial_adapter_properties; + initial_adapter_properties.address.ReplaceValue(initial_adapter_address); + initial_adapter_properties.powered.ReplaceValue(true); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(initial_adapter_path)) + .WillRepeatedly(Return(&initial_adapter_properties)); + + adapter_callback.Run(initial_adapter_path, true); + + // Tell the adapter the default adapter changed; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties new_adapter_properties; + new_adapter_properties.address.ReplaceValue(new_adapter_address); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(new_adapter_path)) + .WillRepeatedly(Return(&new_adapter_properties)); + + // BluetoothAdapter::Observer::AdapterPoweredChanged will be called. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + { + InSequence s; + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), false)) + .Times(1); + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + } + + EXPECT_CALL(adapter_observer, AdapterPoweredChanged(adapter.get(), false)) + .Times(1); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothManagerClient::Observer*>(adapter_chromeos) + ->DefaultAdapterChanged(new_adapter_path); + + // Adapter should have the new property value. + EXPECT_FALSE(adapter->IsPowered()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterPoweredPropertyResetOnReplaceWhenTrue) { + const dbus::ObjectPath initial_adapter_path("/fake/hci0"); + const dbus::ObjectPath new_adapter_path("/fake/hci1"); + const std::string initial_adapter_address = "CA:FE:4A:C0:FE:FE"; + const std::string new_adapter_address = "BA:C0:11:CO:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties initial_adapter_properties; + initial_adapter_properties.address.ReplaceValue(initial_adapter_address); + initial_adapter_properties.powered.ReplaceValue(true); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(initial_adapter_path)) + .WillRepeatedly(Return(&initial_adapter_properties)); + + adapter_callback.Run(initial_adapter_path, true); + + // Tell the adapter the default adapter changed; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties new_adapter_properties; + new_adapter_properties.address.ReplaceValue(new_adapter_address); + new_adapter_properties.powered.ReplaceValue(true); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(new_adapter_path)) + .WillRepeatedly(Return(&new_adapter_properties)); + + // BluetoothAdapter::Observer::AdapterPoweredChanged will be called once + // to set the value to false for the previous adapter and once to set the + // value to true for the new adapter. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + { + InSequence s; + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), false)) + .Times(1); + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + } + + { + InSequence s; + + EXPECT_CALL(adapter_observer, AdapterPoweredChanged(adapter.get(), false)) + .Times(1); + EXPECT_CALL(adapter_observer, AdapterPoweredChanged(adapter.get(), true)) + .Times(1); + } + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothManagerClient::Observer*>(adapter_chromeos) + ->DefaultAdapterChanged(new_adapter_path); + + // Adapter should have the new property value. + EXPECT_TRUE(adapter->IsPowered()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterPoweredPropertyResetOnRemove) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.address.ReplaceValue(adapter_address); + adapter_properties.powered.ReplaceValue(true); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + adapter_callback.Run(adapter_path, true); + + // Report that the adapter has been removed; + // BluetoothAdapter::Observer::AdapterPoweredChanged will be called. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), false)) + .Times(1); + EXPECT_CALL(adapter_observer, AdapterPoweredChanged(adapter.get(), false)) + .Times(1); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothManagerClient::Observer*>(adapter_chromeos) + ->AdapterRemoved(adapter_path); + + // Adapter should have the new property value. + EXPECT_FALSE(adapter->IsPowered()); +} + +TEST_F(BluetoothAdapterChromeOsTest, DefaultAdapterSetPowered) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + adapter_callback.Run(adapter_path, true); + + // Request that the powered property be changed; + // MockBluetoothAdapterClient::Set should be called, passing the address + // of the powered property and a callback to receive the response. + dbus::PropertySet::SetCallback set_callback; + EXPECT_CALL(adapter_properties, Set(&adapter_properties.powered, _)) + .WillOnce(SaveArg<1>(&set_callback)); + + adapter->SetPowered(true, + base::Bind(&BluetoothAdapterChromeOsTest::SetCallback, + base::Unretained(this)), + base::Bind(&BluetoothAdapterChromeOsTest::ErrorCallback, + base::Unretained(this))); + + // Reply to the callback to indicate success, the set callback we provided + // should be called but the properties should not be refetched. + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .Times(0); + + set_callback.Run(true); + + EXPECT_TRUE(set_callback_called_); + EXPECT_FALSE(error_callback_called_); +} + +TEST_F(BluetoothAdapterChromeOsTest, DefaultAdapterSetPoweredError) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + adapter_callback.Run(adapter_path, true); + + // Request that the powered property be changed; + // MockBluetoothAdapterClient::Set should be called, passing the address + // of the powered property and a callback to receive the response. + dbus::PropertySet::SetCallback set_callback; + EXPECT_CALL(adapter_properties, Set(&adapter_properties.powered, _)) + .WillOnce(SaveArg<1>(&set_callback)); + + adapter->SetPowered(true, + base::Bind(&BluetoothAdapterChromeOsTest::SetCallback, + base::Unretained(this)), + base::Bind(&BluetoothAdapterChromeOsTest::ErrorCallback, + base::Unretained(this))); + + // Reply to the callback to indicate failure, the error callback we provided + // should be called but the properties should not be refetched. + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .Times(0); + + set_callback.Run(false); + + EXPECT_FALSE(set_callback_called_); + EXPECT_TRUE(error_callback_called_); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterDiscoveringPropertyInitiallyFalse) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.address.ReplaceValue(adapter_address); + adapter_properties.discovering.ReplaceValue(false); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + adapter_callback.Run(adapter_path, true); + + // Adapter should have the correct property value. + EXPECT_FALSE(adapter->IsDiscovering()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterDiscoveringPropertyInitiallyTrue) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.address.ReplaceValue(adapter_address); + adapter_properties.discovering.ReplaceValue(true); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + // BluetoothAdapter::Observer::AdapterDiscoveringChanged will be called. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + + EXPECT_CALL(adapter_observer, AdapterDiscoveringChanged(adapter.get(), true)) + .Times(1); + + adapter_callback.Run(adapter_path, true); + + // Adapter should have the correct property value. + EXPECT_TRUE(adapter->IsDiscovering()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterDiscoveringPropertyInitiallyTrueWithoutAddress) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set but BluetoothAdapter::Observer::AdapterDiscoveringChanged + // should not yet be called. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.discovering.ReplaceValue(true); + + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + EXPECT_CALL(adapter_observer, AdapterDiscoveringChanged(adapter.get(), _)) + .Times(0); + + adapter_callback.Run(adapter_path, true); + + // Adapter should not yet have the property value. + EXPECT_FALSE(adapter->IsDiscovering()); + + // Tell the adapter the address now, + // BluetoothAdapter::Observer::AdapterPresentChanged and + // BluetoothAdapter::Observer::AdapterDiscoveringChanged now must be called. + adapter_properties.address.ReplaceValue(adapter_address); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + + EXPECT_CALL(adapter_observer, AdapterDiscoveringChanged(adapter.get(), true)) + .Times(1); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->AdapterPropertyChanged(adapter_path, + adapter_properties.address.name()); + + // Adapter should have the correct property value. + EXPECT_TRUE(adapter->IsDiscovering()); +} + +TEST_F(BluetoothAdapterChromeOsTest, DefaultAdapterDiscoveringPropertyChanged) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.address.ReplaceValue(adapter_address); + adapter_properties.discovering.ReplaceValue(false); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + adapter_callback.Run(adapter_path, true); + + // Adapter should have the correct property value. + EXPECT_FALSE(adapter->IsDiscovering()); + + // Report that the property has been changed; + // BluetoothAdapter::Observer::AdapterDiscoveringChanged will be called. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterDiscoveringChanged(adapter.get(), true)) + .Times(1); + + adapter_properties.discovering.ReplaceValue(true); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->AdapterPropertyChanged(adapter_path, + adapter_properties.discovering.name()); + + // Adapter should have the new property values. + EXPECT_TRUE(adapter->IsDiscovering()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterDiscoveringPropertyUnchanged) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.address.ReplaceValue(adapter_address); + adapter_properties.discovering.ReplaceValue(true); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + adapter_callback.Run(adapter_path, true); + + // Adapter should have the correct property value. + EXPECT_TRUE(adapter->IsDiscovering()); + + // Report that the property has been changed, but don't change the value; + // BluetoothAdapter::Observer::AdapterDiscoveringChanged should not be + // called. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterDiscoveringChanged(adapter.get(), _)) + .Times(0); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->AdapterPropertyChanged(adapter_path, + adapter_properties.discovering.name()); + + // Adapter should still have the same property values. + EXPECT_TRUE(adapter->IsDiscovering()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterDiscoveringPropertyChangedWithoutAddress) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set but BluetoothAdapter::Observer::AdapterDiscoveringChanged + // should not yet be called. + MockBluetoothAdapterClient::Properties adapter_properties; + + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + EXPECT_CALL(adapter_observer, AdapterDiscoveringChanged(adapter.get(), _)) + .Times(0); + + adapter_callback.Run(adapter_path, true); + + // Tell the adapter that its discovering property changed, the observer + // method should still not be called because there is no address for + // the adapter so it is not present. + adapter_properties.discovering.ReplaceValue(true); + + EXPECT_CALL(adapter_observer, AdapterDiscoveringChanged(adapter.get(), _)) + .Times(0); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->AdapterPropertyChanged(adapter_path, + adapter_properties.discovering.name()); + + // Adapter should not yet have the property value. + EXPECT_FALSE(adapter->IsDiscovering()); + + // Tell the adapter the address now, + // BluetoothAdapter::Observer::AdapterPresentChanged and + // BluetoothAdapter::Observer::AdapterDiscoveringChanged now must be called. + adapter_properties.address.ReplaceValue(adapter_address); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + + EXPECT_CALL(adapter_observer, AdapterDiscoveringChanged(adapter.get(), true)) + .Times(1); + + static_cast<BluetoothAdapterClient::Observer*>(adapter_chromeos) + ->AdapterPropertyChanged(adapter_path, + adapter_properties.address.name()); + + // Adapter should now have the correct property value. + EXPECT_TRUE(adapter->IsDiscovering()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterDiscoveringPropertyResetOnReplace) { + const dbus::ObjectPath initial_adapter_path("/fake/hci0"); + const dbus::ObjectPath new_adapter_path("/fake/hci1"); + const std::string initial_adapter_address = "CA:FE:4A:C0:FE:FE"; + const std::string new_adapter_address = "BA:C0:11:CO:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties initial_adapter_properties; + initial_adapter_properties.address.ReplaceValue(initial_adapter_address); + initial_adapter_properties.discovering.ReplaceValue(true); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(initial_adapter_path)) + .WillRepeatedly(Return(&initial_adapter_properties)); + + adapter_callback.Run(initial_adapter_path, true); + + // Tell the adapter the default adapter changed; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties new_adapter_properties; + new_adapter_properties.address.ReplaceValue(new_adapter_address); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(new_adapter_path)) + .WillRepeatedly(Return(&new_adapter_properties)); + + // BluetoothAdapter::Observer::AdapterDiscoveringChanged will be called. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + { + InSequence s; + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), false)) + .Times(1); + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + } + + EXPECT_CALL(adapter_observer, AdapterDiscoveringChanged(adapter.get(), false)) + .Times(1); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothManagerClient::Observer*>(adapter_chromeos) + ->DefaultAdapterChanged(new_adapter_path); + + // Adapter should have the new property value. + EXPECT_FALSE(adapter->IsDiscovering()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterDiscoveringPropertyResetOnReplaceWhenTrue) { + const dbus::ObjectPath initial_adapter_path("/fake/hci0"); + const dbus::ObjectPath new_adapter_path("/fake/hci1"); + const std::string initial_adapter_address = "CA:FE:4A:C0:FE:FE"; + const std::string new_adapter_address = "BA:C0:11:CO:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties initial_adapter_properties; + initial_adapter_properties.address.ReplaceValue(initial_adapter_address); + initial_adapter_properties.discovering.ReplaceValue(true); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(initial_adapter_path)) + .WillRepeatedly(Return(&initial_adapter_properties)); + + adapter_callback.Run(initial_adapter_path, true); + + // Tell the adapter the default adapter changed; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties new_adapter_properties; + new_adapter_properties.address.ReplaceValue(new_adapter_address); + new_adapter_properties.discovering.ReplaceValue(true); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(new_adapter_path)) + .WillRepeatedly(Return(&new_adapter_properties)); + + // BluetoothAdapter::Observer::AdapterDiscoveringChanged will be called once + // to set the value to false for the previous adapter and once to set the + // value to true for the new adapter. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + { + InSequence s; + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), false)) + .Times(1); + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), true)) + .Times(1); + } + + { + InSequence s; + + EXPECT_CALL(adapter_observer, AdapterDiscoveringChanged(adapter.get(), + false)) + .Times(1); + EXPECT_CALL(adapter_observer, AdapterDiscoveringChanged(adapter.get(), + true)) + .Times(1); + } + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothManagerClient::Observer*>(adapter_chromeos) + ->DefaultAdapterChanged(new_adapter_path); + + // Adapter should have the new property value. + EXPECT_TRUE(adapter->IsDiscovering()); +} + +TEST_F(BluetoothAdapterChromeOsTest, + DefaultAdapterDiscoveringPropertyResetOnRemove) { + const dbus::ObjectPath adapter_path("/fake/hci0"); + const std::string adapter_address = "CA:FE:4A:C0:FE:FE"; + + // Create the default adapter instance; + // BluetoothManagerClient::DefaultAdapter will be called once, passing + // a callback to obtain the adapter path. + BluetoothManagerClient::AdapterCallback adapter_callback; + EXPECT_CALL(*mock_manager_client_, DefaultAdapter(_)) + .WillOnce(SaveArg<0>(&adapter_callback)); + + scoped_refptr<BluetoothAdapter> adapter = + BluetoothAdapterFactory::DefaultAdapter(); + + // Call the adapter callback; + // BluetoothAdapterClient::GetProperties will be called once to obtain + // the property set. + MockBluetoothAdapterClient::Properties adapter_properties; + adapter_properties.address.ReplaceValue(adapter_address); + adapter_properties.discovering.ReplaceValue(true); + + EXPECT_CALL(*mock_adapter_client_, GetProperties(adapter_path)) + .WillRepeatedly(Return(&adapter_properties)); + + adapter_callback.Run(adapter_path, true); + + // Report that the adapter has been removed; + // BluetoothAdapter::Observer::AdapterDiscoveringChanged will be called. + MockBluetoothAdapter::Observer adapter_observer; + adapter->AddObserver(&adapter_observer); + + EXPECT_CALL(adapter_observer, AdapterPresentChanged(adapter.get(), false)) + .Times(1); + EXPECT_CALL(adapter_observer, AdapterDiscoveringChanged(adapter.get(), false)) + .Times(1); + + BluetoothAdapterChromeOs* adapter_chromeos = + static_cast<BluetoothAdapterChromeOs*>(adapter.get()); + + static_cast<BluetoothManagerClient::Observer*>(adapter_chromeos) + ->AdapterRemoved(adapter_path); + + // Adapter should have the new property value. + EXPECT_FALSE(adapter->IsDiscovering()); +} + +} // namespace chromeos diff --git a/device/bluetooth/bluetooth_adapter_factory.cc b/device/bluetooth/bluetooth_adapter_factory.cc new file mode 100644 index 0000000..03d633f --- /dev/null +++ b/device/bluetooth/bluetooth_adapter_factory.cc @@ -0,0 +1,55 @@ +// Copyright (c) 2012 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_factory.h" + +#include "base/lazy_instance.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "device/bluetooth/bluetooth_adapter.h" + +#if defined(OS_CHROMEOS) +#include "device/bluetooth/bluetooth_adapter_chromeos.h" +#endif + +namespace { + +// Shared default adapter instance, we don't want to keep this class around +// if nobody is using it so use a WeakPtr and create the object when needed; +// since Google C++ Style (and clang's static analyzer) forbids us having +// exit-time destructors we use a leaky lazy instance for it. +base::LazyInstance<base::WeakPtr<device::BluetoothAdapter> >::Leaky + default_adapter = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +namespace device { + +// static +scoped_refptr<BluetoothAdapter> BluetoothAdapterFactory::DefaultAdapter() { + if (!default_adapter.Get().get()) { +#if defined(OS_CHROMEOS) + chromeos::BluetoothAdapterChromeOs* new_adapter = + new chromeos::BluetoothAdapterChromeOs; + new_adapter->TrackDefaultAdapter(); + default_adapter.Get() = new_adapter->weak_ptr_factory_.GetWeakPtr(); +#endif + } + + return scoped_refptr<BluetoothAdapter>(default_adapter.Get()); +} + +// static +BluetoothAdapter* BluetoothAdapterFactory::Create(const std::string& address) { + BluetoothAdapter* adapter = NULL; +#if defined(OS_CHROMEOS) + chromeos::BluetoothAdapterChromeOs* adapter_chromeos = + new chromeos::BluetoothAdapterChromeOs; + adapter_chromeos->FindAdapter(address); + adapter = adapter_chromeos; +#endif + return adapter; +} + +} // namespace device diff --git a/device/bluetooth/bluetooth_adapter_factory.h b/device/bluetooth/bluetooth_adapter_factory.h new file mode 100644 index 0000000..82e1e6f --- /dev/null +++ b/device/bluetooth/bluetooth_adapter_factory.h @@ -0,0 +1,33 @@ +// Copyright (c) 2012 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_FACTORY_H_ +#define DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_FACTORY_H_ + +#include <string> + +#include "base/memory/ref_counted.h" + +namespace device { + +class BluetoothAdapter; + +// BluetoothAdapterFactory is a class that contains static methods, which +// instantiate either a specific bluetooth adapter, or the generic "default +// adapter" which may change depending on availability. +class BluetoothAdapterFactory { + public: + // Returns the shared instance for the default adapter, whichever that may + // be at the time. Check the returned scoped_refptr does not point to NULL and + // use IsPresent() and the AdapterPresentChanged() observer method to + // determine whether an adapter is actually available or not. + static scoped_refptr<BluetoothAdapter> DefaultAdapter(); + + // Creates an instance for a specific adapter at address |address|. + static BluetoothAdapter* Create(const std::string& address); +}; + +} // namespace device + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_ADAPTER_FACTORY_H_ diff --git a/device/bluetooth/bluetooth_device.cc b/device/bluetooth/bluetooth_device.cc new file mode 100644 index 0000000..378c6ce --- /dev/null +++ b/device/bluetooth/bluetooth_device.cc @@ -0,0 +1,170 @@ +// Copyright (c) 2012 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.h" + +#include "base/utf_string_conversions.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" + +namespace device { + +BluetoothDevice::BluetoothDevice() + : bluetooth_class_(0), + visible_(false), + bonded_(false), + connected_(false) { +} + +BluetoothDevice::~BluetoothDevice() { +} + +const std::string& BluetoothDevice::address() const { + return address_; +} + +string16 BluetoothDevice::GetName() const { + if (!name_.empty()) { + return UTF8ToUTF16(name_); + } else { + return GetAddressWithLocalizedDeviceTypeName(); + } +} + +string16 BluetoothDevice::GetAddressWithLocalizedDeviceTypeName() const { + string16 address = UTF8ToUTF16(address_); + BluetoothDevice::DeviceType device_type = GetDeviceType(); + switch (device_type) { + case DEVICE_COMPUTER: + return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_COMPUTER, + address); + case DEVICE_PHONE: + return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_PHONE, + address); + case DEVICE_MODEM: + return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_MODEM, + address); + case DEVICE_AUDIO: + return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_AUDIO, + address); + case DEVICE_CAR_AUDIO: + return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_CAR_AUDIO, + address); + case DEVICE_VIDEO: + return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_VIDEO, + address); + case DEVICE_JOYSTICK: + return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_JOYSTICK, + address); + case DEVICE_GAMEPAD: + return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_GAMEPAD, + address); + case DEVICE_KEYBOARD: + return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_KEYBOARD, + address); + case DEVICE_MOUSE: + return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_MOUSE, + address); + case DEVICE_TABLET: + return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_TABLET, + address); + case DEVICE_KEYBOARD_MOUSE_COMBO: + return l10n_util::GetStringFUTF16( + IDS_BLUETOOTH_DEVICE_KEYBOARD_MOUSE_COMBO, address); + default: + return l10n_util::GetStringFUTF16(IDS_BLUETOOTH_DEVICE_UNKNOWN, address); + } +} + +BluetoothDevice::DeviceType BluetoothDevice::GetDeviceType() const { + // https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm + switch ((bluetooth_class_ & 0x1f00) >> 8) { + case 0x01: + // Computer major device class. + return DEVICE_COMPUTER; + case 0x02: + // Phone major device class. + switch ((bluetooth_class_ & 0xfc) >> 2) { + case 0x01: + case 0x02: + case 0x03: + // Cellular, cordless and smart phones. + return DEVICE_PHONE; + case 0x04: + case 0x05: + // Modems: wired or voice gateway and common ISDN access. + return DEVICE_MODEM; + } + break; + case 0x04: + // Audio major device class. + switch ((bluetooth_class_ & 0xfc) >> 2) { + case 0x08: + // Car audio. + return DEVICE_CAR_AUDIO; + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + case 0x010: + // Video devices. + return DEVICE_VIDEO; + default: + return DEVICE_AUDIO; + } + break; + case 0x05: + // Peripheral major device class. + switch ((bluetooth_class_ & 0xc0) >> 6) { + case 0x00: + // "Not a keyboard or pointing device." + switch ((bluetooth_class_ & 0x01e) >> 2) { + case 0x01: + // Joystick. + return DEVICE_JOYSTICK; + case 0x02: + // Gamepad. + return DEVICE_GAMEPAD; + default: + return DEVICE_PERIPHERAL; + } + break; + case 0x01: + // Keyboard. + return DEVICE_KEYBOARD; + case 0x02: + // Pointing device. + switch ((bluetooth_class_ & 0x01e) >> 2) { + case 0x05: + // Digitizer tablet. + return DEVICE_TABLET; + default: + // Mouse. + return DEVICE_MOUSE; + } + break; + case 0x03: + // Combo device. + return DEVICE_KEYBOARD_MOUSE_COMBO; + } + break; + } + + return DEVICE_UNKNOWN; +} + +bool BluetoothDevice::IsVisible() const { + return visible_; +} + +bool BluetoothDevice::IsBonded() const { + return bonded_; +} + +bool BluetoothDevice::IsConnected() const { + return connected_; +} + +} // namespace device diff --git a/device/bluetooth/bluetooth_device.h b/device/bluetooth/bluetooth_device.h new file mode 100644 index 0000000..90e420b --- /dev/null +++ b/device/bluetooth/bluetooth_device.h @@ -0,0 +1,318 @@ +// Copyright (c) 2012 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_H_ +#define DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/string16.h" +#include "base/memory/ref_counted.h" + +namespace device { + +class BluetoothServiceRecord; +class BluetoothSocket; + +struct BluetoothOutOfBandPairingData; + +// BluetoothDevice represents a remote Bluetooth device, both its properties and +// capabilities as discovered by a local adapter and actions that may be +// performed on the remove device such as pairing, connection and disconnection. +// +// The class is instantiated and managed by the BluetoothAdapter class +// and pointers should only be obtained from that class and not cached, +// instead use the address() method as a unique key for a device. +// +// Since the lifecycle of BluetoothDevice instances is managed by +// BluetoothAdapter, that class rather than this provides observer methods +// for devices coming and going, as well as properties being updated. +class BluetoothDevice { + public: + // Possible values that may be returned by GetDeviceType(), representing + // different types of bluetooth device that we support or are aware of + // decoded from the bluetooth class information. + enum DeviceType { + DEVICE_UNKNOWN, + DEVICE_COMPUTER, + DEVICE_PHONE, + DEVICE_MODEM, + DEVICE_AUDIO, + DEVICE_CAR_AUDIO, + DEVICE_VIDEO, + DEVICE_PERIPHERAL, + DEVICE_JOYSTICK, + DEVICE_GAMEPAD, + DEVICE_KEYBOARD, + DEVICE_MOUSE, + DEVICE_TABLET, + DEVICE_KEYBOARD_MOUSE_COMBO + }; + + // Interface for observing changes from bluetooth devices. + class Observer { + public: + virtual ~Observer() {} + + // TODO(keybuk): add observers for pairing and connection. + }; + + // Interface for negotiating pairing of bluetooth devices. + class PairingDelegate { + public: + virtual ~PairingDelegate() {} + + // This method will be called when the Bluetooth daemon requires a + // PIN Code for authentication of the device |device|, the delegate should + // obtain the code from the user and call SetPinCode() on the device to + // provide it, or RejectPairing() or CancelPairing() to reject or cancel + // the request. + // + // PIN Codes are generally required for Bluetooth 2.0 and earlier devices + // for which there is no automatic pairing or special handling. + virtual void RequestPinCode(BluetoothDevice* device) = 0; + + // This method will be called when the Bluetooth daemon requires a + // Passkey for authentication of the device |device|, the delegate should + // obtain the passkey from the user (a numeric in the range 0-999999) and + // call SetPasskey() on the device to provide it, or RejectPairing() or + // CancelPairing() to reject or cancel the request. + // + // Passkeys are generally required for Bluetooth 2.1 and later devices + // which cannot provide input or display on their own, and don't accept + // passkey-less pairing. + virtual void RequestPasskey(BluetoothDevice* device) = 0; + + // This method will be called when the Bluetooth daemon requires that the + // user enter the PIN code |pincode| into the device |device| so that it + // may be authenticated. The DismissDisplayOrConfirm() method + // will be called to dismiss the display once pairing is complete or + // cancelled. + // + // This is used for Bluetooth 2.0 and earlier keyboard devices, the + // |pincode| will always be a six-digit numeric in the range 000000-999999 + // for compatibilty with later specifications. + virtual void DisplayPinCode(BluetoothDevice* device, + const std::string& pincode) = 0; + + // This method will be called when the Bluetooth daemon requires that the + // user enter the Passkey |passkey| into the device |device| so that it + // may be authenticated. The DismissDisplayOrConfirm() method will be + // called to dismiss the display once pairing is complete or cancelled. + // + // This is used for Bluetooth 2.1 and later devices that support input + // but not display, such as keyboards. The Passkey is a numeric in the + // range 0-999999 and should be always presented zero-padded to six + // digits. + virtual void DisplayPasskey(BluetoothDevice* device, + uint32 passkey) = 0; + + // This method will be called when the Bluetooth daemon requires that the + // user confirm that the Passkey |passkey| is displayed on the screen + // of the device |device| so that it may be authenticated. The delegate + // should display to the user and ask for confirmation, then call + // ConfirmPairing() on the device to confirm, RejectPairing() on the device + // to reject or CancelPairing() on the device to cancel authentication + // for any other reason. + // + // This is used for Bluetooth 2.1 and later devices that support display, + // such as other computers or phones. The Passkey is a numeric in the + // range 0-999999 and should be always present zero-padded to six + // digits. + virtual void ConfirmPasskey(BluetoothDevice* device, + uint32 passkey) = 0; + + // This method will be called when any previous DisplayPinCode(), + // DisplayPasskey() or ConfirmPasskey() request should be concluded + // and removed from the user. + virtual void DismissDisplayOrConfirm() = 0; + }; + + virtual ~BluetoothDevice(); + + // Returns the Bluetooth of address the device. This should be used as + // a unique key to identify the device and copied where needed. + virtual const std::string& address() const; + + // Returns the name of the device suitable for displaying, this may + // be a synthesied string containing the address and localized type name + // if the device has no obtained name. + virtual string16 GetName() const; + + // Returns the type of the device, limited to those we support or are + // aware of, by decoding the bluetooth class information. The returned + // values are unique, and do not overlap, so DEVICE_KEYBOARD is not also + // DEVICE_PERIPHERAL. + DeviceType GetDeviceType() const; + + // Indicates whether the device is paired to the adapter, whether or not + // that pairing is permanent or temporary. + virtual bool IsPaired() const = 0; + + // Indicates whether the device is visible to the adapter, this is not + // mutually exclusive to being paired. + virtual bool IsVisible() const; + + // Indicates whether the device is bonded to the adapter, bonding is + // formed by pairing and exchanging high-security link keys so that + // connections may be encrypted. + virtual bool IsBonded() const; + + // Indicates whether the device is currently connected to the adapter + // and at least one service available for use. + virtual bool IsConnected() const; + + // Returns the services (as UUID strings) that this device provides. + typedef std::vector<std::string> ServiceList; + virtual const ServiceList& GetServices() const = 0; + + // The ErrorCallback is used for methods that can fail in which case it + // is called, in the success case the callback is simply not called. + typedef base::Callback<void()> ErrorCallback; + + // Returns the services (as BluetoothServiceRecord objects) that this device + // provides. + typedef ScopedVector<BluetoothServiceRecord> ServiceRecordList; + typedef base::Callback<void(const ServiceRecordList&)> ServiceRecordsCallback; + virtual void GetServiceRecords(const ServiceRecordsCallback& callback, + const ErrorCallback& error_callback) = 0; + + // Indicates whether this device provides the given service. |uuid| should + // be in canonical form (see utils::CanonicalUuid). + virtual bool ProvidesServiceWithUUID(const std::string& uuid) const = 0; + + // The ProvidesServiceCallback is used by ProvidesServiceWithName to indicate + // whether or not a matching service was found. + typedef base::Callback<void(bool)> ProvidesServiceCallback; + + // Indicates whether this device provides the given service. + virtual void ProvidesServiceWithName( + const std::string& name, + const ProvidesServiceCallback& callback) = 0; + + // Indicates whether the device is currently pairing and expecting a + // PIN Code to be returned. + virtual bool ExpectingPinCode() const = 0; + + // Indicates whether the device is currently pairing and expecting a + // Passkey to be returned. + virtual bool ExpectingPasskey() const = 0; + + // Indicates whether the device is currently pairing and expecting + // confirmation of a displayed passkey. + virtual bool ExpectingConfirmation() const = 0; + + // SocketCallback is used by ConnectToService to return a BluetoothSocket to + // the caller, or NULL if there was an error. The socket will remain open + // until the last reference to the returned BluetoothSocket is released. + typedef base::Callback<void(scoped_refptr<BluetoothSocket>)> + SocketCallback; + + // Initiates a connection to the device, pairing first if necessary. + // + // Method calls will be made on the supplied object |pairing_delegate| + // to indicate what display, and in response should make method calls + // back to the device object. Not all devices require user responses + // during pairing, so it is normal for |pairing_delegate| to receive no + // calls. To explicitly force a low-security connection without bonding, + // pass NULL, though this is ignored if the device is already paired. + // + // If the request fails, |error_callback| will be called; otherwise, + // |callback| is called when the request is complete. + virtual void Connect(PairingDelegate* pairing_delegate, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Sends the PIN code |pincode| to the remote device during pairing. + // + // PIN Codes are generally required for Bluetooth 2.0 and earlier devices + // for which there is no automatic pairing or special handling. + virtual void SetPinCode(const std::string& pincode) = 0; + + // Sends the Passkey |passkey| to the remote device during pairing. + // + // Passkeys are generally required for Bluetooth 2.1 and later devices + // which cannot provide input or display on their own, and don't accept + // passkey-less pairing, and are a numeric in the range 0-999999. + virtual void SetPasskey(uint32 passkey) = 0; + + // Confirms to the remote device during pairing that a passkey provided by + // the ConfirmPasskey() delegate call is displayed on both devices. + virtual void ConfirmPairing() = 0; + + // Rejects a pairing or connection request from a remote device. + virtual void RejectPairing() = 0; + + // Cancels a pairing or connection attempt to a remote device. + virtual void CancelPairing() = 0; + + // Disconnects the device, terminating the low-level ACL connection + // and any application connections using it. Link keys and other pairing + // information are not discarded, and the device object is not deleted. + // If the request fails, |error_callback| will be called; otherwise, + // |callback| is called when the request is complete. + virtual void Disconnect(const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Disconnects the device, terminating the low-level ACL connection + // and any application connections using it, and then discards link keys + // and other pairing information. The device object remainds valid until + // returing from the calling function, after which it should be assumed to + // have been deleted. If the request fails, |error_callback| will be called. + // There is no callback for success beause this object is often deleted + // before that callback would be called. + virtual void Forget(const ErrorCallback& error_callback) = 0; + + // Attempts to open a socket to a service matching |uuid| on this device. If + // the connection is successful, |callback| is called with a BluetoothSocket. + // Otherwise |callback| is called with NULL. The socket is closed as soon as + // all references to the BluetoothSocket are released. Note that the + // BluetoothSocket object can outlive both this BluetoothDevice and the + // BluetoothAdapter for this device. + virtual void ConnectToService(const std::string& service_uuid, + const SocketCallback& callback) = 0; + + // Sets the Out Of Band pairing data for this device to |data|. Exactly one + // of |callback| or |error_callback| will be run. + virtual void SetOutOfBandPairingData( + const BluetoothOutOfBandPairingData& data, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Clears the Out Of Band pairing data for this device. Exactly one of + // |callback| or |error_callback| will be run. + virtual void ClearOutOfBandPairingData( + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + protected: + BluetoothDevice(); + + // The Bluetooth class of the device, a bitmask that may be decoded using + // https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm + uint32 bluetooth_class_; + + // The name of the device, as supplied by the remote device. + std::string name_; + + // The Bluetooth address of the device. + std::string address_; + + // Tracked device state, updated by the adapter managing the lifecyle of + // the device. + bool visible_; + bool bonded_; + bool connected_; + + private: + // Returns a localized string containing the device's bluetooth address and + // a device type for display when |name_| is empty. + string16 GetAddressWithLocalizedDeviceTypeName() const; +}; + +} // namespace device + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_H_ diff --git a/device/bluetooth/bluetooth_device_chromeos.cc b/device/bluetooth/bluetooth_device_chromeos.cc new file mode 100644 index 0000000..2a09cac --- /dev/null +++ b/device/bluetooth/bluetooth_device_chromeos.cc @@ -0,0 +1,690 @@ +// Copyright (c) 2012 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 <map> +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/memory/scoped_vector.h" +#include "base/memory/weak_ptr.h" +#include "base/string16.h" +#include "base/string_util.h" +#include "base/values.h" +#include "chromeos/dbus/bluetooth_adapter_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/bluetooth_out_of_band_client.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/introspectable_client.h" +#include "dbus/bus.h" +#include "dbus/object_path.h" +#include "device/bluetooth/bluetooth_adapter_chromeos.h" +#include "device/bluetooth/bluetooth_out_of_band_pairing_data.h" +#include "device/bluetooth/bluetooth_service_record.h" +#include "device/bluetooth/bluetooth_socket_chromeos.h" +#include "device/bluetooth/bluetooth_utils.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +using device::BluetoothDevice; +using device::BluetoothOutOfBandPairingData; +using device::BluetoothServiceRecord; +using device::BluetoothSocket; + +namespace chromeos { + +BluetoothDeviceChromeOs::BluetoothDeviceChromeOs( + BluetoothAdapterChromeOs* adapter) + : BluetoothDevice(), + adapter_(adapter), + pairing_delegate_(NULL), + connecting_applications_counter_(0), + weak_ptr_factory_(this) { +} + +BluetoothDeviceChromeOs::~BluetoothDeviceChromeOs() { +} + +bool BluetoothDeviceChromeOs::IsPaired() const { + return !object_path_.value().empty(); +} + +const BluetoothDevice::ServiceList& +BluetoothDeviceChromeOs::GetServices() const { + return service_uuids_; +} + +void BluetoothDeviceChromeOs::GetServiceRecords( + const ServiceRecordsCallback& callback, + const ErrorCallback& error_callback) { + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + DiscoverServices( + object_path_, + "", // empty pattern to browse all services + base::Bind(&BluetoothDeviceChromeOs::CollectServiceRecordsCallback, + weak_ptr_factory_.GetWeakPtr(), + callback, + error_callback)); +} + +bool BluetoothDeviceChromeOs::ProvidesServiceWithUUID( + const std::string& uuid) const { + const BluetoothDevice::ServiceList& services = GetServices(); + for (BluetoothDevice::ServiceList::const_iterator iter = services.begin(); + iter != services.end(); + ++iter) { + if (device::bluetooth_utils::CanonicalUuid(*iter) == uuid) + return true; + } + return false; +} + +void BluetoothDeviceChromeOs::ProvidesServiceWithName( + const std::string& name, + const ProvidesServiceCallback& callback) { + GetServiceRecords( + base::Bind(&BluetoothDeviceChromeOs::SearchServicesForNameCallback, + weak_ptr_factory_.GetWeakPtr(), + name, + callback), + base::Bind(&BluetoothDeviceChromeOs::SearchServicesForNameErrorCallback, + weak_ptr_factory_.GetWeakPtr(), + callback)); +} + +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(PairingDelegate* pairing_delegate, + const base::Closure& callback, + const ErrorCallback& error_callback) { + if (IsPaired() || IsBonded() || IsConnected()) { + // Connection to already paired or connected device. + ConnectApplications(callback, error_callback); + + } else if (!pairing_delegate) { + // No pairing delegate supplied, initiate low-security connection only. + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + CreateDevice(adapter_->object_path_, + address_, + base::Bind(&BluetoothDeviceChromeOs::ConnectCallback, + weak_ptr_factory_.GetWeakPtr(), + callback, + error_callback), + base::Bind(&BluetoothDeviceChromeOs::ConnectErrorCallback, + weak_ptr_factory_.GetWeakPtr(), + error_callback)); + } else { + // Initiate high-security connection with pairing. + DCHECK(!pairing_delegate_); + pairing_delegate_ = pairing_delegate; + + // The agent path is relatively meaningless, we use the device address + // to generate it as we only support one pairing attempt at a time for + // a given bluetooth device. + DCHECK(agent_.get() == NULL); + + std::string agent_path_basename; + ReplaceChars(address_, ":", "_", &agent_path_basename); + dbus::ObjectPath agent_path("/org/chromium/bluetooth_agent/" + + agent_path_basename); + + dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus(); + if (system_bus) { + agent_.reset(BluetoothAgentServiceProvider::Create(system_bus, + agent_path, + this)); + } else { + agent_.reset(NULL); + } + + DVLOG(1) << "Pairing: " << address_; + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + CreatePairedDevice( + adapter_->object_path_, + address_, + agent_path, + bluetooth_agent::kDisplayYesNoCapability, + base::Bind(&BluetoothDeviceChromeOs::ConnectCallback, + weak_ptr_factory_.GetWeakPtr(), + callback, + error_callback), + base::Bind(&BluetoothDeviceChromeOs::ConnectErrorCallback, + 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() { + if (!agent_.get()) + return; + + if (!pincode_callback_.is_null()) { + pincode_callback_.Run(REJECTED, ""); + pincode_callback_.Reset(); + } + if (!passkey_callback_.is_null()) { + passkey_callback_.Run(REJECTED, 0); + passkey_callback_.Reset(); + } + if (!confirmation_callback_.is_null()) { + confirmation_callback_.Run(REJECTED); + confirmation_callback_.Reset(); + } +} + +void BluetoothDeviceChromeOs::CancelPairing() { + if (!agent_.get()) + return; + + if (!pincode_callback_.is_null()) { + pincode_callback_.Run(CANCELLED, ""); + pincode_callback_.Reset(); + } + if (!passkey_callback_.is_null()) { + passkey_callback_.Run(CANCELLED, 0); + passkey_callback_.Reset(); + } + if (!confirmation_callback_.is_null()) { + confirmation_callback_.Run(CANCELLED); + confirmation_callback_.Reset(); + } +} + +void BluetoothDeviceChromeOs::Disconnect(const base::Closure& callback, + const ErrorCallback& error_callback) { + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + Disconnect(object_path_, + base::Bind(&BluetoothDeviceChromeOs::DisconnectCallback, + weak_ptr_factory_.GetWeakPtr(), + callback, + error_callback)); + +} + +void BluetoothDeviceChromeOs::Forget(const ErrorCallback& error_callback) { + DBusThreadManager::Get()->GetBluetoothAdapterClient()-> + RemoveDevice(adapter_->object_path_, + object_path_, + base::Bind(&BluetoothDeviceChromeOs::ForgetCallback, + weak_ptr_factory_.GetWeakPtr(), + error_callback)); +} + +void BluetoothDeviceChromeOs::ConnectToService(const std::string& service_uuid, + const SocketCallback& callback) { + GetServiceRecords( + base::Bind(&BluetoothDeviceChromeOs::GetServiceRecordsForConnectCallback, + weak_ptr_factory_.GetWeakPtr(), + service_uuid, + callback), + base::Bind( + &BluetoothDeviceChromeOs::GetServiceRecordsForConnectErrorCallback, + weak_ptr_factory_.GetWeakPtr(), + callback)); +} + +void BluetoothDeviceChromeOs::SetOutOfBandPairingData( + const BluetoothOutOfBandPairingData& data, + const base::Closure& callback, + const ErrorCallback& error_callback) { + DBusThreadManager::Get()->GetBluetoothOutOfBandClient()-> + AddRemoteData( + object_path_, + address(), + data, + base::Bind(&BluetoothDeviceChromeOs::OnRemoteDataCallback, + weak_ptr_factory_.GetWeakPtr(), + callback, + error_callback)); +} + +void BluetoothDeviceChromeOs::ClearOutOfBandPairingData( + const base::Closure& callback, + const ErrorCallback& error_callback) { + DBusThreadManager::Get()->GetBluetoothOutOfBandClient()-> + RemoveRemoteData( + object_path_, + address(), + base::Bind(&BluetoothDeviceChromeOs::OnRemoteDataCallback, + weak_ptr_factory_.GetWeakPtr(), + callback, + error_callback)); +} + +void BluetoothDeviceChromeOs::SetObjectPath( + const dbus::ObjectPath& object_path) { + DCHECK(object_path_ == dbus::ObjectPath("")); + object_path_ = object_path; +} + +void BluetoothDeviceChromeOs::RemoveObjectPath() { + DCHECK(object_path_ != dbus::ObjectPath("")); + object_path_ = dbus::ObjectPath(""); +} + +void BluetoothDeviceChromeOs::Update( + const BluetoothDeviceClient::Properties* properties, + bool update_state) { + std::string address = properties->address.value(); + std::string name = properties->name.value(); + uint32 bluetooth_class = properties->bluetooth_class.value(); + const std::vector<std::string>& uuids = properties->uuids.value(); + + if (!address.empty()) + address_ = address; + if (!name.empty()) + name_ = name; + if (bluetooth_class) + bluetooth_class_ = bluetooth_class; + if (!uuids.empty()) { + service_uuids_.clear(); + service_uuids_.assign(uuids.begin(), uuids.end()); + } + + if (update_state) { + // BlueZ uses paired to mean link keys exchanged, whereas the Bluetooth + // spec refers to this as bonded. Use the spec name for our interface. + bonded_ = properties->paired.value(); + connected_ = properties->connected.value(); + } +} + +void BluetoothDeviceChromeOs::ConnectCallback( + const base::Closure& callback, + const ErrorCallback& error_callback, + const dbus::ObjectPath& device_path) { + DVLOG(1) << "Connection successful: " << device_path.value(); + if (object_path_.value().empty()) { + object_path_ = device_path; + } else { + LOG_IF(WARNING, object_path_ != device_path) + << "Conflicting device paths for objects, result gave: " + << device_path.value() << " but signal gave: " + << object_path_.value(); + } + + // Mark the device trusted so it can connect to us automatically, and + // we can connect after rebooting. This information is part of the + // pairing information of the device, and is unique to the combination + // of our bluetooth address and the device's bluetooth address. A + // different host needs a new pairing, so it's not useful to sync. + DBusThreadManager::Get()->GetBluetoothDeviceClient()-> + GetProperties(object_path_)->trusted.Set( + true, + base::Bind(&BluetoothDeviceChromeOs::OnSetTrusted, + weak_ptr_factory_.GetWeakPtr(), + callback, + error_callback)); + + // Connect application-layer protocols. + ConnectApplications(callback, error_callback); +} + +void BluetoothDeviceChromeOs::ConnectErrorCallback( + const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message) { + LOG(WARNING) << "Connection failed: " << address_ + << ": " << error_name << ": " << error_message; + error_callback.Run(); +} + +void BluetoothDeviceChromeOs::CollectServiceRecordsCallback( + const ServiceRecordsCallback& callback, + const ErrorCallback& error_callback, + const dbus::ObjectPath& device_path, + const BluetoothDeviceClient::ServiceMap& service_map, + bool success) { + if (!success) { + error_callback.Run(); + return; + } + + ScopedVector<BluetoothServiceRecord> records; + for (BluetoothDeviceClient::ServiceMap::const_iterator i = + service_map.begin(); i != service_map.end(); ++i) { + records.push_back( + new BluetoothServiceRecord(address(), i->second)); + } + callback.Run(records); +} + +void BluetoothDeviceChromeOs::OnSetTrusted(const base::Closure& callback, + const ErrorCallback& error_callback, + bool success) { + if (success) { + callback.Run(); + } else { + LOG(WARNING) << "Failed to set device as trusted: " << address_; + error_callback.Run(); + } +} + +void BluetoothDeviceChromeOs::ConnectApplications( + const base::Closure& callback, + const ErrorCallback& error_callback) { + // Introspect the device object to determine supported applications. + DBusThreadManager::Get()->GetIntrospectableClient()-> + Introspect(bluetooth_device::kBluetoothDeviceServiceName, + object_path_, + base::Bind(&BluetoothDeviceChromeOs::OnIntrospect, + weak_ptr_factory_.GetWeakPtr(), + callback, + error_callback)); +} + +void BluetoothDeviceChromeOs::OnIntrospect(const base::Closure& callback, + const ErrorCallback& error_callback, + const std::string& service_name, + const dbus::ObjectPath& device_path, + const std::string& xml_data, + bool success) { + if (!success) { + LOG(WARNING) << "Failed to determine supported applications: " << address_; + error_callback.Run(); + return; + } + + // The introspection data for the device object may list one or more + // additional D-Bus interfaces that BlueZ supports for this particular + // device. Send appropraite Connect calls for each of those interfaces + // to connect all of the application protocols for this device. + std::vector<std::string> interfaces = + IntrospectableClient::GetInterfacesFromIntrospectResult(xml_data); + + DCHECK_EQ(0, connecting_applications_counter_); + connecting_applications_counter_ = 0; + for (std::vector<std::string>::iterator iter = interfaces.begin(); + iter != interfaces.end(); ++iter) { + if (*iter == bluetooth_input::kBluetoothInputInterface) { + connecting_applications_counter_++; + // Supports Input interface. + DBusThreadManager::Get()->GetBluetoothInputClient()-> + Connect(object_path_, + base::Bind(&BluetoothDeviceChromeOs::OnConnect, + weak_ptr_factory_.GetWeakPtr(), + callback, + *iter), + base::Bind(&BluetoothDeviceChromeOs::OnConnectError, + weak_ptr_factory_.GetWeakPtr(), + error_callback, *iter)); + } + } + + // If OnConnect has been called for every call to Connect above, then this + // will decrement the counter to -1. In that case, call the callback + // directly as it has not been called by any of the OnConnect callbacks. + // This is safe because OnIntrospect and OnConnect run on the same thread. + connecting_applications_counter_--; + if (connecting_applications_counter_ == -1) + callback.Run(); +} + +void BluetoothDeviceChromeOs::OnConnect(const base::Closure& callback, + const std::string& interface_name, + const dbus::ObjectPath& device_path) { + DVLOG(1) << "Application connection successful: " << device_path.value() + << ": " << interface_name; + + connecting_applications_counter_--; + // |callback| should only be called once, meaning it cannot be called before + // all requests have been started. The extra decrement after all requests + // have been started, and the check for -1 instead of 0 below, insure only a + // single call to |callback| will occur (provided OnConnect and OnIntrospect + // run on the same thread, which is true). + if (connecting_applications_counter_ == -1) { + connecting_applications_counter_ = 0; + callback.Run(); + } +} + +void BluetoothDeviceChromeOs::OnConnectError( + const ErrorCallback& error_callback, + const std::string& interface_name, + const dbus::ObjectPath& device_path, + const std::string& error_name, + const std::string& error_message) { + LOG(WARNING) << "Connection failed: " << address_ << ": " << interface_name + << ": " << error_name << ": " << error_message; + error_callback.Run(); +} + +void BluetoothDeviceChromeOs::DisconnectCallback( + const base::Closure& callback, + const ErrorCallback& error_callback, + const dbus::ObjectPath& device_path, + bool success) { + DCHECK(device_path == object_path_); + if (success) { + DVLOG(1) << "Disconnection successful: " << address_; + callback.Run(); + } else { + LOG(WARNING) << "Disconnection failed: " << address_; + error_callback.Run(); + } +} + +void BluetoothDeviceChromeOs::ForgetCallback( + const ErrorCallback& error_callback, + const dbus::ObjectPath& adapter_path, + bool success) { + // It's quite normal that this path never gets called on success; we use a + // weak pointer, and bluetoothd might send the DeviceRemoved signal before + // the method reply, in which case this object is deleted and the + // callback never takes place. Therefore don't do anything here for the + // success case. + if (!success) { + LOG(WARNING) << "Forget failed: " << address_; + error_callback.Run(); + } +} + +void BluetoothDeviceChromeOs::SearchServicesForNameErrorCallback( + const ProvidesServiceCallback& callback) { + callback.Run(false); +} + +void BluetoothDeviceChromeOs::SearchServicesForNameCallback( + const std::string& name, + const ProvidesServiceCallback& callback, + const ServiceRecordList& list) { + for (ServiceRecordList::const_iterator i = list.begin(); + i != list.end(); ++i) { + if ((*i)->name() == name) { + callback.Run(true); + return; + } + } + callback.Run(false); +} + +void BluetoothDeviceChromeOs::GetServiceRecordsForConnectErrorCallback( + const SocketCallback& callback) { + callback.Run(NULL); +} + +void BluetoothDeviceChromeOs::GetServiceRecordsForConnectCallback( + const std::string& service_uuid, + const SocketCallback& callback, + const ServiceRecordList& list) { + for (ServiceRecordList::const_iterator i = list.begin(); + i != list.end(); ++i) { + if ((*i)->uuid() == service_uuid) { + // If multiple service records are found, use the first one that works. + scoped_refptr<BluetoothSocket> socket( + BluetoothSocketChromeOs::CreateBluetoothSocket(**i)); + if (socket.get() != NULL) { + callback.Run(socket); + return; + } + } + } + callback.Run(NULL); +} + +void BluetoothDeviceChromeOs::OnRemoteDataCallback( + const base::Closure& callback, + const ErrorCallback& error_callback, + bool success) { + if (success) + callback.Run(); + else + error_callback.Run(); +} + +void BluetoothDeviceChromeOs::DisconnectRequested( + const dbus::ObjectPath& object_path) { + DCHECK(object_path == object_path_); +} + +void BluetoothDeviceChromeOs::Release() { + DCHECK(agent_.get()); + DVLOG(1) << "Release: " << address_; + + DCHECK(pairing_delegate_); + pairing_delegate_->DismissDisplayOrConfirm(); + pairing_delegate_ = NULL; + + pincode_callback_.Reset(); + passkey_callback_.Reset(); + confirmation_callback_.Reset(); + + agent_.reset(); +} + +void BluetoothDeviceChromeOs::RequestPinCode( + const dbus::ObjectPath& device_path, + const PinCodeCallback& callback) { + DCHECK(agent_.get()); + DVLOG(1) << "RequestPinCode: " << device_path.value(); + + DCHECK(pairing_delegate_); + DCHECK(pincode_callback_.is_null()); + pincode_callback_ = callback; + pairing_delegate_->RequestPinCode(this); +} + +void BluetoothDeviceChromeOs::RequestPasskey( + const dbus::ObjectPath& device_path, + const PasskeyCallback& callback) { + DCHECK(agent_.get()); + DCHECK(device_path == object_path_); + DVLOG(1) << "RequestPasskey: " << device_path.value(); + + DCHECK(pairing_delegate_); + DCHECK(passkey_callback_.is_null()); + passkey_callback_ = callback; + pairing_delegate_->RequestPasskey(this); +} + +void BluetoothDeviceChromeOs::DisplayPinCode( + const dbus::ObjectPath& device_path, + const std::string& pincode) { + DCHECK(agent_.get()); + DCHECK(device_path == object_path_); + DVLOG(1) << "DisplayPinCode: " << device_path.value() << " " << pincode; + + DCHECK(pairing_delegate_); + pairing_delegate_->DisplayPinCode(this, pincode); +} + +void BluetoothDeviceChromeOs::DisplayPasskey( + const dbus::ObjectPath& device_path, + uint32 passkey) { + DCHECK(agent_.get()); + DCHECK(device_path == object_path_); + DVLOG(1) << "DisplayPasskey: " << device_path.value() << " " << passkey; + + DCHECK(pairing_delegate_); + pairing_delegate_->DisplayPasskey(this, passkey); +} + +void BluetoothDeviceChromeOs::RequestConfirmation( + const dbus::ObjectPath& device_path, + uint32 passkey, + const ConfirmationCallback& callback) { + DCHECK(agent_.get()); + DCHECK(device_path == object_path_); + DVLOG(1) << "RequestConfirmation: " << device_path.value() << " " << passkey; + + DCHECK(pairing_delegate_); + DCHECK(confirmation_callback_.is_null()); + confirmation_callback_ = callback; + pairing_delegate_->ConfirmPasskey(this, passkey); +} + +void BluetoothDeviceChromeOs::Authorize(const dbus::ObjectPath& device_path, + const std::string& uuid, + const ConfirmationCallback& callback) { + DCHECK(agent_.get()); + DCHECK(device_path == object_path_); + LOG(WARNING) << "Rejected authorization for service: " << uuid + << " requested from device: " << device_path.value(); + callback.Run(REJECTED); +} + +void BluetoothDeviceChromeOs::ConfirmModeChange( + Mode mode, + const ConfirmationCallback& callback) { + DCHECK(agent_.get()); + LOG(WARNING) << "Rejected adapter-level mode change: " << mode + << " made on agent for device: " << address_; + callback.Run(REJECTED); +} + +void BluetoothDeviceChromeOs::Cancel() { + DCHECK(agent_.get()); + DVLOG(1) << "Cancel: " << address_; + + DCHECK(pairing_delegate_); + pairing_delegate_->DismissDisplayOrConfirm(); +} + + +// static +BluetoothDeviceChromeOs* BluetoothDeviceChromeOs::Create( + BluetoothAdapterChromeOs* adapter) { + return new BluetoothDeviceChromeOs(adapter); +} + +} // namespace chromeos diff --git a/device/bluetooth/bluetooth_device_chromeos.h b/device/bluetooth/bluetooth_device_chromeos.h new file mode 100644 index 0000000..4b2d3b5 --- /dev/null +++ b/device/bluetooth/bluetooth_device_chromeos.h @@ -0,0 +1,375 @@ +// Copyright (c) 2012 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 <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/memory/weak_ptr.h" +#include "base/string16.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 device { + +class BluetoothServiceRecord; +class MockBluetoothDevice; +struct BluetoothOutOfBandPairingData; + +} // namespace device + +namespace chromeos { + +class BluetoothAdapterChromeOs; + +// The BluetoothDeviceChromeOs class is an implementation of BluetoothDevice +// for Chrome OS platform. +class BluetoothDeviceChromeOs + : public device::BluetoothDevice, + public BluetoothDeviceClient::Observer, + public BluetoothAgentServiceProvider::Delegate { + public: + virtual ~BluetoothDeviceChromeOs(); + + // BluetoothDevice override + virtual bool IsPaired() const OVERRIDE; + virtual const ServiceList& GetServices() const OVERRIDE; + virtual void GetServiceRecords( + const ServiceRecordsCallback& callback, + const ErrorCallback& error_callback) OVERRIDE; + virtual bool ProvidesServiceWithUUID(const std::string& uuid) const 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 ErrorCallback& 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 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; + + private: + friend class BluetoothAdapterChromeOs; + friend class device::MockBluetoothDevice; + + explicit BluetoothDeviceChromeOs(BluetoothAdapterChromeOs* adapter); + + // Sets the dbus object path for the device to |object_path|, indicating + // that the device has gone from being discovered to paired or bonded. + void SetObjectPath(const dbus::ObjectPath& object_path); + + // Removes the dbus object path from the device, indicating that the + // device is no longer paired or bonded, but perhaps still visible. + void RemoveObjectPath(); + + // Sets whether the device is visible to the owning adapter to |visible|. + void SetVisible(bool visible) { visible_ = visible; } + + // Updates device information from the properties in |properties|, device + // state properties such as |paired_| and |connected_| are ignored unless + // |update_state| is true. + void Update(const BluetoothDeviceClient::Properties* properties, + bool update_state); + + // Called by BluetoothAdapterClient when a call to CreateDevice() or + // CreatePairedDevice() succeeds, provides the new object path for the remote + // device in |device_path|. |callback| and |error_callback| are the callbacks + // provided to Connect(). + void ConnectCallback(const base::Closure& callback, + const ErrorCallback& error_callback, + const dbus::ObjectPath& device_path); + + // Called by BluetoothAdapterClient when a call to CreateDevice() or + // CreatePairedDevice() fails with the error named |error_name| and + // optional message |error_message|, |error_callback| is the callback + // provided to Connect(). + void ConnectErrorCallback(const ErrorCallback& error_callback, + const std::string& error_name, + const std::string& error_message); + + // Called by BluetoothAdapterClient when a call to DiscoverServices() + // completes. |callback| and |error_callback| are the callbacks provided to + // GetServiceRecords. + void CollectServiceRecordsCallback( + const ServiceRecordsCallback& callback, + const ErrorCallback& error_callback, + const dbus::ObjectPath& device_path, + const BluetoothDeviceClient::ServiceMap& service_map, + bool success); + + // Called by BluetoothProperty when the call to Set() for the Trusted + // property completes. |success| indicates whether or not the request + // succeeded, |callback| and |error_callback| are the callbacks provided to + // Connect(). + void OnSetTrusted(const base::Closure& callback, + const ErrorCallback& error_callback, + bool success); + + // Connect application-level protocols of the device to the system, called + // on a successful connection or to reconnect to a device that is already + // paired or previously connected. |error_callback| is called on failure. + // Otherwise, |callback| is called when the request is complete. + void ConnectApplications(const base::Closure& callback, + const ErrorCallback& error_callback); + + // Called by IntrospectableClient when a call to Introspect() completes. + // |success| indicates whether or not the request succeeded, |callback| and + // |error_callback| are the callbacks provided to ConnectApplications(), + // |service_name| and |device_path| specify the remote object being + // introspected and |xml_data| contains the XML-formatted protocol data. + void OnIntrospect(const base::Closure& callback, + const ErrorCallback& error_callback, + const std::string& service_name, + const dbus::ObjectPath& device_path, + const std::string& xml_data, bool success); + + // Called by BluetoothInputClient when the call to Connect() succeeds. + // |error_callback| is the callback provided to ConnectApplications(), + // |interface_name| specifies the interface being connected and + // |device_path| the remote object path. + void OnConnect(const base::Closure& callback, + const std::string& interface_name, + const dbus::ObjectPath& device_path); + + // Called by BluetoothInputClient when the call to Connect() fails. + // |error_callback| is the callback provided to ConnectApplications(), + // |interface_name| specifies the interface being connected, + // |device_path| the remote object path, + // |error_name| the error name and |error_message| the optional message. + void OnConnectError(const ErrorCallback& error_callback, + const std::string& interface_name, + const dbus::ObjectPath& device_path, + const std::string& error_name, + const std::string& error_message); + + // Called by BluetoothDeviceClient when a call to Disconnect() completes, + // |success| indicates whether or not the request succeeded, |callback| and + // |error_callback| are the callbacks provided to Disconnect() and + // |device_path| is the device disconnected. + void DisconnectCallback(const base::Closure& callback, + const ErrorCallback& error_callback, + const dbus::ObjectPath& device_path, bool success); + + // Called by BluetoothAdapterClient when a call to RemoveDevice() + // completes, |success| indicates whether or not the request succeeded, + // |error_callback| is the callback provided to Forget() and |adapter_path| is + // the d-bus object path of the adapter that performed the removal. + void ForgetCallback(const ErrorCallback& error_callback, + const dbus::ObjectPath& adapter_path, bool success); + + // Called if the call to GetServiceRecords from ProvidesServiceWithName fails. + void SearchServicesForNameErrorCallback( + const ProvidesServiceCallback& callback); + + // Called by GetServiceRecords with the list of BluetoothServiceRecords to + // search for |name|. |callback| is the callback from + // ProvidesServiceWithName. + void SearchServicesForNameCallback( + const std::string& name, + const ProvidesServiceCallback& callback, + const ServiceRecordList& list); + + // Called if the call to GetServiceRecords from Connect fails. + void GetServiceRecordsForConnectErrorCallback( + const SocketCallback& callback); + + // Called by GetServiceRecords with the list of BluetoothServiceRecords. + // Connections are attempted to each service in the list matching + // |service_uuid|, and the socket from the first successful connection is + // passed to |callback|. + void GetServiceRecordsForConnectCallback( + const std::string& service_uuid, + const SocketCallback& callback, + const ServiceRecordList& list); + + // Called by BlueoothDeviceClient in response to the AddRemoteData and + // RemoveRemoteData method calls. + void OnRemoteDataCallback(const base::Closure& callback, + const ErrorCallback& error_callback, + bool success); + + // BluetoothDeviceClient::Observer override. + // + // Called when the device with object path |object_path| is about + // to be disconnected, giving a chance for application layers to + // shut down cleanly. + virtual void DisconnectRequested( + const dbus::ObjectPath& object_path) OVERRIDE; + + // BluetoothAgentServiceProvider::Delegate override. + // + // This method will be called when the agent is unregistered from the + // Bluetooth daemon, generally at the end of a pairing request. It may be + // used to perform cleanup tasks. + virtual void Release() OVERRIDE; + + // BluetoothAgentServiceProvider::Delegate override. + // + // This method will be called when the Bluetooth daemon requires a + // PIN Code for authentication of the device with object path |device_path|, + // the agent should obtain the code from the user and call |callback| + // to provide it, or indicate rejection or cancellation of the request. + // + // PIN Codes are generally required for Bluetooth 2.0 and earlier devices + // for which there is no automatic pairing or special handling. + virtual void RequestPinCode(const dbus::ObjectPath& device_path, + const PinCodeCallback& callback) OVERRIDE; + + // BluetoothAgentServiceProvider::Delegate override. + // + // This method will be called when the Bluetooth daemon requires a + // Passkey for authentication of the device with object path |device_path|, + // the agent should obtain the passkey from the user (a numeric in the + // range 0-999999) and call |callback| to provide it, or indicate + // rejection or cancellation of the request. + // + // Passkeys are generally required for Bluetooth 2.1 and later devices + // which cannot provide input or display on their own, and don't accept + // passkey-less pairing. + virtual void RequestPasskey(const dbus::ObjectPath& device_path, + const PasskeyCallback& callback) OVERRIDE; + + // BluetoothAgentServiceProvider::Delegate override. + // + // This method will be called when the Bluetooth daemon requires that the + // user enter the PIN code |pincode| into the device with object path + // |device_path| so that it may be authenticated. The Cancel() method + // will be called to dismiss the display once pairing is complete or + // cancelled. + // + // This is used for Bluetooth 2.0 and earlier keyboard devices, the + // |pincode| will always be a six-digit numeric in the range 000000-999999 + // for compatibilty with later specifications. + virtual void DisplayPinCode(const dbus::ObjectPath& device_path, + const std::string& pincode) OVERRIDE; + + // BluetoothAgentServiceProvider::Delegate override. + // + // This method will be called when the Bluetooth daemon requires that the + // user enter the Passkey |passkey| into the device with object path + // |device_path| so that it may be authenticated. The Cancel() method + // will be called to dismiss the display once pairing is complete or + // cancelled. + // + // This is used for Bluetooth 2.1 and later devices that support input + // but not display, such as keyboards. The Passkey is a numeric in the + // range 0-999999 and should be always presented zero-padded to six + // digits. + virtual void DisplayPasskey(const dbus::ObjectPath& device_path, + uint32 passkey) OVERRIDE; + + // BluetoothAgentServiceProvider::Delegate override. + // + // This method will be called when the Bluetooth daemon requires that the + // user confirm that the Passkey |passkey| is displayed on the screen + // of the device with object path |object_path| so that it may be + // authentication. The agent should display to the user and ask for + // confirmation, then call |callback| to provide their response (success, + // rejected or cancelled). + // + // This is used for Bluetooth 2.1 and later devices that support display, + // such as other computers or phones. The Passkey is a numeric in the + // range 0-999999 and should be always present zero-padded to six + // digits. + virtual void RequestConfirmation( + const dbus::ObjectPath& device_path, + uint32 passkey, + const ConfirmationCallback& callback) OVERRIDE; + + // BluetoothAgentServiceProvider::Delegate override. + // + // This method will be called when the Bluetooth daemon requires that the + // user confirm that the device with object path |object_path| is + // authorized to connect to the service with UUID |uuid|. The agent should + // confirm with the user and call |callback| to provide their response + // (success, rejected or cancelled). + virtual void Authorize(const dbus::ObjectPath& device_path, + const std::string& uuid, + const ConfirmationCallback& callback) OVERRIDE; + + // BluetoothAgentServiceProvider::Delegate override. + // + // This method will be called when the Bluetooth daemon requires that the + // user confirm that the device adapter may switch to mode |mode|. The + // agent should confirm with the user and call |callback| to provide + // their response (success, rejected or cancelled). + virtual void ConfirmModeChange(Mode mode, + const ConfirmationCallback& callback) OVERRIDE; + + // BluetoothAgentServiceProvider::Delegate override. + // + // This method will be called by the Bluetooth daemon to indicate that + // the request failed before a reply was returned from the device. + virtual void Cancel() OVERRIDE; + + // Creates a new BluetoothDeviceChromeOs object bound to the adapter + // |adapter|. + static BluetoothDeviceChromeOs* Create(BluetoothAdapterChromeOs* adapter); + + // The adapter that owns this device instance. + BluetoothAdapterChromeOs* adapter_; + + // The dbus object path of the device, will be empty if the device has only + // been discovered and not yet paired with. + dbus::ObjectPath object_path_; + + // The services (identified by UUIDs) that this device provides. + std::vector<std::string> service_uuids_; + + // 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. + device::BluetoothDevice::PairingDelegate* pairing_delegate_; + + // During pairing this is set to an instance of a D-Bus agent object + // intialized with our own class as its delegate. + scoped_ptr<BluetoothAgentServiceProvider> 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_; + + // Used to keep track of pending application connection requests. + int connecting_applications_counter_; + + // 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<BluetoothDeviceChromeOs> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothDeviceChromeOs); +}; + +} // namespace chromeos + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_DEVICE_CHROMEOS_H_ diff --git a/device/bluetooth/bluetooth_out_of_band_pairing_data.h b/device/bluetooth/bluetooth_out_of_band_pairing_data.h new file mode 100644 index 0000000..1b45bb0 --- /dev/null +++ b/device/bluetooth/bluetooth_out_of_band_pairing_data.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012 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_OUT_OF_BAND_PAIRING_DATA_H_ +#define DEVICE_BLUETOOTH_BLUETOOTH_OUT_OF_BAND_PAIRING_DATA_H_ + +#include "base/basictypes.h" + +namespace device { + +const size_t kBluetoothOutOfBandPairingDataSize = 16; + +// A simple structure representing the data required to perform Out Of Band +// Pairing. See +// http://mclean-linsky.net/joel/cv/Simple%20Pairing_WP_V10r00.pdf +struct BluetoothOutOfBandPairingData { + // Simple Pairing Hash C. + uint8 hash[kBluetoothOutOfBandPairingDataSize]; + + // Simple Pairing Randomizer R. + uint8 randomizer[kBluetoothOutOfBandPairingDataSize]; +}; + +} // namespace device + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_OUT_OF_BAND_PAIRING_DATA_H_ diff --git a/device/bluetooth/bluetooth_service_record.cc b/device/bluetooth/bluetooth_service_record.cc new file mode 100644 index 0000000..0ea18d7 --- /dev/null +++ b/device/bluetooth/bluetooth_service_record.cc @@ -0,0 +1,122 @@ +// Copyright (c) 2012 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_service_record.h" + +#include <string> +#include <vector> + +#include "base/logging.h" +#include "base/string_number_conversions.h" +#include "device/bluetooth/bluetooth_utils.h" +#include "third_party/libxml/chromium/libxml_utils.h" + +namespace { + +static const char* kAttributeNode = "attribute"; +static const char* kIdAttribute = "id"; +static const char* kProtocolDescriptorListId = "0x0004"; +static const char* kRfcommUuid = "0x0003"; +static const char* kSdpNameId = "0x0100"; +static const char* kSequenceNode = "sequence"; +static const char* kTextNode = "text"; +static const char* kUint8Node = "uint8"; +static const char* kUuidId = "0x0001"; +static const char* kUuidNode = "uuid"; +static const char* kValueAttribute = "value"; + +bool AdvanceToTag(XmlReader* reader, const char* node_type) { + do { + if (!reader->Read()) + return false; + } while (reader->NodeName() != node_type); + return true; +} + +bool ExtractTextValue(XmlReader* reader, std::string* value_out) { + if (AdvanceToTag(reader, kTextNode)) { + reader->NodeAttribute(kValueAttribute, value_out); + return true; + } + return false; +} + +} // namespace + +namespace device { + +BluetoothServiceRecord::BluetoothServiceRecord( + const std::string& address, + const std::string& xml_data) + : address_(address), + supports_rfcomm_(false) { + + XmlReader reader; + if (!reader.Load(xml_data)) + return; + + while (AdvanceToTag(&reader, kAttributeNode)) { + std::string id; + if (reader.NodeAttribute(kIdAttribute, &id)) { + if (id == kSdpNameId) { + ExtractTextValue(&reader, &name_); + } else if (id == kProtocolDescriptorListId) { + if (AdvanceToTag(&reader, kSequenceNode)) { + ExtractChannels(&reader); + } + } else if (id == kUuidId) { + if (AdvanceToTag(&reader, kSequenceNode)) { + ExtractUuid(&reader); + } + } + } + // We don't care about anything else here, so find the closing tag + AdvanceToTag(&reader, kAttributeNode); + } +} + +void BluetoothServiceRecord::ExtractChannels(XmlReader* reader) { + const int start_depth = reader->Depth(); + do { + if (reader->NodeName() == kSequenceNode) { + if (AdvanceToTag(reader, kUuidNode)) { + std::string type; + if (reader->NodeAttribute(kValueAttribute, &type) && + type == kRfcommUuid) { + if (AdvanceToTag(reader, kUint8Node)) { + std::string channel_string; + if (reader->NodeAttribute(kValueAttribute, &channel_string)) { + std::vector<uint8> channel_bytes; + if (base::HexStringToBytes(channel_string.substr(2), + &channel_bytes)) { + if (channel_bytes.size() == 1) { + rfcomm_channel_ = channel_bytes[0]; + supports_rfcomm_ = true; + } + } + } + } + } + } + } + } while (AdvanceToTag(reader, kSequenceNode) && + reader->Depth() != start_depth); +} + +void BluetoothServiceRecord::ExtractUuid(XmlReader* reader) { + const int start_depth = reader->Depth(); + do { + if (reader->NodeName() == kSequenceNode) { + if (AdvanceToTag(reader, kUuidNode)) { + if (!reader->NodeAttribute(kValueAttribute, &uuid_)) + uuid_.clear(); + } + } + } while (AdvanceToTag(reader, kSequenceNode) && + reader->Depth() != start_depth); + + uuid_ = device::bluetooth_utils::CanonicalUuid(uuid_); +} + +} // namespace device diff --git a/device/bluetooth/bluetooth_service_record.h b/device/bluetooth/bluetooth_service_record.h new file mode 100644 index 0000000..e04d73a --- /dev/null +++ b/device/bluetooth/bluetooth_service_record.h @@ -0,0 +1,59 @@ +// Copyright (c) 2012 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_SERVICE_RECORD_H_ +#define DEVICE_BLUETOOTH_BLUETOOTH_SERVICE_RECORD_H_ + +#include <string> + +#include "base/basictypes.h" + +class XmlReader; + +namespace device { + +// BluetoothServiceRecord represents an SDP service record. +// +// This implementation is currently incomplete: it only supports those fields +// that have been necessary so far. +class BluetoothServiceRecord { + public: + BluetoothServiceRecord( + const std::string& address, + const std::string& xml_data); + + // The human-readable name of this service. + const std::string& name() const { return name_; } + + // The address of the BluetoothDevice providing this service. + const std::string& address() const { return address_; } + + // The UUID of the service. This field may be empty if no UUID was + // specified in the service record. + const std::string& uuid() const { return uuid_; } + + // Indicates if this service supports RFCOMM communication. + bool SupportsRfcomm() const { return supports_rfcomm_; } + + // The RFCOMM channel to use, if this service supports RFCOMM communication. + // The return value is undefined if SupportsRfcomm() returns false. + uint8 rfcomm_channel() const { return rfcomm_channel_; } + + private: + void ExtractChannels(XmlReader* reader); + void ExtractUuid(XmlReader* reader); + + std::string address_; + std::string name_; + std::string uuid_; + + bool supports_rfcomm_; + uint8 rfcomm_channel_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothServiceRecord); +}; + +} // namespace device + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_SERVICE_RECORD_H_ diff --git a/device/bluetooth/bluetooth_service_record_unittest.cc b/device/bluetooth/bluetooth_service_record_unittest.cc new file mode 100644 index 0000000..b59474f5 --- /dev/null +++ b/device/bluetooth/bluetooth_service_record_unittest.cc @@ -0,0 +1,78 @@ +// Copyright (c) 2012 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 <string> + +#include "base/base_paths.h" +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "device/bluetooth/bluetooth_service_record.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +static const char* kAddress = "01:02:03:04:05:06"; +static const char* kCustomUuid = "01234567-89ab-cdef-0123-456789abcdef"; +static const char* kSerialUuid = "00001101-0000-1000-8000-00805f9b34fb"; + +} // namespace + +namespace device { + +class BluetoothServiceRecordTest : public testing::Test { + public: + FilePath GetTestDataFilePath(const char* file) { + FilePath path; + PathService::Get(base::DIR_SOURCE_ROOT, &path); + path = path.AppendASCII("device"); + path = path.AppendASCII("test"); + path = path.AppendASCII("data"); + path = path.AppendASCII("bluetooth"); + path = path.AppendASCII(file); + return path; + } +}; + +TEST_F(BluetoothServiceRecordTest, RfcommService) { + std::string xml_data; + file_util::ReadFileToString(GetTestDataFilePath("rfcomm.xml"), &xml_data); + + BluetoothServiceRecord service_record(kAddress, xml_data); + EXPECT_EQ(kAddress, service_record.address()); + EXPECT_EQ("Headset Audio Gateway", service_record.name()); + EXPECT_TRUE(service_record.SupportsRfcomm()); + EXPECT_EQ((uint8)12, service_record.rfcomm_channel()); + EXPECT_EQ(kCustomUuid, service_record.uuid()); +} + +TEST_F(BluetoothServiceRecordTest, ShortUuid) { + std::string xml_data; + file_util::ReadFileToString(GetTestDataFilePath("short_uuid.xml"), &xml_data); + BluetoothServiceRecord short_uuid_service_record(kAddress, xml_data); + EXPECT_EQ(kSerialUuid, short_uuid_service_record.uuid()); + + xml_data.clear(); + file_util::ReadFileToString( + GetTestDataFilePath("medium_uuid.xml"), &xml_data); + BluetoothServiceRecord medium_uuid_service_record(kAddress, xml_data); + EXPECT_EQ(kSerialUuid, medium_uuid_service_record.uuid()); +} + +TEST_F(BluetoothServiceRecordTest, CleanUuid) { + std::string xml_data; + file_util::ReadFileToString(GetTestDataFilePath("uppercase_uuid.xml"), + &xml_data); + BluetoothServiceRecord service_record(kAddress, xml_data); + EXPECT_EQ(kCustomUuid, service_record.uuid()); + + xml_data.clear(); + file_util::ReadFileToString(GetTestDataFilePath("invalid_uuid.xml"), + &xml_data); + BluetoothServiceRecord invalid_service_record(kAddress, xml_data); + EXPECT_EQ("", invalid_service_record.uuid()); +} + +} // namespace device diff --git a/device/bluetooth/bluetooth_socket.h b/device/bluetooth/bluetooth_socket.h new file mode 100644 index 0000000..b444110 --- /dev/null +++ b/device/bluetooth/bluetooth_socket.h @@ -0,0 +1,30 @@ +// Copyright (c) 2012 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_H_ +#define DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_H_ + +#include "base/memory/ref_counted.h" + +namespace device { + +// BluetoothSocket represents a socket to a specific service on a +// BluetoothDevice. BluetoothSocket objects are ref counted and may outlive +// both the BluetoothDevice and BluetoothAdapter that were involved in their +// creation. +class BluetoothSocket : public base::RefCounted<BluetoothSocket> { + public: + // TODO(youngki): Replace this with an opaque id when read/write calls are + // added. This interface is platform-independent and file descriptor is + // linux-specific hence this method has to be renamed. + virtual int fd() const = 0; + + protected: + friend class base::RefCounted<BluetoothSocket>; + virtual ~BluetoothSocket() {} +}; + +} // namespace device + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_H_ diff --git a/device/bluetooth/bluetooth_socket_chromeos.cc b/device/bluetooth/bluetooth_socket_chromeos.cc new file mode 100644 index 0000000..7e75a9d --- /dev/null +++ b/device/bluetooth/bluetooth_socket_chromeos.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2012 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 <vector> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> +#include <errno.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <string.h> +#include <unistd.h> + +#include "base/logging.h" +#include "device/bluetooth/bluetooth_service_record.h" +#include "device/bluetooth/bluetooth_utils.h" + +using device::BluetoothServiceRecord; +using device::BluetoothSocket; + +namespace chromeos { + +BluetoothSocketChromeOs::BluetoothSocketChromeOs( + const std::string& address, int fd) + : address_(address), + fd_(fd) { +} + +BluetoothSocketChromeOs::~BluetoothSocketChromeOs() { + close(fd_); +} + +// static +scoped_refptr<BluetoothSocket> BluetoothSocketChromeOs::CreateBluetoothSocket( + const BluetoothServiceRecord& service_record) { + BluetoothSocketChromeOs* bluetooth_socket = NULL; + if (service_record.SupportsRfcomm()) { + int socket_fd = socket( + AF_BLUETOOTH, SOCK_STREAM | SOCK_NONBLOCK, BTPROTO_RFCOMM); + struct sockaddr_rc socket_address = { 0 }; + socket_address.rc_family = AF_BLUETOOTH; + socket_address.rc_channel = service_record.rfcomm_channel(); + device::bluetooth_utils::str2ba(service_record.address(), + &socket_address.rc_bdaddr); + + int status = connect(socket_fd, (struct sockaddr *)&socket_address, + sizeof(socket_address)); + int errsv = errno; + if (status == 0 || errno == EINPROGRESS) { + bluetooth_socket = new BluetoothSocketChromeOs(service_record.address(), + socket_fd); + } else { + LOG(ERROR) << "Failed to connect bluetooth socket " + << "(" << service_record.address() << "): " + << "(" << errsv << ") " << strerror(errsv); + close(socket_fd); + } + } + // TODO(bryeung): add support for L2CAP sockets as well. + + return scoped_refptr<BluetoothSocketChromeOs>(bluetooth_socket); +} + +int BluetoothSocketChromeOs::fd() const { + return fd_; +} + +} // namespace chromeos diff --git a/device/bluetooth/bluetooth_socket_chromeos.h b/device/bluetooth/bluetooth_socket_chromeos.h new file mode 100644 index 0000000..0685830 --- /dev/null +++ b/device/bluetooth/bluetooth_socket_chromeos.h @@ -0,0 +1,45 @@ +// Copyright (c) 2012 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 <string> + +#include "base/memory/ref_counted.h" +#include "device/bluetooth/bluetooth_socket.h" + +namespace device { + +class BluetoothServiceRecord; + +} // namespace device + +namespace chromeos { + +// This class is an implementation of BluetoothSocket class for Chrome OS +// platform. +class BluetoothSocketChromeOs : public device::BluetoothSocket { + public: + static scoped_refptr<device::BluetoothSocket> CreateBluetoothSocket( + const device::BluetoothServiceRecord& service_record); + + // BluetoothSocket override + virtual int fd() const OVERRIDE; + + protected: + virtual ~BluetoothSocketChromeOs(); + + private: + BluetoothSocketChromeOs(const std::string& address, int fd); + + const std::string address_; + const int fd_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothSocketChromeOs); +}; + +} // namespace chromeos + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_SOCKET_CHROMEOS_H_ diff --git a/device/bluetooth/bluetooth_utils.cc b/device/bluetooth/bluetooth_utils.cc new file mode 100644 index 0000000..6d06409 --- /dev/null +++ b/device/bluetooth/bluetooth_utils.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2012 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_utils.h" + +#include <vector> + +#if defined(OS_CHROMEOS) +#include <bluetooth/bluetooth.h> +#endif + +#include "base/logging.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" + +namespace { +static const char* kCommonUuidPostfix = "-0000-1000-8000-00805f9b34fb"; +static const char* kCommonUuidPrefix = "0000"; +static const int kUuidSize = 36; +} // namespace + +namespace device { +namespace bluetooth_utils { + +#if defined(OS_CHROMEOS) +bool str2ba(const std::string& in_address, bdaddr_t* out_address) { + if (!out_address) + return false; + + memset(out_address, 0, sizeof(*out_address)); + + if (in_address.size() != 17) + return false; + + std::string numbers_only; + for (int i = 0; i < 6; ++i) { + numbers_only += in_address.substr(i * 3, 2); + } + + std::vector<uint8> address_bytes; + if (base::HexStringToBytes(numbers_only, &address_bytes)) { + if (address_bytes.size() == 6) { + for (int i = 0; i < 6; ++i) { + out_address->b[5 - i] = address_bytes[i]; + } + return true; + } + } + + return false; +} +#endif + +std::string CanonicalUuid(std::string uuid) { + if (uuid.empty()) + return ""; + + if (uuid.size() < 11 && uuid.find("0x") == 0) + uuid = uuid.substr(2); + + if (!(uuid.size() == 4 || uuid.size() == 8 || uuid.size() == 36)) + return ""; + + if (uuid.size() == 4 || uuid.size() == 8) { + for (size_t i = 0; i < uuid.size(); ++i) { + if (!IsHexDigit(uuid[i])) + return ""; + } + + if (uuid.size() == 4) + return kCommonUuidPrefix + uuid + kCommonUuidPostfix; + + return uuid + kCommonUuidPostfix; + } + + std::string uuid_result(uuid); + for (int i = 0; i < kUuidSize; ++i) { + if (i == 8 || i == 13 || i == 18 || i == 23) { + if (uuid[i] != '-') + return ""; + } else { + if (!IsHexDigit(uuid[i])) + return ""; + uuid_result[i] = tolower(uuid[i]); + } + } + return uuid_result; +} + +} // namespace bluetooth_utils +} // namespace device diff --git a/device/bluetooth/bluetooth_utils.h b/device/bluetooth/bluetooth_utils.h new file mode 100644 index 0000000..f796d5d --- /dev/null +++ b/device/bluetooth/bluetooth_utils.h @@ -0,0 +1,42 @@ +// Copyright (c) 2012 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_UTILS_H_ +#define DEVICE_BLUETOOTH_BLUETOOTH_UTILS_H_ + +#include <string> + +#if defined(OS_CHROMEOS) +#include <bluetooth/bluetooth.h> +#endif + +namespace device { +namespace bluetooth_utils { + +#if defined(OS_CHROMEOS) +// Converts a bluetooth address in the format "B0:D0:9C:0F:3A:2D" into a +// bdaddr_t struct. Returns true on success, false on failure. The contents +// of |out_address| are zeroed on failure. +// Note that the order is reversed upon conversion. For example, +// "B0:D0:9C:0F:3A:2D" -> {"0x2d", "0x3a", "0x0f", "0x9c", "0xd0", "0xb0"} +bool str2ba(const std::string& in_address, bdaddr_t* out_address); +#endif + +// Takes a 4, 8 or 36 character UUID, validates it and returns it in 36 +// character format with all hex digits lower case. If |uuid| is invalid, the +// empty string is returned. +// +// Valid inputs are: +// XXXX +// 0xXXXX +// XXXXXXXX +// 0xXXXXXXXX +// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX +std::string CanonicalUuid(std::string uuid); + +} // namespace bluetooth_utils +} // namespace device + +#endif // DEVICE_BLUETOOTH_BLUETOOTH_UTILS_H_ + diff --git a/device/bluetooth/bluetooth_utils_unittest.cc b/device/bluetooth/bluetooth_utils_unittest.cc new file mode 100644 index 0000000..0e05f8e --- /dev/null +++ b/device/bluetooth/bluetooth_utils_unittest.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2012 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. + +#if defined(OS_CHROMEOS) +#include <bluetooth/bluetooth.h> +#endif + +#include "device/bluetooth/bluetooth_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace device { + +#if defined(OS_CHROMEOS) +TEST(BluetoothUtilsTest, str2ba) { + bdaddr_t bluetooth_address; + + EXPECT_TRUE(bluetooth_utils::str2ba("01:02:03:0A:10:A0", &bluetooth_address)); + EXPECT_EQ(1, bluetooth_address.b[5]); + EXPECT_EQ(2, bluetooth_address.b[4]); + EXPECT_EQ(3, bluetooth_address.b[3]); + EXPECT_EQ(10, bluetooth_address.b[2]); + EXPECT_EQ(16, bluetooth_address.b[1]); + EXPECT_EQ(160, bluetooth_address.b[0]); + + EXPECT_FALSE(bluetooth_utils::str2ba("obviously wrong", &bluetooth_address)); + EXPECT_FALSE(bluetooth_utils::str2ba("00:00", &bluetooth_address)); + EXPECT_FALSE( + bluetooth_utils::str2ba("00:00:00:00:00:00:00", &bluetooth_address)); + EXPECT_FALSE(bluetooth_utils::str2ba("01:02:03:0A:10:A0", NULL)); +} +#endif + +TEST(BluetoothUtilsTest, CanonicalUuid) { + // Does nothing for an already canonical UUID + EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb", + bluetooth_utils::CanonicalUuid("00001101-0000-1000-8000-00805f9b34fb")); + + // Rejects misformatted + EXPECT_EQ("", bluetooth_utils::CanonicalUuid("1101a")); + EXPECT_EQ("", bluetooth_utils::CanonicalUuid("Z101")); + EXPECT_EQ("", bluetooth_utils::CanonicalUuid("0000-1101")); + EXPECT_EQ("", bluetooth_utils::CanonicalUuid("0000Z101")); + EXPECT_EQ("", + bluetooth_utils::CanonicalUuid("0001101-0000-1000-8000-00805f9b34fb")); + EXPECT_EQ("", + bluetooth_utils::CanonicalUuid("Z0001101-0000-1000-8000-00805f9b34fb")); + EXPECT_EQ("", + bluetooth_utils::CanonicalUuid("00001101 0000-1000-8000-00805f9b34fb")); + EXPECT_EQ("", + bluetooth_utils::CanonicalUuid("00001101-0000:1000-8000-00805f9b34fb")); + EXPECT_EQ("", + bluetooth_utils::CanonicalUuid("00001101-0000-1000;8000-00805f9b34fb")); + EXPECT_EQ("", + bluetooth_utils::CanonicalUuid("00001101-0000-1000-8000000805f9b34fb")); + + // Lower case + EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb", + bluetooth_utils::CanonicalUuid("00001101-0000-1000-8000-00805F9B34FB")); + + // Short to full + EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb", + bluetooth_utils::CanonicalUuid("1101")); + EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb", + bluetooth_utils::CanonicalUuid("0x1101")); + EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb", + bluetooth_utils::CanonicalUuid("00001101")); + EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb", + bluetooth_utils::CanonicalUuid("0x00001101")); + + // No 0x prefix on 36 character + EXPECT_EQ("", + bluetooth_utils::CanonicalUuid("0x00001101-0000-1000-8000-00805f9b34fb")); +} + +} // namespace device diff --git a/device/bluetooth/test/mock_bluetooth_adapter.cc b/device/bluetooth/test/mock_bluetooth_adapter.cc new file mode 100644 index 0000000..cae8829 --- /dev/null +++ b/device/bluetooth/test/mock_bluetooth_adapter.cc @@ -0,0 +1,20 @@ +// Copyright (c) 2012 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/test/mock_bluetooth_adapter.h" + +namespace device { + +MockBluetoothAdapter::Observer::Observer() {} +MockBluetoothAdapter::Observer::~Observer() {} + +MockBluetoothAdapter::MockBluetoothAdapter(const std::string& address, + const std::string& name) { + address_ = address; + name_ = name; +} + +MockBluetoothAdapter::~MockBluetoothAdapter() {} + +} // namespace device diff --git a/device/bluetooth/test/mock_bluetooth_adapter.h b/device/bluetooth/test/mock_bluetooth_adapter.h new file mode 100644 index 0000000..33f7afb --- /dev/null +++ b/device/bluetooth/test/mock_bluetooth_adapter.h @@ -0,0 +1,61 @@ +// Copyright (c) 2012 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_TEST_MOCK_BLUETOOTH_ADAPTER_H_ +#define DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_ADAPTER_H_ + +#include <string> + +#include "base/callback.h" +#include "device/bluetooth/bluetooth_adapter.h" +#include "device/bluetooth/bluetooth_device.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace device { + +class MockBluetoothAdapter : public BluetoothAdapter { + public: + class Observer : public BluetoothAdapter::Observer { + public: + Observer(); + virtual ~Observer(); + + MOCK_METHOD2(AdapterPresentChanged, void(BluetoothAdapter*, bool)); + MOCK_METHOD2(AdapterPoweredChanged, void(BluetoothAdapter*, bool)); + MOCK_METHOD2(AdapterDiscoveringChanged, void(BluetoothAdapter*, bool)); + MOCK_METHOD2(DeviceAdded, void(BluetoothAdapter*, BluetoothDevice*)); + MOCK_METHOD2(DeviceChanged, void(BluetoothAdapter*, BluetoothDevice*)); + MOCK_METHOD2(DeviceRemoved, void(BluetoothAdapter*, BluetoothDevice*)); + }; + + MockBluetoothAdapter(const std::string& address, const std::string& name); + + MOCK_METHOD1(AddObserver, void(BluetoothAdapter::Observer*)); + MOCK_METHOD1(RemoveObserver, void(BluetoothAdapter::Observer*)); + MOCK_CONST_METHOD0(IsPresent, bool()); + MOCK_CONST_METHOD0(IsPowered, bool()); + MOCK_METHOD3(SetPowered, + void(bool discovering, + const base::Closure& callback, + const ErrorCallback& error_callback)); + MOCK_CONST_METHOD0(IsDiscovering, bool()); + MOCK_METHOD3(SetDiscovering, + void(bool discovering, + const base::Closure& callback, + const ErrorCallback& error_callback)); + MOCK_CONST_METHOD0(GetDevices, BluetoothAdapter::ConstDeviceList()); + MOCK_METHOD1(GetDevice, BluetoothDevice*(const std::string& address)); + MOCK_CONST_METHOD1(GetDevice, + const BluetoothDevice*(const std::string& address)); + MOCK_METHOD2( + ReadLocalOutOfBandPairingData, + void(const BluetoothOutOfBandPairingDataCallback& callback, + const ErrorCallback& error_callback)); + protected: + virtual ~MockBluetoothAdapter(); +}; + +} // namespace device + +#endif // DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_ADAPTER_H_ diff --git a/device/bluetooth/test/mock_bluetooth_device.cc b/device/bluetooth/test/mock_bluetooth_device.cc new file mode 100644 index 0000000..f2f358d --- /dev/null +++ b/device/bluetooth/test/mock_bluetooth_device.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2012 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/utf_string_conversions.h" +#include "device/bluetooth/test/mock_bluetooth_adapter.h" +#include "device/bluetooth/test/mock_bluetooth_device.h" + +namespace device { + +MockBluetoothDevice::MockBluetoothDevice(MockBluetoothAdapter* adapter, + const std::string& name, + const std::string& address, + bool paired, + bool bonded, + bool connected) + : name_(UTF8ToUTF16(name)), + address_(address) { + ON_CALL(*this, GetName()) + .WillByDefault(testing::Return(name_)); + ON_CALL(*this, address()) + .WillByDefault(testing::ReturnRef(address_)); + ON_CALL(*this, IsPaired()) + .WillByDefault(testing::Return(paired)); + ON_CALL(*this, IsBonded()) + .WillByDefault(testing::Return(bonded)); + ON_CALL(*this, IsConnected()) + .WillByDefault(testing::Return(connected)); + ON_CALL(*this, ExpectingPinCode()) + .WillByDefault(testing::Return(false)); + ON_CALL(*this, ExpectingPasskey()) + .WillByDefault(testing::Return(false)); + ON_CALL(*this, ExpectingConfirmation()) + .WillByDefault(testing::Return(false)); + ON_CALL(*this, GetServices()) + .WillByDefault(testing::ReturnRef(service_list_)); +} + +MockBluetoothDevice::~MockBluetoothDevice() {} + +} // namespace device diff --git a/device/bluetooth/test/mock_bluetooth_device.h b/device/bluetooth/test/mock_bluetooth_device.h new file mode 100644 index 0000000..e917a30 --- /dev/null +++ b/device/bluetooth/test/mock_bluetooth_device.h @@ -0,0 +1,79 @@ +// Copyright (c) 2012 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_TEST_MOCK_BLUETOOTH_DEVICE_H_ +#define DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_DEVICE_H_ + +#include <string> + +#include "base/string16.h" +#include "device/bluetooth/bluetooth_device.h" +#include "device/bluetooth/bluetooth_out_of_band_pairing_data.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace device { + +class MockBluetoothAdapter; + +class MockBluetoothDevice : public BluetoothDevice { + public: + MockBluetoothDevice(MockBluetoothAdapter* adapter, + const std::string& name, + const std::string& address, + bool paired, + bool bonded, + bool connected); + virtual ~MockBluetoothDevice(); + + MOCK_CONST_METHOD0(address, const std::string&()); + MOCK_CONST_METHOD0(GetName, string16()); + MOCK_CONST_METHOD0(GetDeviceType, BluetoothDevice::DeviceType()); + MOCK_CONST_METHOD0(IsPaired, bool()); + MOCK_CONST_METHOD0(IsBonded, bool()); + MOCK_CONST_METHOD0(IsConnected, bool()); + MOCK_CONST_METHOD0(GetServices, const ServiceList&()); + MOCK_METHOD2(GetServiceRecords, + void(const BluetoothDevice::ServiceRecordsCallback&, + const BluetoothDevice::ErrorCallback&)); + MOCK_CONST_METHOD1(ProvidesServiceWithUUID, bool(const std::string&)); + MOCK_METHOD2(ProvidesServiceWithName, + void(const std::string&, + const BluetoothDevice::ProvidesServiceCallback&)); + MOCK_CONST_METHOD0(ExpectingPinCode, bool()); + MOCK_CONST_METHOD0(ExpectingPasskey, bool()); + MOCK_CONST_METHOD0(ExpectingConfirmation, bool()); + MOCK_METHOD3(Connect, + void(BluetoothDevice::PairingDelegate* pairnig_delegate, + const base::Closure& callback, + const BluetoothDevice::ErrorCallback& error_callback)); + MOCK_METHOD1(SetPinCode, void(const std::string&)); + MOCK_METHOD1(SetPasskey, void(uint32)); + MOCK_METHOD0(ConfirmPairing, void()); + MOCK_METHOD0(RejectPairing, void()); + MOCK_METHOD0(CancelPairing, void()); + MOCK_METHOD2(Disconnect, + void(const base::Closure& callback, + const BluetoothDevice::ErrorCallback& error_callback)); + MOCK_METHOD1(Forget, void(const BluetoothDevice::ErrorCallback&)); + MOCK_METHOD2(ConnectToService, + void(const std::string&, + const BluetoothDevice::SocketCallback&)); + + MOCK_METHOD3(SetOutOfBandPairingData, + void(const BluetoothOutOfBandPairingData& data, + const base::Closure& callback, + const BluetoothDevice::ErrorCallback& error_callback)); + MOCK_METHOD2(ClearOutOfBandPairingData, + void(const base::Closure& callback, + const BluetoothDevice::ErrorCallback& error_callback)); + + private: + string16 name_; + std::string address_; + BluetoothDevice::ServiceList service_list_; +}; + +} // namespace device + +#endif // DEVICE_BLUETOOTH_TEST_MOCK_BLUETOOTH_DEVICE_H_ diff --git a/device/device.gyp b/device/device.gyp new file mode 100644 index 0000000..4b4c1ad --- /dev/null +++ b/device/device.gyp @@ -0,0 +1,111 @@ +# Copyright (c) 2012 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. + +{ + 'variables': { + }, + 'targets': [ + { + 'target_name': 'device_bluetooth', + 'type': '<(library)', + 'dependencies': [ + '../chrome/chrome_resources.gyp:chrome_strings', + '../third_party/libxml/libxml.gyp:libxml', + '../ui/ui.gyp:ui' + ], + 'sources': [ + 'bluetooth/bluetooth_adapter.cc', + 'bluetooth/bluetooth_adapter.h', + 'bluetooth/bluetooth_adapter_chromeos.cc', + 'bluetooth/bluetooth_adapter_chromeos.h', + 'bluetooth/bluetooth_adapter_factory.cc', + 'bluetooth/bluetooth_adapter_factory.h', + 'bluetooth/bluetooth_device.cc', + 'bluetooth/bluetooth_device.h', + 'bluetooth/bluetooth_device_chromeos.cc', + 'bluetooth/bluetooth_device_chromeos.h', + 'bluetooth/bluetooth_out_of_band_pairing_data.h', + 'bluetooth/bluetooth_service_record.cc', + 'bluetooth/bluetooth_service_record.h', + 'bluetooth/bluetooth_socket.h', + 'bluetooth/bluetooth_socket_chromeos.cc', + 'bluetooth/bluetooth_socket_chromeos.h', + 'bluetooth/bluetooth_utils.cc', + 'bluetooth/bluetooth_utils.h', + ], + 'conditions': [ + ['chromeos==0', { + 'sources!': [ + # ChromeOs-only; exclude on other platforms. + 'bluetooth/bluetooth_adapter_chromeos.cc', + 'bluetooth/bluetooth_adapter_chromeos.h', + 'bluetooth/bluetooth_device_chromeos.cc', + 'bluetooth/bluetooth_device_chromeos.h', + 'bluetooth/bluetooth_socket_chromeos.cc', + 'bluetooth/bluetooth_socket_chromeos.h', + ] + }, { # chromeos==1 + 'dependencies': [ + '../build/linux/system.gyp:dbus-glib', + '../chromeos/chromeos.gyp:chromeos', + '../dbus/dbus.gyp:dbus', + ] + }], + ], + }, + { + 'target_name': 'device_bluetooth_mocks', + 'type': '<(library)', + 'dependencies': [ + 'device_bluetooth', + '../testing/gmock.gyp:gmock', + ], + 'sources': [ + 'bluetooth/test/mock_bluetooth_adapter.cc', + 'bluetooth/test/mock_bluetooth_adapter.h', + 'bluetooth/test/mock_bluetooth_device.cc', + 'bluetooth/test/mock_bluetooth_device.h', + ], + 'include_dirs': [ + '..', + ], + }, + { + 'target_name': 'device_unittests', + 'type': '<(gtest_target_type)', + 'dependencies': [ + 'device_bluetooth', + 'device_bluetooth_mocks', + '../base/base.gyp:test_support_base', + '../content/content.gyp:test_support_content', + '../testing/gmock.gyp:gmock', + '../testing/gtest.gyp:gtest', + ], + 'sources': [ + 'bluetooth/bluetooth_adapter_chromeos_unittest.cc', + 'bluetooth/bluetooth_adapter_chromeos_devices_unittest.cc', + 'bluetooth/bluetooth_service_record_unittest.cc', + 'bluetooth/bluetooth_utils_unittest.cc', + 'test/device_test_suite.cc', + 'test/device_test_suite.h', + 'test/run_all_unittests.cc', + ], + 'conditions': [ + ['chromeos==0', { + 'sources!': [ + # ChromeOs-only; exclude on other platforms. + 'bluetooth/bluetooth_adapter_chromeos_unittest.cc', + 'bluetooth/bluetooth_adapter_chromeos_devices_unittest.cc', + ] + }, { # chromeos==1 + 'dependencies': [ + '../build/linux/system.gyp:dbus-glib', + '../chromeos/chromeos.gyp:chromeos_test_support', + '../dbus/dbus.gyp:dbus', + ] + }], + ], + }, + ], +} diff --git a/device/test/data/bluetooth/invalid_uuid.xml b/device/test/data/bluetooth/invalid_uuid.xml new file mode 100644 index 0000000..2b33304 --- /dev/null +++ b/device/test/data/bluetooth/invalid_uuid.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<record> + <attribute id="0x0001"> + <sequence> + <uuid value="01234567:89AB-CDEF-0123-456789ABCDEF" /> + </sequence> + </attribute> +</record> diff --git a/device/test/data/bluetooth/medium_uuid.xml b/device/test/data/bluetooth/medium_uuid.xml new file mode 100644 index 0000000..432d7fe --- /dev/null +++ b/device/test/data/bluetooth/medium_uuid.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<record> + <attribute id="0x0001"> + <sequence> + <uuid value="0x00001101" /> + </sequence> + </attribute> +</record> diff --git a/device/test/data/bluetooth/rfcomm.xml b/device/test/data/bluetooth/rfcomm.xml new file mode 100644 index 0000000..ec3bdec --- /dev/null +++ b/device/test/data/bluetooth/rfcomm.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<record> + <attribute id="0x0000"> + <uint32 value="0x00010003" /> + </attribute> + <attribute id="0x0001"> + <sequence> + <uuid value="01234567-89ab-cdef-0123-456789abcdef" /> + </sequence> + </attribute> + <attribute id="0x0004"> + <sequence> + <sequence> + <uuid value="0x0100" /> + </sequence> + <sequence> + <uuid value="0x0003" /> + <uint8 value="0x0c" /> + </sequence> + </sequence> + </attribute> + <attribute id="0x0005"> + <sequence> + <uuid value="0x1002" /> + </sequence> + </attribute> + <attribute id="0x0009"> + <sequence> + <sequence> + <uuid value="0x1108" /> + <uint16 value="0x0102" /> + </sequence> + </sequence> + </attribute> + <attribute id="0x0100"> + <text value="Headset Audio Gateway" /> + </attribute> +</record> diff --git a/device/test/data/bluetooth/short_uuid.xml b/device/test/data/bluetooth/short_uuid.xml new file mode 100644 index 0000000..9ad3c9f --- /dev/null +++ b/device/test/data/bluetooth/short_uuid.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<record> + <attribute id="0x0001"> + <sequence> + <uuid value="0x1101" /> + </sequence> + </attribute> +</record> diff --git a/device/test/data/bluetooth/uppercase_uuid.xml b/device/test/data/bluetooth/uppercase_uuid.xml new file mode 100644 index 0000000..4e0574f --- /dev/null +++ b/device/test/data/bluetooth/uppercase_uuid.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<record> + <attribute id="0x0001"> + <sequence> + <uuid value="01234567-89AB-CDEF-0123-456789ABCDEF" /> + </sequence> + </attribute> +</record> diff --git a/device/test/device_test_suite.cc b/device/test/device_test_suite.cc new file mode 100644 index 0000000..6556076 --- /dev/null +++ b/device/test/device_test_suite.cc @@ -0,0 +1,18 @@ +// Copyright (c) 2012 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/test/device_test_suite.h" + +#include "content/public/common/content_client.h" + +DeviceTestSuite::DeviceTestSuite(int argc, char** argv) + : content::ContentTestSuiteBase(argc, argv) { +} + +DeviceTestSuite::~DeviceTestSuite() { +} + +content::ContentClient* DeviceTestSuite::CreateClientForInitialization() { + return new content::ContentClient(); +} diff --git a/device/test/device_test_suite.h b/device/test/device_test_suite.h new file mode 100644 index 0000000..15059ab --- /dev/null +++ b/device/test/device_test_suite.h @@ -0,0 +1,19 @@ +// Copyright (c) 2012 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_TEST_DEVICE_TEST_SUITE_H_ +#define DEVICE_TEST_DEVICE_TEST_SUITE_H_ + +#include "content/public/test/content_test_suite_base.h" + +class DeviceTestSuite : public content::ContentTestSuiteBase { + public: + DeviceTestSuite(int argc, char** argv); + virtual ~DeviceTestSuite(); + + protected: + virtual content::ContentClient* CreateClientForInitialization() OVERRIDE; +}; + +#endif // DEVICE_TEST_DEVICE_TEST_SUITE_H_ diff --git a/device/test/run_all_unittests.cc b/device/test/run_all_unittests.cc new file mode 100644 index 0000000..36e6e51 --- /dev/null +++ b/device/test/run_all_unittests.cc @@ -0,0 +1,10 @@ +// Copyright (c) 2011 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 "content/public/test/unittest_test_suite.h" +#include "device/test/device_test_suite.h" + +int main(int argc, char **argv) { + return content::UnitTestTestSuite(new DeviceTestSuite(argc, argv)).Run(); +} |