diff options
author | rkc <rkc@chromium.org> | 2015-10-06 04:25:52 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-10-06 11:26:53 +0000 |
commit | c7eeff6a15962486725ac28b0ded7fd9296f1e77 (patch) | |
tree | 9658dfae6f757fab11e852a17427c43160399ad7 /chromeos | |
parent | 6a4640a83d99484907275aac3bec32c474cb6c63 (diff) | |
download | chromium_src-c7eeff6a15962486725ac28b0ded7fd9296f1e77.zip chromium_src-c7eeff6a15962486725ac28b0ded7fd9296f1e77.tar.gz chromium_src-c7eeff6a15962486725ac28b0ded7fd9296f1e77.tar.bz2 |
Revert "Refactor DBusThreadManager to split away BT clients."
This reverts commit 5d3c8959a7aee861b6750fd734cad96bd24e1f9f.
This commit is breaking the valgrind tests.
R=rkc@chromium.org
TBR=armansito@chromium.org, stevenjb@chromium.org, sky@chromium.org
BUG=None.
Review URL: https://codereview.chromium.org/1382923003
Cr-Commit-Position: refs/heads/master@{#352581}
Diffstat (limited to 'chromeos')
81 files changed, 14323 insertions, 0 deletions
diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp index abda945..4ccdfca 100644 --- a/chromeos/chromeos.gyp +++ b/chromeos/chromeos.gyp @@ -59,6 +59,44 @@ 'dbus/audio_node.h', 'dbus/blocking_method_caller.cc', 'dbus/blocking_method_caller.h', + 'dbus/bluetooth_adapter_client.cc', + 'dbus/bluetooth_adapter_client.h', + 'dbus/bluetooth_le_advertising_manager_client.cc', + 'dbus/bluetooth_le_advertising_manager_client.h', + 'dbus/bluetooth_le_advertisement_service_provider.cc', + 'dbus/bluetooth_le_advertisement_service_provider.h', + 'dbus/bluetooth_agent_manager_client.cc', + 'dbus/bluetooth_agent_manager_client.h', + 'dbus/bluetooth_agent_service_provider.cc', + 'dbus/bluetooth_agent_service_provider.h', + 'dbus/bluetooth_device_client.cc', + 'dbus/bluetooth_device_client.h', + 'dbus/bluetooth_gatt_characteristic_client.cc', + 'dbus/bluetooth_gatt_characteristic_client.h', + 'dbus/bluetooth_gatt_characteristic_service_provider.cc', + 'dbus/bluetooth_gatt_characteristic_service_provider.h', + 'dbus/bluetooth_gatt_descriptor_client.cc', + 'dbus/bluetooth_gatt_descriptor_client.h', + 'dbus/bluetooth_gatt_descriptor_service_provider.cc', + 'dbus/bluetooth_gatt_descriptor_service_provider.h', + 'dbus/bluetooth_gatt_manager_client.cc', + 'dbus/bluetooth_gatt_manager_client.h', + 'dbus/bluetooth_gatt_service_client.cc', + 'dbus/bluetooth_gatt_service_client.h', + 'dbus/bluetooth_gatt_service_service_provider.cc', + 'dbus/bluetooth_gatt_service_service_provider.h', + 'dbus/bluetooth_input_client.cc', + 'dbus/bluetooth_input_client.h', + 'dbus/bluetooth_media_client.cc', + 'dbus/bluetooth_media_client.h', + 'dbus/bluetooth_media_endpoint_service_provider.cc', + 'dbus/bluetooth_media_endpoint_service_provider.h', + 'dbus/bluetooth_media_transport_client.cc', + 'dbus/bluetooth_media_transport_client.h', + 'dbus/bluetooth_profile_manager_client.cc', + 'dbus/bluetooth_profile_manager_client.h', + 'dbus/bluetooth_profile_service_provider.cc', + 'dbus/bluetooth_profile_service_provider.h', 'dbus/cras_audio_client.cc', 'dbus/cras_audio_client.h', 'dbus/cros_disks_client.cc', @@ -82,6 +120,44 @@ 'dbus/fake_ap_manager_client.h', 'dbus/fake_audio_dsp_client.cc', 'dbus/fake_audio_dsp_client.h', + 'dbus/fake_bluetooth_adapter_client.cc', + 'dbus/fake_bluetooth_adapter_client.h', + 'dbus/fake_bluetooth_le_advertising_manager_client.cc', + 'dbus/fake_bluetooth_le_advertising_manager_client.h', + 'dbus/fake_bluetooth_le_advertisement_service_provider.cc', + 'dbus/fake_bluetooth_le_advertisement_service_provider.h', + 'dbus/fake_bluetooth_agent_manager_client.cc', + 'dbus/fake_bluetooth_agent_manager_client.h', + 'dbus/fake_bluetooth_agent_service_provider.cc', + 'dbus/fake_bluetooth_agent_service_provider.h', + 'dbus/fake_bluetooth_device_client.cc', + 'dbus/fake_bluetooth_device_client.h', + 'dbus/fake_bluetooth_gatt_characteristic_client.cc', + 'dbus/fake_bluetooth_gatt_characteristic_client.h', + 'dbus/fake_bluetooth_gatt_characteristic_service_provider.cc', + 'dbus/fake_bluetooth_gatt_characteristic_service_provider.h', + 'dbus/fake_bluetooth_gatt_descriptor_client.cc', + 'dbus/fake_bluetooth_gatt_descriptor_client.h', + 'dbus/fake_bluetooth_gatt_descriptor_service_provider.cc', + 'dbus/fake_bluetooth_gatt_descriptor_service_provider.h', + 'dbus/fake_bluetooth_gatt_manager_client.cc', + 'dbus/fake_bluetooth_gatt_manager_client.h', + 'dbus/fake_bluetooth_gatt_service_client.cc', + 'dbus/fake_bluetooth_gatt_service_client.h', + 'dbus/fake_bluetooth_gatt_service_service_provider.cc', + 'dbus/fake_bluetooth_gatt_service_service_provider.h', + 'dbus/fake_bluetooth_input_client.cc', + 'dbus/fake_bluetooth_input_client.h', + 'dbus/fake_bluetooth_media_client.cc', + 'dbus/fake_bluetooth_media_client.h', + 'dbus/fake_bluetooth_media_endpoint_service_provider.cc', + 'dbus/fake_bluetooth_media_endpoint_service_provider.h', + 'dbus/fake_bluetooth_media_transport_client.cc', + 'dbus/fake_bluetooth_media_transport_client.h', + 'dbus/fake_bluetooth_profile_manager_client.cc', + 'dbus/fake_bluetooth_profile_manager_client.h', + 'dbus/fake_bluetooth_profile_service_provider.cc', + 'dbus/fake_bluetooth_profile_service_provider.h', 'dbus/fake_cras_audio_client.cc', 'dbus/fake_cras_audio_client.h', 'dbus/fake_cros_disks_client.cc', diff --git a/chromeos/dbus/bluetooth_adapter_client.cc b/chromeos/dbus/bluetooth_adapter_client.cc new file mode 100644 index 0000000..064262e --- /dev/null +++ b/chromeos/dbus/bluetooth_adapter_client.cc @@ -0,0 +1,350 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_adapter_client.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_manager.h" +#include "dbus/object_proxy.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +BluetoothAdapterClient::DiscoveryFilter::DiscoveryFilter() {} + +BluetoothAdapterClient::DiscoveryFilter::~DiscoveryFilter() {} + +void BluetoothAdapterClient::DiscoveryFilter::CopyFrom( + const DiscoveryFilter& filter) { + if (filter.rssi.get()) + rssi.reset(new int16_t(*filter.rssi)); + else + rssi.reset(); + + if (filter.pathloss.get()) + pathloss.reset(new uint16_t(*filter.pathloss)); + else + pathloss.reset(); + + if (filter.transport.get()) + transport.reset(new std::string(*filter.transport)); + else + transport.reset(); + + if (filter.uuids.get()) + uuids.reset(new std::vector<std::string>(*filter.uuids)); + else + uuids.reset(); +} + +const char BluetoothAdapterClient::kNoResponseError[] = + "org.chromium.Error.NoResponse"; +const char BluetoothAdapterClient::kUnknownAdapterError[] = + "org.chromium.Error.UnknownAdapter"; + +BluetoothAdapterClient::Properties::Properties( + dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const PropertyChangedCallback& callback) + : dbus::PropertySet(object_proxy, interface_name, callback) { + RegisterProperty(bluetooth_adapter::kAddressProperty, &address); + RegisterProperty(bluetooth_adapter::kNameProperty, &name); + RegisterProperty(bluetooth_adapter::kAliasProperty, &alias); + RegisterProperty(bluetooth_adapter::kClassProperty, &bluetooth_class); + RegisterProperty(bluetooth_adapter::kPoweredProperty, &powered); + RegisterProperty(bluetooth_adapter::kDiscoverableProperty, &discoverable); + RegisterProperty(bluetooth_adapter::kPairableProperty, &pairable); + RegisterProperty(bluetooth_adapter::kPairableTimeoutProperty, + &pairable_timeout); + RegisterProperty(bluetooth_adapter::kDiscoverableTimeoutProperty, + &discoverable_timeout); + RegisterProperty(bluetooth_adapter::kDiscoveringProperty, &discovering); + RegisterProperty(bluetooth_adapter::kUUIDsProperty, &uuids); + RegisterProperty(bluetooth_adapter::kModaliasProperty, &modalias); +} + +BluetoothAdapterClient::Properties::~Properties() {} + +// The BluetoothAdapterClient implementation used in production. +class BluetoothAdapterClientImpl : public BluetoothAdapterClient, + public dbus::ObjectManager::Interface { + public: + BluetoothAdapterClientImpl() + : object_manager_(NULL), weak_ptr_factory_(this) {} + + ~BluetoothAdapterClientImpl() override { + object_manager_->UnregisterInterface( + bluetooth_adapter::kBluetoothAdapterInterface); + } + + // BluetoothAdapterClient override. + void AddObserver(BluetoothAdapterClient::Observer* observer) override { + DCHECK(observer); + observers_.AddObserver(observer); + } + + // BluetoothAdapterClient override. + void RemoveObserver(BluetoothAdapterClient::Observer* observer) override { + DCHECK(observer); + observers_.RemoveObserver(observer); + } + + // Returns the list of adapter object paths known to the system. + std::vector<dbus::ObjectPath> GetAdapters() override { + return object_manager_->GetObjectsWithInterface( + bluetooth_adapter::kBluetoothAdapterInterface); + } + + // dbus::ObjectManager::Interface override. + dbus::PropertySet* CreateProperties( + dbus::ObjectProxy* object_proxy, + const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + Properties* properties = new Properties( + object_proxy, interface_name, + base::Bind(&BluetoothAdapterClientImpl::OnPropertyChanged, + weak_ptr_factory_.GetWeakPtr(), object_path)); + return static_cast<dbus::PropertySet*>(properties); + } + + // BluetoothAdapterClient override. + Properties* GetProperties(const dbus::ObjectPath& object_path) override { + return static_cast<Properties*>(object_manager_->GetProperties( + object_path, bluetooth_adapter::kBluetoothAdapterInterface)); + } + + // BluetoothAdapterClient override. + void StartDiscovery(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call(bluetooth_adapter::kBluetoothAdapterInterface, + bluetooth_adapter::kStartDiscovery); + + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownAdapterError, ""); + return; + } + + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothAdapterClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothAdapterClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothAdapterClient override. + void StopDiscovery(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call(bluetooth_adapter::kBluetoothAdapterInterface, + bluetooth_adapter::kStopDiscovery); + + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownAdapterError, ""); + return; + } + + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothAdapterClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothAdapterClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothAdapterClient override. + void RemoveDevice(const dbus::ObjectPath& object_path, + const dbus::ObjectPath& device_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call(bluetooth_adapter::kBluetoothAdapterInterface, + bluetooth_adapter::kRemoveDevice); + + dbus::MessageWriter writer(&method_call); + writer.AppendObjectPath(device_path); + + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownAdapterError, ""); + return; + } + + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothAdapterClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothAdapterClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothAdapterClient override. + void SetDiscoveryFilter(const dbus::ObjectPath& object_path, + const DiscoveryFilter& discovery_filter, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call(bluetooth_adapter::kBluetoothAdapterInterface, + bluetooth_adapter::kSetDiscoveryFilter); + + dbus::MessageWriter writer(&method_call); + dbus::MessageWriter dict_writer(nullptr); + + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownAdapterError, ""); + return; + } + + writer.OpenArray("{sv}", &dict_writer); + + if (discovery_filter.uuids.get()) { + std::vector<std::string>* uuids = discovery_filter.uuids.get(); + dbus::MessageWriter uuids_entry_writer(nullptr); + dict_writer.OpenDictEntry(&uuids_entry_writer); + uuids_entry_writer.AppendString( + bluetooth_adapter::kDiscoveryFilterParameterUUIDs); + + dbus::MessageWriter uuids_array_variant(nullptr); + uuids_entry_writer.OpenVariant("as", &uuids_array_variant); + dbus::MessageWriter uuids_array(nullptr); + uuids_array_variant.OpenArray("s", &uuids_array); + + for (auto& it : *uuids) + uuids_array.AppendString(it); + + uuids_array_variant.CloseContainer(&uuids_array); + uuids_entry_writer.CloseContainer(&uuids_array_variant); + dict_writer.CloseContainer(&uuids_entry_writer); + } + + if (discovery_filter.rssi.get()) { + dbus::MessageWriter rssi_entry_writer(nullptr); + dict_writer.OpenDictEntry(&rssi_entry_writer); + rssi_entry_writer.AppendString( + bluetooth_adapter::kDiscoveryFilterParameterRSSI); + rssi_entry_writer.AppendVariantOfInt16(*discovery_filter.rssi.get()); + dict_writer.CloseContainer(&rssi_entry_writer); + } + + if (discovery_filter.pathloss.get()) { + dbus::MessageWriter pathloss_entry_writer(nullptr); + dict_writer.OpenDictEntry(&pathloss_entry_writer); + pathloss_entry_writer.AppendString( + bluetooth_adapter::kDiscoveryFilterParameterPathloss); + pathloss_entry_writer.AppendVariantOfUint16( + *discovery_filter.pathloss.get()); + dict_writer.CloseContainer(&pathloss_entry_writer); + } + + if (discovery_filter.transport.get()) { + dbus::MessageWriter transport_entry_writer(nullptr); + dict_writer.OpenDictEntry(&transport_entry_writer); + transport_entry_writer.AppendString( + bluetooth_adapter::kDiscoveryFilterParameterTransport); + transport_entry_writer.AppendVariantOfString( + *discovery_filter.transport.get()); + dict_writer.CloseContainer(&transport_entry_writer); + } + + writer.CloseContainer(&dict_writer); + + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothAdapterClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothAdapterClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + protected: + void Init(dbus::Bus* bus) override { + object_manager_ = bus->GetObjectManager( + bluetooth_object_manager::kBluetoothObjectManagerServiceName, + dbus::ObjectPath( + bluetooth_object_manager::kBluetoothObjectManagerServicePath)); + object_manager_->RegisterInterface( + bluetooth_adapter::kBluetoothAdapterInterface, this); + } + + private: + // Called by dbus::ObjectManager when an object with the adapter interface + // is created. Informs observers. + void ObjectAdded(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + FOR_EACH_OBSERVER(BluetoothAdapterClient::Observer, observers_, + AdapterAdded(object_path)); + } + + // Called by dbus::ObjectManager when an object with the adapter interface + // is removed. Informs observers. + void ObjectRemoved(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + FOR_EACH_OBSERVER(BluetoothAdapterClient::Observer, observers_, + AdapterRemoved(object_path)); + } + + // Called by dbus::PropertySet when a property value is changed, + // either by result of a signal or response to a GetAll() or Get() + // call. Informs observers. + void OnPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) { + FOR_EACH_OBSERVER(BluetoothAdapterClient::Observer, observers_, + AdapterPropertyChanged(object_path, property_name)); + } + + // Called when a response for successful method call is received. + void OnSuccess(const base::Closure& callback, dbus::Response* response) { + DCHECK(response); + callback.Run(); + } + + // Called when a response for a failed method call is received. + void OnError(const ErrorCallback& error_callback, + dbus::ErrorResponse* response) { + // Error response has optional error message argument. + std::string error_name; + std::string error_message; + if (response) { + dbus::MessageReader reader(response); + error_name = response->GetErrorName(); + reader.PopString(&error_message); + } else { + error_name = kNoResponseError; + error_message = ""; + } + error_callback.Run(error_name, error_message); + } + + dbus::ObjectManager* object_manager_; + + // List of observers interested in event notifications from us. + base::ObserverList<BluetoothAdapterClient::Observer> observers_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothAdapterClientImpl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothAdapterClientImpl); +}; + +BluetoothAdapterClient::BluetoothAdapterClient() {} + +BluetoothAdapterClient::~BluetoothAdapterClient() {} + +BluetoothAdapterClient* BluetoothAdapterClient::Create() { + return new BluetoothAdapterClientImpl; +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_adapter_client.h b/chromeos/dbus/bluetooth_adapter_client.h new file mode 100644 index 0000000..c7237d7 --- /dev/null +++ b/chromeos/dbus/bluetooth_adapter_client.h @@ -0,0 +1,184 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_ADAPTER_CLIENT_H_ +#define CHROMEOS_DBUS_BLUETOOTH_ADAPTER_CLIENT_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/observer_list.h" +#include "base/values.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_client.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +// BluetoothAdapterClient is used to communicate with objects representing +// local Bluetooth Adapters. +class CHROMEOS_EXPORT BluetoothAdapterClient : public DBusClient { + public: + // A DiscoveryFilter represents a filter passed to the SetDiscoveryFilter + // method. + struct DiscoveryFilter { + DiscoveryFilter(); + ~DiscoveryFilter(); + + // Copy content of |filter| into this filter + void CopyFrom(const DiscoveryFilter& filter); + + scoped_ptr<std::vector<std::string>> uuids; + scoped_ptr<int16_t> rssi; + scoped_ptr<uint16_t> pathloss; + scoped_ptr<std::string> transport; + + DISALLOW_COPY_AND_ASSIGN(DiscoveryFilter); + }; + + // Structure of properties associated with bluetooth adapters. + struct Properties : public dbus::PropertySet { + // The Bluetooth device address of the adapter. Read-only. + dbus::Property<std::string> address; + + // The Bluetooth system name, generally derived from the hostname. + dbus::Property<std::string> name; + + // The Bluetooth friendly name of the adapter, unlike remote devices, + // this property can be changed to change the presentation for when + // the adapter is discoverable. + dbus::Property<std::string> alias; + + // The Bluetooth class of the adapter device. Read-only. + dbus::Property<uint32> bluetooth_class; + + // Whether the adapter radio is powered. + dbus::Property<bool> powered; + + // Whether the adapter is discoverable by other Bluetooth devices. + // |discovering_timeout| is used to automatically disable after a time + // period. + dbus::Property<bool> discoverable; + + // Whether the adapter accepts incoming pairing requests from other + // Bluetooth devices. |pairable_timeout| is used to automatically disable + // after a time period. + dbus::Property<bool> pairable; + + // The timeout in seconds to cease accepting incoming pairing requests + // after |pairable| is set to true. Zero means adapter remains pairable + // forever. + dbus::Property<uint32> pairable_timeout; + + // The timeout in seconds to cease the adapter being discoverable by + // other Bluetooth devices after |discoverable| is set to true. Zero + // means adapter remains discoverable forever. + dbus::Property<uint32> discoverable_timeout; + + // Indicates that the adapter is discovering other Bluetooth Devices. + // Read-only. Use StartDiscovery() to begin discovery. + dbus::Property<bool> discovering; + + // List of 128-bit UUIDs that represent the available local services. + // Read-only. + dbus::Property<std::vector<std::string>> uuids; + + // Local Device ID information in Linux kernel modalias format. Read-only. + dbus::Property<std::string> modalias; + + Properties(dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const PropertyChangedCallback& callback); + ~Properties() override; + }; + + // Interface for observing changes from a local bluetooth adapter. + class Observer { + public: + virtual ~Observer() {} + + // Called when the adapter with object path |object_path| is added to the + // system. + virtual void AdapterAdded(const dbus::ObjectPath& object_path) {} + + // Called when the adapter with object path |object_path| is removed from + // the system. + virtual void AdapterRemoved(const dbus::ObjectPath& object_path) {} + + // Called when the adapter with object path |object_path| has a + // change in value of the property named |property_name|. + virtual void AdapterPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) {} + }; + + ~BluetoothAdapterClient() override; + + // Adds and removes observers for events on all local bluetooth + // adapters. Check the |object_path| parameter of observer methods to + // determine which adapter is issuing the event. + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + + // Returns the list of adapter object paths known to the system. + virtual std::vector<dbus::ObjectPath> GetAdapters() = 0; + + // Obtain the properties for the adapter with object path |object_path|, + // any values should be copied if needed. + virtual Properties* GetProperties(const dbus::ObjectPath& object_path) = 0; + + // The ErrorCallback is used by adapter methods to indicate failure. + // It receives two arguments: the name of the error in |error_name| and + // an optional message in |error_message|. + typedef base::Callback<void(const std::string& error_name, + const std::string& error_message)> ErrorCallback; + + // Starts a device discovery on the adapter with object path |object_path|. + virtual void StartDiscovery(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Cancels any previous device discovery on the adapter with object path + // |object_path|. + virtual void StopDiscovery(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Removes from the adapter with object path |object_path| the remote + // device with object path |object_path| from the list of known devices + // and discards any pairing information. + virtual void RemoveDevice(const dbus::ObjectPath& object_path, + const dbus::ObjectPath& device_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Sets the device discovery filter on the adapter with object path + // |object_path|. When this method is called with no filter parameter, filter + // is removed. + // SetDiscoveryFilter can be called before StartDiscovery. It is useful when + // client will create first discovery session, to ensure that proper scan + // will be started right after call to StartDiscovery. + virtual void SetDiscoveryFilter(const dbus::ObjectPath& object_path, + const DiscoveryFilter& discovery_filter, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Creates the instance. + static BluetoothAdapterClient* Create(); + + // Constants used to indicate exceptional error conditions. + static const char kNoResponseError[]; + static const char kUnknownAdapterError[]; + + protected: + BluetoothAdapterClient(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothAdapterClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_ADAPTER_CLIENT_H_ diff --git a/chromeos/dbus/bluetooth_agent_manager_client.cc b/chromeos/dbus/bluetooth_agent_manager_client.cc new file mode 100644 index 0000000..a5606a9 --- /dev/null +++ b/chromeos/dbus/bluetooth_agent_manager_client.cc @@ -0,0 +1,137 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_agent_manager_client.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_proxy.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +const char BluetoothAgentManagerClient::kNoResponseError[] = + "org.chromium.Error.NoResponse"; + +// The BluetoothAgentManagerClient implementation used in production. +class BluetoothAgentManagerClientImpl : public BluetoothAgentManagerClient { + public: + BluetoothAgentManagerClientImpl() : weak_ptr_factory_(this) {} + + ~BluetoothAgentManagerClientImpl() override {} + + // BluetoothAgentManagerClient override. + void RegisterAgent(const dbus::ObjectPath& agent_path, + const std::string& capability, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call( + bluetooth_agent_manager::kBluetoothAgentManagerInterface, + bluetooth_agent_manager::kRegisterAgent); + + dbus::MessageWriter writer(&method_call); + writer.AppendObjectPath(agent_path); + writer.AppendString(capability); + + object_proxy_->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothAgentManagerClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothAgentManagerClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothAgentManagerClient override. + void UnregisterAgent(const dbus::ObjectPath& agent_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call( + bluetooth_agent_manager::kBluetoothAgentManagerInterface, + bluetooth_agent_manager::kUnregisterAgent); + + dbus::MessageWriter writer(&method_call); + writer.AppendObjectPath(agent_path); + + object_proxy_->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothAgentManagerClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothAgentManagerClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothAgentManagerClient override. + void RequestDefaultAgent(const dbus::ObjectPath& agent_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call( + bluetooth_agent_manager::kBluetoothAgentManagerInterface, + bluetooth_agent_manager::kRequestDefaultAgent); + + dbus::MessageWriter writer(&method_call); + writer.AppendObjectPath(agent_path); + + object_proxy_->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothAgentManagerClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothAgentManagerClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + protected: + void Init(dbus::Bus* bus) override { + DCHECK(bus); + object_proxy_ = bus->GetObjectProxy( + bluetooth_agent_manager::kBluetoothAgentManagerServiceName, + dbus::ObjectPath( + bluetooth_agent_manager::kBluetoothAgentManagerServicePath)); + } + + private: + // Called when a response for successful method call is received. + void OnSuccess(const base::Closure& callback, dbus::Response* response) { + DCHECK(response); + callback.Run(); + } + + // Called when a response for a failed method call is received. + void OnError(const ErrorCallback& error_callback, + dbus::ErrorResponse* response) { + // Error response has optional error message argument. + std::string error_name; + std::string error_message; + if (response) { + dbus::MessageReader reader(response); + error_name = response->GetErrorName(); + reader.PopString(&error_message); + } else { + error_name = kNoResponseError; + error_message = ""; + } + error_callback.Run(error_name, error_message); + } + + dbus::ObjectProxy* object_proxy_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothAgentManagerClientImpl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothAgentManagerClientImpl); +}; + +BluetoothAgentManagerClient::BluetoothAgentManagerClient() {} + +BluetoothAgentManagerClient::~BluetoothAgentManagerClient() {} + +BluetoothAgentManagerClient* BluetoothAgentManagerClient::Create() { + return new BluetoothAgentManagerClientImpl(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_agent_manager_client.h b/chromeos/dbus/bluetooth_agent_manager_client.h new file mode 100644 index 0000000..f43f4f8 --- /dev/null +++ b/chromeos/dbus/bluetooth_agent_manager_client.h @@ -0,0 +1,68 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_AGENT_MANAGER_CLIENT_H_ +#define CHROMEOS_DBUS_BLUETOOTH_AGENT_MANAGER_CLIENT_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/values.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_client.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// BluetoothAgentManagerClient is used to communicate with the agent manager +// object of the Bluetooth daemon. +class CHROMEOS_EXPORT BluetoothAgentManagerClient : public DBusClient { + public: + ~BluetoothAgentManagerClient() override; + + // The ErrorCallback is used by agent manager methods to indicate failure. + // It receives two arguments: the name of the error in |error_name| and + // an optional message in |error_message|. + typedef base::Callback<void(const std::string& error_name, + const std::string& error_message)> ErrorCallback; + + // Registers an agent within the local process at the D-bus object path + // |agent_path| with the remote agent manager. The agent is used for pairing + // and for authorization of incoming connection requests. |capability| + // specifies the input and display capabilities of the agent and should be + // one of the constants declared in the bluetooth_agent_manager:: namespace. + virtual void RegisterAgent(const dbus::ObjectPath& agent_path, + const std::string& capability, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Unregisters the agent with the D-Bus object path |agent_path| from the + // remote agent manager. + virtual void UnregisterAgent(const dbus::ObjectPath& agent_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Requests that the agent with the D-Bus object path |agent_path| be made + // the default. + virtual void RequestDefaultAgent(const dbus::ObjectPath& agent_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Creates the instance. + static BluetoothAgentManagerClient* Create(); + + // Constants used to indicate exceptional error conditions. + static const char kNoResponseError[]; + + protected: + BluetoothAgentManagerClient(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothAgentManagerClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_AGENT_MANAGER_CLIENT_H_ diff --git a/chromeos/dbus/bluetooth_agent_service_provider.cc b/chromeos/dbus/bluetooth_agent_service_provider.cc new file mode 100644 index 0000000..54ebeb3 --- /dev/null +++ b/chromeos/dbus/bluetooth_agent_service_provider.cc @@ -0,0 +1,444 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_agent_service_provider.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/threading/platform_thread.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_agent_service_provider.h" +#include "dbus/exported_object.h" +#include "dbus/message.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +// The BluetoothAgentServiceProvider implementation used in production. +class BluetoothAgentServiceProviderImpl : public BluetoothAgentServiceProvider { + public: + BluetoothAgentServiceProviderImpl(dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate) + : origin_thread_id_(base::PlatformThread::CurrentId()), + bus_(bus), + delegate_(delegate), + object_path_(object_path), + weak_ptr_factory_(this) { + VLOG(1) << "Creating Bluetooth Agent: " << object_path_.value(); + + exported_object_ = bus_->GetExportedObject(object_path_); + + exported_object_->ExportMethod( + bluetooth_agent::kBluetoothAgentInterface, bluetooth_agent::kRelease, + base::Bind(&BluetoothAgentServiceProviderImpl::Release, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothAgentServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + bluetooth_agent::kBluetoothAgentInterface, + bluetooth_agent::kRequestPinCode, + base::Bind(&BluetoothAgentServiceProviderImpl::RequestPinCode, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothAgentServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + bluetooth_agent::kBluetoothAgentInterface, + bluetooth_agent::kDisplayPinCode, + base::Bind(&BluetoothAgentServiceProviderImpl::DisplayPinCode, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothAgentServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + bluetooth_agent::kBluetoothAgentInterface, + bluetooth_agent::kRequestPasskey, + base::Bind(&BluetoothAgentServiceProviderImpl::RequestPasskey, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothAgentServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + bluetooth_agent::kBluetoothAgentInterface, + bluetooth_agent::kDisplayPasskey, + base::Bind(&BluetoothAgentServiceProviderImpl::DisplayPasskey, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothAgentServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + bluetooth_agent::kBluetoothAgentInterface, + bluetooth_agent::kRequestConfirmation, + base::Bind(&BluetoothAgentServiceProviderImpl::RequestConfirmation, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothAgentServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + bluetooth_agent::kBluetoothAgentInterface, + bluetooth_agent::kRequestAuthorization, + base::Bind(&BluetoothAgentServiceProviderImpl::RequestAuthorization, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothAgentServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + bluetooth_agent::kBluetoothAgentInterface, + bluetooth_agent::kAuthorizeService, + base::Bind(&BluetoothAgentServiceProviderImpl::AuthorizeService, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothAgentServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + bluetooth_agent::kBluetoothAgentInterface, bluetooth_agent::kCancel, + base::Bind(&BluetoothAgentServiceProviderImpl::Cancel, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothAgentServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + } + + ~BluetoothAgentServiceProviderImpl() override { + VLOG(1) << "Cleaning up Bluetooth Agent: " << object_path_.value(); + + // Unregister the object path so we can reuse with a new agent. + bus_->UnregisterExportedObject(object_path_); + } + + private: + // Returns true if the current thread is on the origin thread. + bool OnOriginThread() { + return base::PlatformThread::CurrentId() == origin_thread_id_; + } + + // Called by dbus:: when the agent is unregistered from the Bluetooth + // daemon, generally at the end of a pairing request. + void Release(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + delegate_->Released(); + + response_sender.Run(dbus::Response::FromMethodCall(method_call)); + } + + // Called by dbus:: when the Bluetooth daemon requires a PIN Code for + // device authentication. + void RequestPinCode(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + dbus::MessageReader reader(method_call); + dbus::ObjectPath device_path; + if (!reader.PopObjectPath(&device_path)) { + LOG(WARNING) << "RequestPinCode called with incorrect paramters: " + << method_call->ToString(); + return; + } + + Delegate::PinCodeCallback callback = base::Bind( + &BluetoothAgentServiceProviderImpl::OnPinCode, + weak_ptr_factory_.GetWeakPtr(), method_call, response_sender); + + delegate_->RequestPinCode(device_path, callback); + } + + // Called by dbus:: when the Bluetooth daemon requires that the user + // enter a PIN Code into the remote device so that it may be + // authenticated. + void DisplayPinCode(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + dbus::MessageReader reader(method_call); + dbus::ObjectPath device_path; + std::string pincode; + if (!reader.PopObjectPath(&device_path) || !reader.PopString(&pincode)) { + LOG(WARNING) << "DisplayPinCode called with incorrect paramters: " + << method_call->ToString(); + return; + } + + delegate_->DisplayPinCode(device_path, pincode); + + response_sender.Run(dbus::Response::FromMethodCall(method_call)); + } + + // Called by dbus:: when the Bluetooth daemon requires a Passkey for + // device authentication. + void RequestPasskey(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + dbus::MessageReader reader(method_call); + dbus::ObjectPath device_path; + if (!reader.PopObjectPath(&device_path)) { + LOG(WARNING) << "RequestPasskey called with incorrect paramters: " + << method_call->ToString(); + return; + } + + Delegate::PasskeyCallback callback = base::Bind( + &BluetoothAgentServiceProviderImpl::OnPasskey, + weak_ptr_factory_.GetWeakPtr(), method_call, response_sender); + + delegate_->RequestPasskey(device_path, callback); + } + + // Called by dbus:: when the Bluetooth daemon requires that the user + // enter a Passkey into the remote device so that it may be + // authenticated. + void DisplayPasskey(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + dbus::MessageReader reader(method_call); + dbus::ObjectPath device_path; + uint32 passkey; + uint16 entered; + if (!reader.PopObjectPath(&device_path) || !reader.PopUint32(&passkey) || + !reader.PopUint16(&entered)) { + LOG(WARNING) << "DisplayPasskey called with incorrect paramters: " + << method_call->ToString(); + return; + } + + delegate_->DisplayPasskey(device_path, passkey, entered); + + response_sender.Run(dbus::Response::FromMethodCall(method_call)); + } + + // Called by dbus:: when the Bluetooth daemon requires that the user + // confirm that a Passkey is displayed on the screen of the remote + // device so that it may be authenticated. + void RequestConfirmation( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + dbus::MessageReader reader(method_call); + dbus::ObjectPath device_path; + uint32 passkey; + if (!reader.PopObjectPath(&device_path) || !reader.PopUint32(&passkey)) { + LOG(WARNING) << "RequestConfirmation called with incorrect paramters: " + << method_call->ToString(); + return; + } + + Delegate::ConfirmationCallback callback = base::Bind( + &BluetoothAgentServiceProviderImpl::OnConfirmation, + weak_ptr_factory_.GetWeakPtr(), method_call, response_sender); + + delegate_->RequestConfirmation(device_path, passkey, callback); + } + + // Called by dbus:: when the Bluetooth daemon requires that the user + // confirm an incoming just-works pairing. + void RequestAuthorization( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + dbus::MessageReader reader(method_call); + dbus::ObjectPath device_path; + if (!reader.PopObjectPath(&device_path)) { + LOG(WARNING) << "RequestAuthorization called with incorrect paramters: " + << method_call->ToString(); + return; + } + + Delegate::ConfirmationCallback callback = base::Bind( + &BluetoothAgentServiceProviderImpl::OnConfirmation, + weak_ptr_factory_.GetWeakPtr(), method_call, response_sender); + + delegate_->RequestAuthorization(device_path, callback); + } + + // Called by dbus:: when the Bluetooth daemon requires that the user + // confirm that that a remote device is authorized to connect to a service + // UUID. + void AuthorizeService(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + dbus::MessageReader reader(method_call); + dbus::ObjectPath device_path; + std::string uuid; + if (!reader.PopObjectPath(&device_path) || !reader.PopString(&uuid)) { + LOG(WARNING) << "AuthorizeService called with incorrect paramters: " + << method_call->ToString(); + return; + } + + Delegate::ConfirmationCallback callback = base::Bind( + &BluetoothAgentServiceProviderImpl::OnConfirmation, + weak_ptr_factory_.GetWeakPtr(), method_call, response_sender); + + delegate_->AuthorizeService(device_path, uuid, callback); + } + + // Called by dbus:: when the request failed before a reply was returned + // from the device. + void Cancel(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + delegate_->Cancel(); + + response_sender.Run(dbus::Response::FromMethodCall(method_call)); + } + + // Called by dbus:: when a method is exported. + void OnExported(const std::string& interface_name, + const std::string& method_name, + bool success) { + LOG_IF(WARNING, !success) << "Failed to export " << interface_name << "." + << method_name; + } + + // Called by the Delegate to response to a method requesting a PIN code. + void OnPinCode(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender, + Delegate::Status status, + const std::string& pincode) { + DCHECK(OnOriginThread()); + + switch (status) { + case Delegate::SUCCESS: { + scoped_ptr<dbus::Response> response( + dbus::Response::FromMethodCall(method_call)); + dbus::MessageWriter writer(response.get()); + writer.AppendString(pincode); + response_sender.Run(response.Pass()); + break; + } + case Delegate::REJECTED: { + response_sender.Run(dbus::ErrorResponse::FromMethodCall( + method_call, bluetooth_agent::kErrorRejected, "rejected")); + break; + } + case Delegate::CANCELLED: { + response_sender.Run(dbus::ErrorResponse::FromMethodCall( + method_call, bluetooth_agent::kErrorCanceled, "canceled")); + break; + } + default: + NOTREACHED() << "Unexpected status code from delegate: " << status; + } + } + + // Called by the Delegate to response to a method requesting a Passkey. + void OnPasskey(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender, + Delegate::Status status, + uint32 passkey) { + DCHECK(OnOriginThread()); + + switch (status) { + case Delegate::SUCCESS: { + scoped_ptr<dbus::Response> response( + dbus::Response::FromMethodCall(method_call)); + dbus::MessageWriter writer(response.get()); + writer.AppendUint32(passkey); + response_sender.Run(response.Pass()); + break; + } + case Delegate::REJECTED: { + response_sender.Run(dbus::ErrorResponse::FromMethodCall( + method_call, bluetooth_agent::kErrorRejected, "rejected")); + break; + } + case Delegate::CANCELLED: { + response_sender.Run(dbus::ErrorResponse::FromMethodCall( + method_call, bluetooth_agent::kErrorCanceled, "canceled")); + break; + } + default: + NOTREACHED() << "Unexpected status code from delegate: " << status; + } + } + + // Called by the Delegate in response to a method requiring confirmation. + void OnConfirmation(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender, + Delegate::Status status) { + DCHECK(OnOriginThread()); + + switch (status) { + case Delegate::SUCCESS: { + response_sender.Run(dbus::Response::FromMethodCall(method_call)); + break; + } + case Delegate::REJECTED: { + response_sender.Run(dbus::ErrorResponse::FromMethodCall( + method_call, bluetooth_agent::kErrorRejected, "rejected")); + break; + } + case Delegate::CANCELLED: { + response_sender.Run(dbus::ErrorResponse::FromMethodCall( + method_call, bluetooth_agent::kErrorCanceled, "canceled")); + break; + } + default: + NOTREACHED() << "Unexpected status code from delegate: " << status; + } + } + + // Origin thread (i.e. the UI thread in production). + base::PlatformThreadId origin_thread_id_; + + // D-Bus bus object is exported on, not owned by this object and must + // outlive it. + dbus::Bus* bus_; + + // All incoming method calls are passed on to the Delegate and a callback + // passed to generate the reply. |delegate_| is generally the object that + // owns this one, and must outlive it. + Delegate* delegate_; + + // D-Bus object path of object we are exporting, kept so we can unregister + // again in our destructor. + dbus::ObjectPath object_path_; + + // D-Bus object we are exporting, owned by this object. + scoped_refptr<dbus::ExportedObject> exported_object_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothAgentServiceProviderImpl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothAgentServiceProviderImpl); +}; + +BluetoothAgentServiceProvider::BluetoothAgentServiceProvider() {} + +BluetoothAgentServiceProvider::~BluetoothAgentServiceProvider() {} + +// static +BluetoothAgentServiceProvider* BluetoothAgentServiceProvider::Create( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate) { + if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH)) { + return new BluetoothAgentServiceProviderImpl(bus, object_path, delegate); + } else { + return new FakeBluetoothAgentServiceProvider(object_path, delegate); + } +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_agent_service_provider.h b/chromeos/dbus/bluetooth_agent_service_provider.h new file mode 100644 index 0000000..8e84fa5 --- /dev/null +++ b/chromeos/dbus/bluetooth_agent_service_provider.h @@ -0,0 +1,178 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_AGENT_SERVICE_PROVIDER_H_ +#define CHROMEOS_DBUS_BLUETOOTH_AGENT_SERVICE_PROVIDER_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "chromeos/chromeos_export.h" +#include "dbus/bus.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// BluetoothAgentServiceProvider is used to provide a D-Bus object that +// the bluetooth daemon can communicate with during a remote device pairing +// request. +// +// Instantiate with a chosen D-Bus object path and delegate object, and pass +// the D-Bus object path as the |agent_path| argument to the +// chromeos::BluetoothAgentManagerClient::RegisterAgent() method. +// +// After initiating the pairing process with a device, using the +// chromeos::BluetoothDeviceClient::Pair() method, the Bluetooth daemon will +// make calls to this agent object and they will be passed on to your Delegate +// object for handling. Responses should be returned using the callbacks +// supplied to those methods. +class CHROMEOS_EXPORT BluetoothAgentServiceProvider { + public: + // Interface for reacting to agent requests. + class Delegate { + public: + virtual ~Delegate() {} + + // Possible status values that may be returned to callbacks. Success + // indicates that a pincode or passkey has been obtained, or permission + // granted; rejected indicates the user rejected the request or denied + // permission; cancelled indicates the user cancelled the request + // without confirming either way. + enum Status { SUCCESS, REJECTED, CANCELLED }; + + // The PinCodeCallback is used for the RequestPinCode() method, it should + // be called with two arguments, the |status| of the request (success, + // rejected or cancelled) and the |pincode| requested. + typedef base::Callback<void(Status, const std::string&)> PinCodeCallback; + + // The PasskeyCallback is used for the RequestPasskey() method, it should + // be called with two arguments, the |status| of the request (success, + // rejected or cancelled) and the |passkey| requested, a numeric in the + // range 0-999999, + typedef base::Callback<void(Status, uint32)> PasskeyCallback; + + // The ConfirmationCallback is used for methods which request confirmation + // or authorization, it should be called with one argument, the |status| + // of the request (success, rejected or cancelled). + typedef base::Callback<void(Status)> ConfirmationCallback; + + // 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. This corresponds to the + // org.bluez.Agent1.Release method and is renamed to avoid a conflict + // with base::Refcounted<T>. + virtual void Released() = 0; + + // 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) = 0; + + // 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) = 0; + + // 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) = 0; + + // 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. + // + // As the user enters the passkey onto the device, |entered| will be + // updated to reflect the number of digits entered so far. + virtual void DisplayPasskey(const dbus::ObjectPath& device_path, + uint32 passkey, + uint16 entered) = 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 with object path |object_path| so that it may be + // authenticated. 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) = 0; + + // This method will be called when the Bluetooth daemon requires + // authorization of an incoming pairing attempt from the device with object + // path |device_path| that would have otherwised triggered the just-works + // pairing model. + // + // The agent should confirm the incoming pairing with the user and call + // |callback| to provide their response (success, rejected or cancelled). + virtual void RequestAuthorization(const dbus::ObjectPath& device_path, + const ConfirmationCallback& callback) = 0; + + // 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 AuthorizeService(const dbus::ObjectPath& device_path, + const std::string& uuid, + const ConfirmationCallback& callback) = 0; + + // 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() = 0; + }; + + virtual ~BluetoothAgentServiceProvider(); + + // Creates the instance where |bus| is the D-Bus bus connection to export + // the object onto, |object_path| is the object path that it should have + // and |delegate| is the object to which all method calls will be passed + // and responses generated from. + static BluetoothAgentServiceProvider* Create( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate); + + protected: + BluetoothAgentServiceProvider(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothAgentServiceProvider); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_AGENT_SERVICE_PROVIDER_H_ diff --git a/chromeos/dbus/bluetooth_device_client.cc b/chromeos/dbus/bluetooth_device_client.cc new file mode 100644 index 0000000..7b1d590 --- /dev/null +++ b/chromeos/dbus/bluetooth_device_client.cc @@ -0,0 +1,378 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_device_client.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/stl_util.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_manager.h" +#include "dbus/object_proxy.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +namespace { + +// Value returned for the the RSSI or TX power if it cannot be read. +const int kUnknownPower = 127; + +} // namespace + +const char BluetoothDeviceClient::kNoResponseError[] = + "org.chromium.Error.NoResponse"; +const char BluetoothDeviceClient::kUnknownDeviceError[] = + "org.chromium.Error.UnknownDevice"; + +BluetoothDeviceClient::Properties::Properties( + dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const PropertyChangedCallback& callback) + : dbus::PropertySet(object_proxy, interface_name, callback) { + RegisterProperty(bluetooth_device::kAddressProperty, &address); + RegisterProperty(bluetooth_device::kNameProperty, &name); + RegisterProperty(bluetooth_device::kIconProperty, &icon); + RegisterProperty(bluetooth_device::kClassProperty, &bluetooth_class); + RegisterProperty(bluetooth_device::kAppearanceProperty, &appearance); + RegisterProperty(bluetooth_device::kUUIDsProperty, &uuids); + RegisterProperty(bluetooth_device::kPairedProperty, &paired); + RegisterProperty(bluetooth_device::kConnectedProperty, &connected); + RegisterProperty(bluetooth_device::kTrustedProperty, &trusted); + RegisterProperty(bluetooth_device::kBlockedProperty, &blocked); + RegisterProperty(bluetooth_device::kAliasProperty, &alias); + RegisterProperty(bluetooth_device::kAdapterProperty, &adapter); + RegisterProperty(bluetooth_device::kLegacyPairingProperty, &legacy_pairing); + RegisterProperty(bluetooth_device::kModaliasProperty, &modalias); + RegisterProperty(bluetooth_device::kRSSIProperty, &rssi); + RegisterProperty(bluetooth_device::kTxPowerProperty, &tx_power); +} + +BluetoothDeviceClient::Properties::~Properties() {} + +// The BluetoothDeviceClient implementation used in production. +class BluetoothDeviceClientImpl : public BluetoothDeviceClient, + public dbus::ObjectManager::Interface { + public: + BluetoothDeviceClientImpl() + : object_manager_(NULL), weak_ptr_factory_(this) {} + + ~BluetoothDeviceClientImpl() override { + object_manager_->UnregisterInterface( + bluetooth_device::kBluetoothDeviceInterface); + } + + // BluetoothDeviceClient override. + void AddObserver(BluetoothDeviceClient::Observer* observer) override { + DCHECK(observer); + observers_.AddObserver(observer); + } + + // BluetoothDeviceClient override. + void RemoveObserver(BluetoothDeviceClient::Observer* observer) override { + DCHECK(observer); + observers_.RemoveObserver(observer); + } + + // dbus::ObjectManager::Interface override. + dbus::PropertySet* CreateProperties( + dbus::ObjectProxy* object_proxy, + const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + Properties* properties = + new Properties(object_proxy, interface_name, + base::Bind(&BluetoothDeviceClientImpl::OnPropertyChanged, + weak_ptr_factory_.GetWeakPtr(), object_path)); + return static_cast<dbus::PropertySet*>(properties); + } + + // BluetoothDeviceClient override. + std::vector<dbus::ObjectPath> GetDevicesForAdapter( + const dbus::ObjectPath& adapter_path) override { + std::vector<dbus::ObjectPath> object_paths, device_paths; + device_paths = object_manager_->GetObjectsWithInterface( + bluetooth_device::kBluetoothDeviceInterface); + for (std::vector<dbus::ObjectPath>::iterator iter = device_paths.begin(); + iter != device_paths.end(); ++iter) { + Properties* properties = GetProperties(*iter); + if (properties->adapter.value() == adapter_path) + object_paths.push_back(*iter); + } + return object_paths; + } + + // BluetoothDeviceClient override. + Properties* GetProperties(const dbus::ObjectPath& object_path) override { + return static_cast<Properties*>(object_manager_->GetProperties( + object_path, bluetooth_device::kBluetoothDeviceInterface)); + } + + // BluetoothDeviceClient override. + void Connect(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call(bluetooth_device::kBluetoothDeviceInterface, + bluetooth_device::kConnect); + + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownDeviceError, ""); + return; + } + + // Connect may take an arbitrary length of time, so use no timeout. + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_INFINITE, + base::Bind(&BluetoothDeviceClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothDeviceClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothDeviceClient override. + void Disconnect(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call(bluetooth_device::kBluetoothDeviceInterface, + bluetooth_device::kDisconnect); + + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownDeviceError, ""); + return; + } + + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothDeviceClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothDeviceClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothDeviceClient override. + void ConnectProfile(const dbus::ObjectPath& object_path, + const std::string& uuid, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call(bluetooth_device::kBluetoothDeviceInterface, + bluetooth_device::kConnectProfile); + + dbus::MessageWriter writer(&method_call); + writer.AppendString(uuid); + + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownDeviceError, ""); + return; + } + + // Connect may take an arbitrary length of time, so use no timeout. + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_INFINITE, + base::Bind(&BluetoothDeviceClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothDeviceClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothDeviceClient override. + void DisconnectProfile(const dbus::ObjectPath& object_path, + const std::string& uuid, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call(bluetooth_device::kBluetoothDeviceInterface, + bluetooth_device::kDisconnectProfile); + + dbus::MessageWriter writer(&method_call); + writer.AppendString(uuid); + + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownDeviceError, ""); + return; + } + + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothDeviceClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothDeviceClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothDeviceClient override. + void Pair(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call(bluetooth_device::kBluetoothDeviceInterface, + bluetooth_device::kPair); + + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownDeviceError, ""); + return; + } + + // Pairing may take an arbitrary length of time, so use no timeout. + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_INFINITE, + base::Bind(&BluetoothDeviceClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothDeviceClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothDeviceClient override. + void CancelPairing(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call(bluetooth_device::kBluetoothDeviceInterface, + bluetooth_device::kCancelPairing); + + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownDeviceError, ""); + return; + } + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothDeviceClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothDeviceClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothDeviceClient override. + void GetConnInfo(const dbus::ObjectPath& object_path, + const ConnInfoCallback& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call( + bluetooth_plugin_device::kBluetoothPluginInterface, + bluetooth_plugin_device::kGetConnInfo); + + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownDeviceError, ""); + return; + } + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothDeviceClientImpl::OnGetConnInfoSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothDeviceClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + protected: + void Init(dbus::Bus* bus) override { + object_manager_ = bus->GetObjectManager( + bluetooth_object_manager::kBluetoothObjectManagerServiceName, + dbus::ObjectPath( + bluetooth_object_manager::kBluetoothObjectManagerServicePath)); + object_manager_->RegisterInterface( + bluetooth_device::kBluetoothDeviceInterface, this); + } + + private: + // Called by dbus::ObjectManager when an object with the device interface + // is created. Informs observers. + void ObjectAdded(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, + DeviceAdded(object_path)); + } + + // Called by dbus::ObjectManager when an object with the device interface + // is removed. Informs observers. + void ObjectRemoved(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, + DeviceRemoved(object_path)); + } + + // Called by BluetoothPropertySet when a property value is changed, + // either by result of a signal or response to a GetAll() or Get() + // call. Informs observers. + void OnPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) { + FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, + DevicePropertyChanged(object_path, property_name)); + } + + // Called when a response for successful method call is received. + void OnSuccess(const base::Closure& callback, dbus::Response* response) { + DCHECK(response); + callback.Run(); + } + + // Called when a response for the GetConnInfo method is received. + void OnGetConnInfoSuccess(const ConnInfoCallback& callback, + dbus::Response* response) { + int16 rssi = kUnknownPower; + int16 transmit_power = kUnknownPower; + int16 max_transmit_power = kUnknownPower; + + if (!response) { + LOG(ERROR) << "GetConnInfo succeeded, but no response received."; + callback.Run(rssi, transmit_power, max_transmit_power); + return; + } + + dbus::MessageReader reader(response); + if (!reader.PopInt16(&rssi) || !reader.PopInt16(&transmit_power) || + !reader.PopInt16(&max_transmit_power)) { + LOG(ERROR) << "Arguments for GetConnInfo invalid."; + } + callback.Run(rssi, transmit_power, max_transmit_power); + } + + // Called when a response for a failed method call is received. + void OnError(const ErrorCallback& error_callback, + dbus::ErrorResponse* response) { + // Error response has optional error message argument. + std::string error_name; + std::string error_message; + if (response) { + dbus::MessageReader reader(response); + error_name = response->GetErrorName(); + reader.PopString(&error_message); + } else { + error_name = kNoResponseError; + error_message = ""; + } + error_callback.Run(error_name, error_message); + } + + dbus::ObjectManager* object_manager_; + + // List of observers interested in event notifications from us. + base::ObserverList<BluetoothDeviceClient::Observer> observers_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothDeviceClientImpl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothDeviceClientImpl); +}; + +BluetoothDeviceClient::BluetoothDeviceClient() {} + +BluetoothDeviceClient::~BluetoothDeviceClient() {} + +BluetoothDeviceClient* BluetoothDeviceClient::Create() { + return new BluetoothDeviceClientImpl(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_device_client.h b/chromeos/dbus/bluetooth_device_client.h new file mode 100644 index 0000000..6768b3e --- /dev/null +++ b/chromeos/dbus/bluetooth_device_client.h @@ -0,0 +1,206 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_DEVICE_CLIENT_H_ +#define CHROMEOS_DBUS_BLUETOOTH_DEVICE_CLIENT_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/observer_list.h" +#include "base/values.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_client.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +// BluetoothDeviceClient is used to communicate with objects representing +// remote Bluetooth Devices. +class CHROMEOS_EXPORT BluetoothDeviceClient : public DBusClient { + public: + // Structure of properties associated with bluetooth devices. + struct Properties : public dbus::PropertySet { + // The Bluetooth device address of the device. Read-only. + dbus::Property<std::string> address; + + // The Bluetooth friendly name of the device. Read-only, to give a + // different local name, use the |alias| property. + dbus::Property<std::string> name; + + // Proposed icon name for the device according to the freedesktop.org + // icon naming specification. Read-only. + dbus::Property<std::string> icon; + + // The Bluetooth class of the device. Read-only. + dbus::Property<uint32> bluetooth_class; + + // The GAP external appearance of the device. Read-only. + dbus::Property<uint16> appearance; + + // Unique numeric identifier for the vendor of the device. Read-only. + dbus::Property<uint16> vendor; + + // List of 128-bit UUIDs that represent the available remote services. + // Read-only. + dbus::Property<std::vector<std::string>> uuids; + + // Transmitted power level. This field is avaliable only for LE devices + // that include this field in AD. Read-only. + dbus::Property<int16> tx_power; + + // Indicates that the device is currently paired. Read-only. + dbus::Property<bool> paired; + + // Indicates that the device is currently connected. Read-only. + dbus::Property<bool> connected; + + // Whether the device is trusted, and connections should be always + // accepted and attempted when the device is visible. + dbus::Property<bool> trusted; + + // Whether the device is blocked, connections will be always rejected + // and the device will not be visible. + dbus::Property<bool> blocked; + + // Local alias for the device, if not set, is equal to |name|. + dbus::Property<std::string> alias; + + // Object path of the adapter the device belongs to. Read-only. + dbus::Property<dbus::ObjectPath> adapter; + + // Indicates whether the device is likely to only support pre-2.1 + // PIN Code pairing rather than 2.1 Secure Simple Pairing, this can + // give false positives. Read-only. + dbus::Property<bool> legacy_pairing; + + // Remote Device ID information in Linux kernel modalias format. Read-only. + dbus::Property<std::string> modalias; + + // Received signal strength indicator that is set when the device is + // discovered during inquiry. Read-only. + dbus::Property<int16> rssi; + + Properties(dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const PropertyChangedCallback& callback); + ~Properties() override; + }; + + // Interface for observing changes from a remote bluetooth device. + class Observer { + public: + virtual ~Observer() {} + + // Called when the remote device with object path |object_path| is added + // to the set of known devices. + virtual void DeviceAdded(const dbus::ObjectPath& object_path) {} + + // Called when the remote device with object path |object_path| is removed + // from the set of known devices. + virtual void DeviceRemoved(const dbus::ObjectPath& object_path) {} + + // Called when the device with object path |object_path| has a + // change in value of the property named |property_name|. + virtual void DevicePropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) {} + }; + + ~BluetoothDeviceClient() override; + + // Adds and removes observers for events on all remote bluetooth + // devices. Check the |object_path| parameter of observer methods to + // determine which device is issuing the event. + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + + // Returns the list of device object paths associated with the given adapter + // identified by the D-Bus object path |adapter_path|. + virtual std::vector<dbus::ObjectPath> GetDevicesForAdapter( + const dbus::ObjectPath& adapter_path) = 0; + + // Obtain the properties for the device with object path |object_path|, + // any values should be copied if needed. + virtual Properties* GetProperties(const dbus::ObjectPath& object_path) = 0; + + // The ErrorCallback is used by device methods to indicate failure. + // It receives two arguments: the name of the error in |error_name| and + // an optional message in |error_message|. + typedef base::Callback<void(const std::string& error_name, + const std::string& error_message)> ErrorCallback; + + // Connects to the device with object path |object_path|, connecting any + // profiles that can be connected to and have been flagged as auto-connected; + // may be used to connect additional profiles for an already connected device, + // and succeeds if at least one profile is connected. + virtual void Connect(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Disconnects the device with object path |object_path|, terminating + // the low-level ACL connection and any profiles using it. + virtual void Disconnect(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Connects to the profile |uuid| on the device with object path + // |object_path|, provided that the profile has been registered with a + // handler on the local device. + virtual void ConnectProfile(const dbus::ObjectPath& object_path, + const std::string& uuid, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Disconnects from the profile |uuid| on the device with object path + // |object_path|. + virtual void DisconnectProfile(const dbus::ObjectPath& object_path, + const std::string& uuid, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Initiates pairing with the device with object path |object_path| and + // retrieves all SDP records or GATT primary services. An agent must be + // registered to handle the pairing request. + virtual void Pair(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Cancels an in-progress pairing with the device with object path + // |object_path| initiated by Pair(). + virtual void CancelPairing(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // The callback invoked for a successful GetConnInfo API call with the + // RSSI, TX power, and maximum TX power of the current connection. + typedef base::Callback<void(int16 rssi, + int16 transmit_power, + int16 max_transmit_power)> ConnInfoCallback; + + // Returns the RSSI, TX power, and maximum TX power of a connection to the + // device with object path |object_path|. If the device is not connected, then + // an error will be returned. + virtual void GetConnInfo(const dbus::ObjectPath& object_path, + const ConnInfoCallback& callback, + const ErrorCallback& error_callback) = 0; + + // Creates the instance. + static BluetoothDeviceClient* Create(); + + // Constants used to indicate exceptional error conditions. + static const char kNoResponseError[]; + static const char kUnknownDeviceError[]; + + protected: + BluetoothDeviceClient(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothDeviceClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_DEVICE_CLIENT_H_ diff --git a/chromeos/dbus/bluetooth_gatt_characteristic_client.cc b/chromeos/dbus/bluetooth_gatt_characteristic_client.cc new file mode 100644 index 0000000..8e241e6 --- /dev/null +++ b/chromeos/dbus/bluetooth_gatt_characteristic_client.cc @@ -0,0 +1,303 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_gatt_characteristic_client.h" + +#include "base/bind.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "dbus/bus.h" +#include "dbus/object_manager.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +namespace { + +// TODO(armansito): Move this constant to cros_system_api. +const char kValueProperty[] = "Value"; + +} // namespace + +// static +const char BluetoothGattCharacteristicClient::kNoResponseError[] = + "org.chromium.Error.NoResponse"; +// static +const char BluetoothGattCharacteristicClient::kUnknownCharacteristicError[] = + "org.chromium.Error.UnknownCharacteristic"; + +BluetoothGattCharacteristicClient::Properties::Properties( + dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const PropertyChangedCallback& callback) + : dbus::PropertySet(object_proxy, interface_name, callback) { + RegisterProperty(bluetooth_gatt_characteristic::kUUIDProperty, &uuid); + RegisterProperty(bluetooth_gatt_characteristic::kServiceProperty, &service); + RegisterProperty(kValueProperty, &value); + RegisterProperty(bluetooth_gatt_characteristic::kNotifyingProperty, + ¬ifying); + RegisterProperty(bluetooth_gatt_characteristic::kFlagsProperty, &flags); + RegisterProperty(bluetooth_gatt_characteristic::kDescriptorsProperty, + &descriptors); +} + +BluetoothGattCharacteristicClient::Properties::~Properties() {} + +// The BluetoothGattCharacteristicClient implementation used in production. +class BluetoothGattCharacteristicClientImpl + : public BluetoothGattCharacteristicClient, + public dbus::ObjectManager::Interface { + public: + BluetoothGattCharacteristicClientImpl() + : object_manager_(NULL), weak_ptr_factory_(this) {} + + ~BluetoothGattCharacteristicClientImpl() override { + object_manager_->UnregisterInterface( + bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface); + } + + // BluetoothGattCharacteristicClient override. + void AddObserver( + BluetoothGattCharacteristicClient::Observer* observer) override { + DCHECK(observer); + observers_.AddObserver(observer); + } + + // BluetoothGattCharacteristicClient override. + void RemoveObserver( + BluetoothGattCharacteristicClient::Observer* observer) override { + DCHECK(observer); + observers_.RemoveObserver(observer); + } + + // BluetoothGattCharacteristicClient override. + std::vector<dbus::ObjectPath> GetCharacteristics() override { + DCHECK(object_manager_); + return object_manager_->GetObjectsWithInterface( + bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface); + } + + // BluetoothGattCharacteristicClient override. + Properties* GetProperties(const dbus::ObjectPath& object_path) override { + DCHECK(object_manager_); + return static_cast<Properties*>(object_manager_->GetProperties( + object_path, + bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface)); + } + + // BluetoothGattCharacteristicClient override. + void ReadValue(const dbus::ObjectPath& object_path, + const ValueCallback& callback, + const ErrorCallback& error_callback) override { + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownCharacteristicError, ""); + return; + } + + dbus::MethodCall method_call( + bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface, + bluetooth_gatt_characteristic::kReadValue); + + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothGattCharacteristicClientImpl::OnValueSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothGattCharacteristicClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothGattCharacteristicClient override. + void WriteValue(const dbus::ObjectPath& object_path, + const std::vector<uint8>& value, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownCharacteristicError, ""); + return; + } + + dbus::MethodCall method_call( + bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface, + bluetooth_gatt_characteristic::kWriteValue); + dbus::MessageWriter writer(&method_call); + writer.AppendArrayOfBytes(value.data(), value.size()); + + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothGattCharacteristicClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothGattCharacteristicClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothGattCharacteristicClient override. + void StartNotify(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownCharacteristicError, ""); + return; + } + + dbus::MethodCall method_call( + bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface, + bluetooth_gatt_characteristic::kStartNotify); + + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothGattCharacteristicClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothGattCharacteristicClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothGattCharacteristicClient override. + void StopNotify(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownCharacteristicError, ""); + return; + } + + dbus::MethodCall method_call( + bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface, + bluetooth_gatt_characteristic::kStopNotify); + + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothGattCharacteristicClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothGattCharacteristicClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // dbus::ObjectManager::Interface override. + dbus::PropertySet* CreateProperties( + dbus::ObjectProxy* object_proxy, + const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + Properties* properties = new Properties( + object_proxy, interface_name, + base::Bind(&BluetoothGattCharacteristicClientImpl::OnPropertyChanged, + weak_ptr_factory_.GetWeakPtr(), object_path)); + return static_cast<dbus::PropertySet*>(properties); + } + + // dbus::ObjectManager::Interface override. + void ObjectAdded(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + VLOG(2) << "Remote GATT characteristic added: " << object_path.value(); + FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, + GattCharacteristicAdded(object_path)); + } + + // dbus::ObjectManager::Interface override. + void ObjectRemoved(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + VLOG(2) << "Remote GATT characteristic removed: " << object_path.value(); + FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, + GattCharacteristicRemoved(object_path)); + } + + protected: + // chromeos::DBusClient override. + void Init(dbus::Bus* bus) override { + object_manager_ = bus->GetObjectManager( + bluetooth_object_manager::kBluetoothObjectManagerServiceName, + dbus::ObjectPath( + bluetooth_object_manager::kBluetoothObjectManagerServicePath)); + object_manager_->RegisterInterface( + bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface, + this); + } + + private: + // Called by dbus::PropertySet when a property value is changed, either by + // result of a signal or response to a GetAll() or Get() call. Informs + // observers. + virtual void OnPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) { + VLOG(2) << "Remote GATT characteristic property changed: " + << object_path.value() << ": " << property_name; + FOR_EACH_OBSERVER( + BluetoothGattCharacteristicClient::Observer, observers_, + GattCharacteristicPropertyChanged(object_path, property_name)); + } + + // Called when a response for successful method call is received. + void OnSuccess(const base::Closure& callback, dbus::Response* response) { + DCHECK(response); + callback.Run(); + } + + // Called when a characteristic value response for a successful method call + // is received. + void OnValueSuccess(const ValueCallback& callback, dbus::Response* response) { + DCHECK(response); + dbus::MessageReader reader(response); + + const uint8* bytes = NULL; + size_t length = 0; + + if (!reader.PopArrayOfBytes(&bytes, &length)) + VLOG(2) << "Error reading array of bytes in ValueCallback"; + + std::vector<uint8> value; + + if (bytes) + value.assign(bytes, bytes + length); + + callback.Run(value); + } + + // Called when a response for a failed method call is received. + void OnError(const ErrorCallback& error_callback, + dbus::ErrorResponse* response) { + // Error response has optional error message argument. + std::string error_name; + std::string error_message; + if (response) { + dbus::MessageReader reader(response); + error_name = response->GetErrorName(); + reader.PopString(&error_message); + } else { + error_name = kNoResponseError; + error_message = ""; + } + error_callback.Run(error_name, error_message); + } + + dbus::ObjectManager* object_manager_; + + // List of observers interested in event notifications from us. + base::ObserverList<BluetoothGattCharacteristicClient::Observer> observers_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothGattCharacteristicClientImpl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothGattCharacteristicClientImpl); +}; + +BluetoothGattCharacteristicClient::BluetoothGattCharacteristicClient() {} + +BluetoothGattCharacteristicClient::~BluetoothGattCharacteristicClient() {} + +// static +BluetoothGattCharacteristicClient* BluetoothGattCharacteristicClient::Create() { + return new BluetoothGattCharacteristicClientImpl(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_gatt_characteristic_client.h b/chromeos/dbus/bluetooth_gatt_characteristic_client.h new file mode 100644 index 0000000..b040b56 --- /dev/null +++ b/chromeos/dbus/bluetooth_gatt_characteristic_client.h @@ -0,0 +1,143 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_GATT_CHARACTERISTIC_CLIENT_H_ +#define CHROMEOS_DBUS_BLUETOOTH_GATT_CHARACTERISTIC_CLIENT_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_client.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +// BluetoothGattCharacteristicClient is used to communicate with remote GATT +// characteristic objects exposed by the Bluetooth daemon. +class CHROMEOS_EXPORT BluetoothGattCharacteristicClient : public DBusClient { + public: + // Structure of properties associated with GATT characteristics. + struct Properties : public dbus::PropertySet { + // The 128-bit characteristic UUID. [read-only] + dbus::Property<std::string> uuid; + + // Object path of the GATT service the characteristic belongs to. + // [read-only] + dbus::Property<dbus::ObjectPath> service; + + // The cached value of the characteristic. This property gets updated only + // after a successful read request and when a notification or indication is + // received. [read-only] + dbus::Property<std::vector<uint8_t>> value; + + // Whether or not this characteristic is currently sending ValueUpdated + // signals. [read-only] + dbus::Property<bool> notifying; + + // List of flags representing the GATT "Characteristic Properties bit field" + // and properties read from the GATT "Characteristic Extended Properties" + // descriptor bit field. [read-only, optional] + dbus::Property<std::vector<std::string>> flags; + + // Array of object paths representing the descriptors of this + // characteristic. [read-only] + dbus::Property<std::vector<dbus::ObjectPath>> descriptors; + + Properties(dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const PropertyChangedCallback& callback); + ~Properties() override; + }; + + // Interface for observing changes from a remote GATT characteristic. + class Observer { + public: + virtual ~Observer() {} + + // Called when the GATT characteristic with object path |object_path| is + // added to the system. + virtual void GattCharacteristicAdded(const dbus::ObjectPath& object_path) {} + + // Called when the GATT characteristic with object path |object_path| is + // removed from the system. + virtual void GattCharacteristicRemoved( + const dbus::ObjectPath& object_path) {} + + // Called when the GATT characteristic with object path |object_path| has a + // change in the value of the property named |property_name|. + virtual void GattCharacteristicPropertyChanged( + const dbus::ObjectPath& object_path, + const std::string& property_name) {} + }; + + // Callbacks used to report the result of asynchronous methods. + typedef base::Callback<void(const std::string& error_name, + const std::string& error_message)> ErrorCallback; + typedef base::Callback<void(const std::vector<uint8>& value)> ValueCallback; + + ~BluetoothGattCharacteristicClient() override; + + // Adds and removes observers for events on all remote GATT characteristics. + // Check the |object_path| parameter of observer methods to determine which + // GATT characteristic is issuing the event. + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + + // Returns the list of GATT characteristic object paths known to the system. + virtual std::vector<dbus::ObjectPath> GetCharacteristics() = 0; + + // Obtain the properties for the GATT characteristic with object path + // |object_path|. Values should be copied if needed. + virtual Properties* GetProperties(const dbus::ObjectPath& object_path) = 0; + + // Issues a request to read the value of GATT characteristic with object path + // |object_path| and returns the value in |callback| on success. On error, + // invokes |error_callback|. + virtual void ReadValue(const dbus::ObjectPath& object_path, + const ValueCallback& callback, + const ErrorCallback& error_callback) = 0; + + // Issues a request to write the value of GATT characteristic with object path + // |object_path| with value |value|. Invokes |callback| on success and + // |error_callback| on failure. + virtual void WriteValue(const dbus::ObjectPath& object_path, + const std::vector<uint8>& value, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Starts a notification session from this characteristic with object path + // |object_path| if it supports value notifications or indications. Invokes + // |callback| on success and |error_callback| on failure. + virtual void StartNotify(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Cancels any previous StartNotify transaction for characteristic with + // object path |object_path|. Invokes |callback| on success and + // |error_callback| on failure. + virtual void StopNotify(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Creates the instance. + static BluetoothGattCharacteristicClient* Create(); + + // Constants used to indicate exceptional error conditions. + static const char kNoResponseError[]; + static const char kUnknownCharacteristicError[]; + + protected: + BluetoothGattCharacteristicClient(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothGattCharacteristicClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_GATT_CHARACTERISTIC_CLIENT_H_ diff --git a/chromeos/dbus/bluetooth_gatt_characteristic_service_provider.cc b/chromeos/dbus/bluetooth_gatt_characteristic_service_provider.cc new file mode 100644 index 0000000..856e60d --- /dev/null +++ b/chromeos/dbus/bluetooth_gatt_characteristic_service_provider.cc @@ -0,0 +1,464 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_gatt_characteristic_service_provider.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/weak_ptr.h" +#include "base/strings/string_util.h" +#include "base/threading/platform_thread.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_gatt_characteristic_service_provider.h" +#include "dbus/exported_object.h" +#include "dbus/message.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { +namespace { +const char kErrorInvalidArgs[] = "org.freedesktop.DBus.Error.InvalidArgs"; +const char kErrorPropertyReadOnly[] = + "org.freedesktop.DBus.Error.PropertyReadOnly"; +const char kErrorFailed[] = "org.freedesktop.DBus.Error.Failed"; +} // namespace + +// The BluetoothGattCharacteristicServiceProvider implementation used in +// production. +class BluetoothGattCharacteristicServiceProviderImpl + : public BluetoothGattCharacteristicServiceProvider { + public: + BluetoothGattCharacteristicServiceProviderImpl( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate, + const std::string& uuid, + const std::vector<std::string>& flags, + const std::vector<std::string>& permissions, + const dbus::ObjectPath& service_path) + : origin_thread_id_(base::PlatformThread::CurrentId()), + uuid_(uuid), + bus_(bus), + delegate_(delegate), + object_path_(object_path), + service_path_(service_path), + weak_ptr_factory_(this) { + VLOG(1) << "Created Bluetooth GATT characteristic: " << object_path.value() + << " UUID: " << uuid; + DCHECK(bus_); + DCHECK(delegate_); + DCHECK(!uuid_.empty()); + DCHECK(object_path_.IsValid()); + DCHECK(service_path_.IsValid()); + DCHECK(base::StartsWith(object_path_.value(), service_path_.value() + "/", + base::CompareCase::SENSITIVE)); + + exported_object_ = bus_->GetExportedObject(object_path_); + + exported_object_->ExportMethod( + dbus::kDBusPropertiesInterface, dbus::kDBusPropertiesGet, + base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::Get, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + dbus::kDBusPropertiesInterface, dbus::kDBusPropertiesSet, + base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::Set, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + dbus::kDBusPropertiesInterface, dbus::kDBusPropertiesGetAll, + base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::GetAll, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + } + + ~BluetoothGattCharacteristicServiceProviderImpl() override { + VLOG(1) << "Cleaning up Bluetooth GATT characteristic: " + << object_path_.value(); + bus_->UnregisterExportedObject(object_path_); + } + + // BluetoothGattCharacteristicServiceProvider override. + void SendValueChanged(const std::vector<uint8>& value) override { + VLOG(2) << "Emitting a PropertiesChanged signal for characteristic value."; + dbus::Signal signal(dbus::kDBusPropertiesInterface, + dbus::kDBusPropertiesChangedSignal); + dbus::MessageWriter writer(&signal); + dbus::MessageWriter array_writer(NULL); + dbus::MessageWriter dict_entry_writer(NULL); + dbus::MessageWriter variant_writer(NULL); + + // interface_name + writer.AppendString( + bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface); + + // changed_properties + writer.OpenArray("{sv}", &array_writer); + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString( + bluetooth_gatt_characteristic::kValueProperty); + dict_entry_writer.OpenVariant("ay", &variant_writer); + variant_writer.AppendArrayOfBytes(value.data(), value.size()); + dict_entry_writer.CloseContainer(&variant_writer); + array_writer.CloseContainer(&dict_entry_writer); + writer.CloseContainer(&array_writer); + + // invalidated_properties. + writer.OpenArray("s", &array_writer); + writer.CloseContainer(&array_writer); + + exported_object_->SendSignal(&signal); + } + + private: + // Returns true if the current thread is on the origin thread. + bool OnOriginThread() { + return base::PlatformThread::CurrentId() == origin_thread_id_; + } + + // Called by dbus:: when the Bluetooth daemon fetches a single property of + // the characteristic. + void Get(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "BluetoothGattCharacteristicServiceProvider::Get: " + << object_path_.value(); + DCHECK(OnOriginThread()); + + dbus::MessageReader reader(method_call); + + std::string interface_name; + std::string property_name; + if (!reader.PopString(&interface_name) || + !reader.PopString(&property_name) || reader.HasMoreData()) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall(method_call, kErrorInvalidArgs, + "Expected 'ss'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Only the GATT characteristic interface is supported. + if (interface_name != + bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "No such interface: '" + interface_name + "'."); + response_sender.Run(error_response.Pass()); + return; + } + + // If getting the "Value" property, obtain the value from the delegate. + if (property_name == bluetooth_gatt_characteristic::kValueProperty) { + DCHECK(delegate_); + delegate_->GetCharacteristicValue( + base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnGet, + weak_ptr_factory_.GetWeakPtr(), method_call, + response_sender), + base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure, + weak_ptr_factory_.GetWeakPtr(), method_call, + response_sender)); + return; + } + + scoped_ptr<dbus::Response> response = + dbus::Response::FromMethodCall(method_call); + dbus::MessageWriter writer(response.get()); + dbus::MessageWriter variant_writer(NULL); + + // TODO(armansito): Process the "Flags" and "Permissions" properties below. + if (property_name == bluetooth_gatt_characteristic::kUUIDProperty) { + writer.OpenVariant("s", &variant_writer); + variant_writer.AppendString(uuid_); + writer.CloseContainer(&variant_writer); + } else if (property_name == + bluetooth_gatt_characteristic::kServiceProperty) { + writer.OpenVariant("o", &variant_writer); + variant_writer.AppendObjectPath(service_path_); + writer.CloseContainer(&variant_writer); + } else { + response = dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "No such property: '" + property_name + "'."); + } + + response_sender.Run(response.Pass()); + } + + // Called by dbus:: when the Bluetooth daemon sets a single property of the + // characteristic. + void Set(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "BluetoothGattCharacteristicServiceProvider::Set: " + << object_path_.value(); + DCHECK(OnOriginThread()); + + dbus::MessageReader reader(method_call); + + std::string interface_name; + std::string property_name; + dbus::MessageReader variant_reader(NULL); + if (!reader.PopString(&interface_name) || + !reader.PopString(&property_name) || + !reader.PopVariant(&variant_reader) || reader.HasMoreData()) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall(method_call, kErrorInvalidArgs, + "Expected 'ssv'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Only the GATT characteristic interface is allowed. + if (interface_name != + bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "No such interface: '" + interface_name + "'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Only the "Value" property is writeable. + if (property_name != bluetooth_gatt_characteristic::kValueProperty) { + std::string error_name; + std::string error_message; + if (property_name == bluetooth_gatt_characteristic::kUUIDProperty || + property_name == bluetooth_gatt_characteristic::kServiceProperty) { + error_name = kErrorPropertyReadOnly; + error_message = "Read-only property: '" + property_name + "'."; + } else { + error_name = kErrorInvalidArgs; + error_message = "No such property: '" + property_name + "'."; + } + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall(method_call, error_name, + error_message); + response_sender.Run(error_response.Pass()); + return; + } + + // Obtain the value. + const uint8* bytes = NULL; + size_t length = 0; + if (!variant_reader.PopArrayOfBytes(&bytes, &length)) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "Property '" + property_name + "' has type 'ay'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Pass the set request onto the delegate. + std::vector<uint8> value(bytes, bytes + length); + DCHECK(delegate_); + delegate_->SetCharacteristicValue( + value, + base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnSet, + weak_ptr_factory_.GetWeakPtr(), method_call, + response_sender), + base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure, + weak_ptr_factory_.GetWeakPtr(), method_call, + response_sender)); + } + + // Called by dbus:: when the Bluetooth daemon fetches all properties of the + // characteristic. + void GetAll(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "BluetoothGattCharacteristicServiceProvider::GetAll: " + << object_path_.value(); + DCHECK(OnOriginThread()); + + dbus::MessageReader reader(method_call); + + std::string interface_name; + if (!reader.PopString(&interface_name) || reader.HasMoreData()) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall(method_call, kErrorInvalidArgs, + "Expected 's'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Only the GATT characteristic interface is supported. + if (interface_name != + bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "No such interface: '" + interface_name + "'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Try to obtain the value from the delegate. We will construct the + // response in the success callback. + DCHECK(delegate_); + delegate_->GetCharacteristicValue( + base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnGetAll, + weak_ptr_factory_.GetWeakPtr(), method_call, + response_sender), + base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure, + weak_ptr_factory_.GetWeakPtr(), method_call, + response_sender)); + } + + // Called by dbus:: when a method is exported. + void OnExported(const std::string& interface_name, + const std::string& method_name, + bool success) { + LOG_IF(WARNING, !success) << "Failed to export " << interface_name << "." + << method_name; + } + + // Called by the Delegate in response to a method to call to get all + // properties, in which the delegate has successfully returned the + // characteristic value. + void OnGetAll(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender, + const std::vector<uint8>& value) { + VLOG(2) << "Characteristic value obtained from delegate. Responding to " + << "GetAll."; + + scoped_ptr<dbus::Response> response = + dbus::Response::FromMethodCall(method_call); + dbus::MessageWriter writer(response.get()); + dbus::MessageWriter array_writer(NULL); + dbus::MessageWriter dict_entry_writer(NULL); + dbus::MessageWriter variant_writer(NULL); + + writer.OpenArray("{sv}", &array_writer); + + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString( + bluetooth_gatt_characteristic::kUUIDProperty); + dict_entry_writer.AppendVariantOfString(uuid_); + array_writer.CloseContainer(&dict_entry_writer); + + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString( + bluetooth_gatt_characteristic::kServiceProperty); + dict_entry_writer.AppendVariantOfObjectPath(service_path_); + array_writer.CloseContainer(&dict_entry_writer); + + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString( + bluetooth_gatt_characteristic::kValueProperty); + dict_entry_writer.OpenVariant("ay", &variant_writer); + variant_writer.AppendArrayOfBytes(value.data(), value.size()); + dict_entry_writer.CloseContainer(&variant_writer); + array_writer.CloseContainer(&dict_entry_writer); + + // TODO(armansito): Process Flags & Permissions properties. + + writer.CloseContainer(&array_writer); + + response_sender.Run(response.Pass()); + } + + // Called by the Delegate in response to a successful method call to get the + // characteristic value. + void OnGet(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender, + const std::vector<uint8>& value) { + VLOG(2) << "Returning characteristic value obtained from delegate."; + scoped_ptr<dbus::Response> response = + dbus::Response::FromMethodCall(method_call); + dbus::MessageWriter writer(response.get()); + dbus::MessageWriter variant_writer(NULL); + + writer.OpenVariant("ay", &variant_writer); + variant_writer.AppendArrayOfBytes(value.data(), value.size()); + writer.CloseContainer(&variant_writer); + + response_sender.Run(response.Pass()); + } + + // Called by the Delegate in response to a successful method call to set the + // characteristic value. + void OnSet(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "Successfully set characteristic value. Return success."; + response_sender.Run(dbus::Response::FromMethodCall(method_call)); + } + + // Called by the Delegate in response to a failed method call to get or set + // the characteristic value. + void OnFailure(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "Failed to get/set characteristic value. Report error."; + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorFailed, + "Failed to get/set characteristic value."); + response_sender.Run(error_response.Pass()); + } + + // Origin thread (i.e. the UI thread in production). + base::PlatformThreadId origin_thread_id_; + + // 128-bit characteristic UUID of this object. + std::string uuid_; + + // D-Bus bus object is exported on, not owned by this object and must + // outlive it. + dbus::Bus* bus_; + + // Incoming methods to get and set the "Value" property are passed on to the + // delegate and callbacks passed to generate a reply. |delegate_| is generally + // the object that owns this one and must outlive it. + Delegate* delegate_; + + // D-Bus object path of object we are exporting, kept so we can unregister + // again in our destructor. + dbus::ObjectPath object_path_; + + // Object path of the GATT service that the exported characteristic belongs + // to. + dbus::ObjectPath service_path_; + + // D-Bus object we are exporting, owned by this object. + scoped_refptr<dbus::ExportedObject> exported_object_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothGattCharacteristicServiceProviderImpl> + weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothGattCharacteristicServiceProviderImpl); +}; + +BluetoothGattCharacteristicServiceProvider:: + BluetoothGattCharacteristicServiceProvider() {} + +BluetoothGattCharacteristicServiceProvider:: + ~BluetoothGattCharacteristicServiceProvider() {} + +// static +BluetoothGattCharacteristicServiceProvider* +BluetoothGattCharacteristicServiceProvider::Create( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate, + const std::string& uuid, + const std::vector<std::string>& flags, + const std::vector<std::string>& permissions, + const dbus::ObjectPath& service_path) { + if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH)) { + return new BluetoothGattCharacteristicServiceProviderImpl( + bus, object_path, delegate, uuid, flags, permissions, service_path); + } + return new FakeBluetoothGattCharacteristicServiceProvider( + object_path, delegate, uuid, flags, permissions, service_path); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_gatt_characteristic_service_provider.h b/chromeos/dbus/bluetooth_gatt_characteristic_service_provider.h new file mode 100644 index 0000000..174a908 --- /dev/null +++ b/chromeos/dbus/bluetooth_gatt_characteristic_service_provider.h @@ -0,0 +1,113 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_GATT_CHARACTERISTIC_SERVICE_PROVIDER_H_ +#define CHROMEOS_DBUS_BLUETOOTH_GATT_CHARACTERISTIC_SERVICE_PROVIDER_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "chromeos/chromeos_export.h" +#include "dbus/bus.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// BluetoothGattCharacteristicServiceProvider is used to provide a D-Bus object +// that represents a local GATT characteristic that the Bluetooth daemon can +// communicate with. +// +// Instantiate with a chosen D-Bus object path, delegate, and other fields. +// The Bluetooth daemon communicates with a GATT characteristic using the +// standard DBus.Properties interface. While most properties of the GATT +// characteristic interface are read-only and don't change throughout the +// life-time of the object, the "Value" property is both writeable and its +// value can change. Both Get and Set operations performed on the "Value" +// property are delegated to the Delegate object, an instance of which is +// mandatory during initialization. In addition, a "SendValueChanged" method is +// provided, which emits a DBus.Properties.PropertyChanged signal for the +// "Value" property. +class CHROMEOS_EXPORT BluetoothGattCharacteristicServiceProvider { + public: + // Interface for reacting to GATT characteristic value requests. + class Delegate { + public: + virtual ~Delegate() {} + + // ValueCallback is used for methods that require a characteristic value + // to be returned. + typedef base::Callback<void(const std::vector<uint8>&)> ValueCallback; + + // ErrorCallback is used by methods to report failure. + typedef base::Closure ErrorCallback; + + // This method will be called when a remote device requests to read the + // value of the exported GATT characteristic. Invoke |callback| with a value + // to return that value to the requester. Invoke |error_callback| to report + // a failure to read the value. This can happen, for example, if the + // characteristic has no read permission set. Either callback should be + // invoked after a reasonable amount of time, since the request will time + // out if left pending for too long. + virtual void GetCharacteristicValue( + const ValueCallback& callback, + const ErrorCallback& error_callback) = 0; + + // This method will be called, when a remote device requests to write the + // value of the exported GATT characteristic. Invoke |callback| to report + // that the value was successfully written. Invoke |error_callback| to + // report a failure to write the value. This can happen, for example, if the + // characteristic has no write permission set. Either callback should be + // invoked after a reasonable amount of time, since the request will time + // out if left pending for too long. + // + // The delegate should use this method to perform any side-effects that may + // occur based on the set value and potentially send a property changed + // signal to notify the Bluetooth daemon that the value has changed. + virtual void SetCharacteristicValue( + const std::vector<uint8>& value, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + }; + + virtual ~BluetoothGattCharacteristicServiceProvider(); + + // Send a PropertyChanged signal to notify the Bluetooth daemon that the value + // of the "Value" property has changed to |value|. + virtual void SendValueChanged(const std::vector<uint8>& value) = 0; + + // Creates the instance, where |bus| is the D-Bus bus connection to export + // the object onto, |uuid| is the 128-bit GATT characteristic UUID, + // |flags| is the list of GATT characteristic properties, |permissions| is the + // list of attribute permissions, |service_path| is the object path of the + // exported GATT service the characteristic belongs to, |object_path| is the + // object path that the characteristic should have, and |delegate| is the + // object that "Value" Get/Set requests will be passed to and responses + // generated from. + // + // Object paths of GATT characteristics must be hierarchical to the path of + // the GATT service they belong to. Hence, |object_path| must have + // |service_path| as its prefix. Ownership of |delegate| is not taken, thus + // the delegate should outlive this instance. A delegate should handle only + // a single exported characteristic and own it. + static BluetoothGattCharacteristicServiceProvider* Create( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate, + const std::string& uuid, + const std::vector<std::string>& flags, + const std::vector<std::string>& permissions, + const dbus::ObjectPath& service_path); + + protected: + BluetoothGattCharacteristicServiceProvider(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothGattCharacteristicServiceProvider); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_GATT_CHARACTERISTIC_SERVICE_PROVIDER_H_ diff --git a/chromeos/dbus/bluetooth_gatt_descriptor_client.cc b/chromeos/dbus/bluetooth_gatt_descriptor_client.cc new file mode 100644 index 0000000..4d97415 --- /dev/null +++ b/chromeos/dbus/bluetooth_gatt_descriptor_client.cc @@ -0,0 +1,251 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_gatt_descriptor_client.h" + +#include "base/bind.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "dbus/bus.h" +#include "dbus/object_manager.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +namespace { + +// TODO(armansito): Move this constant to cros_system_api. +const char kValueProperty[] = "Value"; + +} // namespace + +// static +const char BluetoothGattDescriptorClient::kNoResponseError[] = + "org.chromium.Error.NoResponse"; +// static +const char BluetoothGattDescriptorClient::kUnknownDescriptorError[] = + "org.chromium.Error.UnknownDescriptor"; + +BluetoothGattDescriptorClient::Properties::Properties( + dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const PropertyChangedCallback& callback) + : dbus::PropertySet(object_proxy, interface_name, callback) { + RegisterProperty(bluetooth_gatt_descriptor::kUUIDProperty, &uuid); + RegisterProperty(bluetooth_gatt_descriptor::kCharacteristicProperty, + &characteristic); + RegisterProperty(kValueProperty, &value); +} + +BluetoothGattDescriptorClient::Properties::~Properties() {} + +// The BluetoothGattDescriptorClient implementation used in production. +class BluetoothGattDescriptorClientImpl + : public BluetoothGattDescriptorClient, + public dbus::ObjectManager::Interface { + public: + BluetoothGattDescriptorClientImpl() + : object_manager_(NULL), weak_ptr_factory_(this) {} + + ~BluetoothGattDescriptorClientImpl() override { + object_manager_->UnregisterInterface( + bluetooth_gatt_descriptor::kBluetoothGattDescriptorInterface); + } + + // BluetoothGattDescriptorClientImpl override. + void AddObserver(BluetoothGattDescriptorClient::Observer* observer) override { + DCHECK(observer); + observers_.AddObserver(observer); + } + + // BluetoothGattDescriptorClientImpl override. + void RemoveObserver( + BluetoothGattDescriptorClient::Observer* observer) override { + DCHECK(observer); + observers_.RemoveObserver(observer); + } + + // BluetoothGattDescriptorClientImpl override. + std::vector<dbus::ObjectPath> GetDescriptors() override { + DCHECK(object_manager_); + return object_manager_->GetObjectsWithInterface( + bluetooth_gatt_descriptor::kBluetoothGattDescriptorInterface); + } + + // BluetoothGattDescriptorClientImpl override. + Properties* GetProperties(const dbus::ObjectPath& object_path) override { + DCHECK(object_manager_); + return static_cast<Properties*>(object_manager_->GetProperties( + object_path, + bluetooth_gatt_descriptor::kBluetoothGattDescriptorInterface)); + } + + // BluetoothGattDescriptorClientImpl override. + void ReadValue(const dbus::ObjectPath& object_path, + const ValueCallback& callback, + const ErrorCallback& error_callback) override { + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownDescriptorError, ""); + return; + } + + dbus::MethodCall method_call( + bluetooth_gatt_descriptor::kBluetoothGattDescriptorInterface, + bluetooth_gatt_descriptor::kReadValue); + + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothGattDescriptorClientImpl::OnValueSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothGattDescriptorClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothGattDescriptorClientImpl override. + void WriteValue(const dbus::ObjectPath& object_path, + const std::vector<uint8>& value, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(object_path); + if (!object_proxy) { + error_callback.Run(kUnknownDescriptorError, ""); + return; + } + + dbus::MethodCall method_call( + bluetooth_gatt_descriptor::kBluetoothGattDescriptorInterface, + bluetooth_gatt_descriptor::kWriteValue); + dbus::MessageWriter writer(&method_call); + writer.AppendArrayOfBytes(value.data(), value.size()); + + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothGattDescriptorClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothGattDescriptorClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // dbus::ObjectManager::Interface override. + dbus::PropertySet* CreateProperties( + dbus::ObjectProxy* object_proxy, + const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + Properties* properties = new Properties( + object_proxy, interface_name, + base::Bind(&BluetoothGattDescriptorClientImpl::OnPropertyChanged, + weak_ptr_factory_.GetWeakPtr(), object_path)); + return static_cast<dbus::PropertySet*>(properties); + } + + // dbus::ObjectManager::Interface override. + void ObjectAdded(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + VLOG(2) << "Remote GATT descriptor added: " << object_path.value(); + FOR_EACH_OBSERVER(BluetoothGattDescriptorClient::Observer, observers_, + GattDescriptorAdded(object_path)); + } + + // dbus::ObjectManager::Interface override. + void ObjectRemoved(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + VLOG(2) << "Remote GATT descriptor removed: " << object_path.value(); + FOR_EACH_OBSERVER(BluetoothGattDescriptorClient::Observer, observers_, + GattDescriptorRemoved(object_path)); + } + + protected: + // chromeos::DBusClient override. + void Init(dbus::Bus* bus) override { + object_manager_ = bus->GetObjectManager( + bluetooth_object_manager::kBluetoothObjectManagerServiceName, + dbus::ObjectPath( + bluetooth_object_manager::kBluetoothObjectManagerServicePath)); + object_manager_->RegisterInterface( + bluetooth_gatt_descriptor::kBluetoothGattDescriptorInterface, this); + } + + private: + // Called by dbus::PropertySet when a property value is changed, either by + // result of a signal or response to a GetAll() or Get() call. Informs + // observers. + virtual void OnPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) { + VLOG(2) << "Remote GATT descriptor property changed: " + << object_path.value() << ": " << property_name; + FOR_EACH_OBSERVER( + BluetoothGattDescriptorClient::Observer, observers_, + GattDescriptorPropertyChanged(object_path, property_name)); + } + + // Called when a response for a successful method call is received. + void OnSuccess(const base::Closure& callback, dbus::Response* response) { + DCHECK(response); + callback.Run(); + } + + // Called when a descriptor value response for a successful method call is + // received. + void OnValueSuccess(const ValueCallback& callback, dbus::Response* response) { + DCHECK(response); + dbus::MessageReader reader(response); + + const uint8* bytes = NULL; + size_t length = 0; + + if (!reader.PopArrayOfBytes(&bytes, &length)) + VLOG(2) << "Error reading array of bytes in ValueCallback"; + + std::vector<uint8> value; + + if (bytes) + value.assign(bytes, bytes + length); + + callback.Run(value); + } + + // Called when a response for a failed method call is received. + void OnError(const ErrorCallback& error_callback, + dbus::ErrorResponse* response) { + // Error response has optional error message argument. + std::string error_name; + std::string error_message; + if (response) { + dbus::MessageReader reader(response); + error_name = response->GetErrorName(); + reader.PopString(&error_message); + } else { + error_name = kNoResponseError; + error_message = ""; + } + error_callback.Run(error_name, error_message); + } + + dbus::ObjectManager* object_manager_; + + // List of observers interested in event notifications from us. + base::ObserverList<BluetoothGattDescriptorClient::Observer> observers_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothGattDescriptorClientImpl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothGattDescriptorClientImpl); +}; + +BluetoothGattDescriptorClient::BluetoothGattDescriptorClient() {} + +BluetoothGattDescriptorClient::~BluetoothGattDescriptorClient() {} + +// static +BluetoothGattDescriptorClient* BluetoothGattDescriptorClient::Create() { + return new BluetoothGattDescriptorClientImpl(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_gatt_descriptor_client.h b/chromeos/dbus/bluetooth_gatt_descriptor_client.h new file mode 100644 index 0000000..15d3d9c --- /dev/null +++ b/chromeos/dbus/bluetooth_gatt_descriptor_client.h @@ -0,0 +1,115 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_GATT_DESCRIPTOR_CLIENT_H_ +#define CHROMEOS_DBUS_BLUETOOTH_GATT_DESCRIPTOR_CLIENT_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_client.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +// BluetoothGattDescriptorClient is used to communicate with remote GATT +// characteristic descriptor objects exposed by the Bluetooth daemon. +class CHROMEOS_EXPORT BluetoothGattDescriptorClient : public DBusClient { + public: + // Structure of properties associated with GATT descriptors. + struct Properties : public dbus::PropertySet { + // The 128-bit characteristic descriptor UUID. [read-only] + dbus::Property<std::string> uuid; + + // Object path of the GATT characteristic the descriptor belongs to. + // [read-only] + dbus::Property<dbus::ObjectPath> characteristic; + + // The cached value of the descriptor. This property gets updated only after + // a successful read request. [read-only] + dbus::Property<std::vector<uint8_t>> value; + + Properties(dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const PropertyChangedCallback& callback); + ~Properties() override; + }; + + // Interface for observing changes from a remote GATT characteristic + // descriptor. + class Observer { + public: + virtual ~Observer() {} + + // Called when the GATT descriptor with object path |object_path| is added + // to the system. + virtual void GattDescriptorAdded(const dbus::ObjectPath& object_path) {} + + // Called when the GATT descriptor with object path |object_path| is removed + // from the system. + virtual void GattDescriptorRemoved(const dbus::ObjectPath& object_path) {} + + // Called when the GATT descriptor with object path |object_path| has a + // change in the value of the property named |property_name|. + virtual void GattDescriptorPropertyChanged( + const dbus::ObjectPath& object_path, + const std::string& property_name) {} + }; + + // Callbacks used to report the result of asynchronous methods. + typedef base::Callback<void(const std::string& error_name, + const std::string& error_message)> ErrorCallback; + typedef base::Callback<void(const std::vector<uint8>& value)> ValueCallback; + + ~BluetoothGattDescriptorClient() override; + + // Adds and removes observers for events on all remote GATT descriptors. Check + // the |object_path| parameter of observer methods to determine which GATT + // descriptor is issuing the event. + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + + // Returns the list of GATT descriptor object paths known to the system. + virtual std::vector<dbus::ObjectPath> GetDescriptors() = 0; + + // Obtain the properties for the GATT descriptor with object path + // |object_path|. Values should be copied if needed. + virtual Properties* GetProperties(const dbus::ObjectPath& object_path) = 0; + + // Issues a request to read the value of GATT descriptor with object path + // |object_path| and returns the value in |callback| on success. On error, + // invokes |error_callback|. + virtual void ReadValue(const dbus::ObjectPath& object_path, + const ValueCallback& callback, + const ErrorCallback& error_callback) = 0; + + // Issues a request to write the value of GATT descriptor with object path + // |object_path| with value |value|. Invokes |callback| on success and + // |error_callback| on failure. + virtual void WriteValue(const dbus::ObjectPath& object_path, + const std::vector<uint8>& value, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Creates the instance. + static BluetoothGattDescriptorClient* Create(); + + // Constants used to indicate exceptional error conditions. + static const char kNoResponseError[]; + static const char kUnknownDescriptorError[]; + + protected: + BluetoothGattDescriptorClient(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothGattDescriptorClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_GATT_DESCRIPTOR_CLIENT_H_ diff --git a/chromeos/dbus/bluetooth_gatt_descriptor_service_provider.cc b/chromeos/dbus/bluetooth_gatt_descriptor_service_provider.cc new file mode 100644 index 0000000..1fe1657 --- /dev/null +++ b/chromeos/dbus/bluetooth_gatt_descriptor_service_provider.cc @@ -0,0 +1,457 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_gatt_descriptor_service_provider.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/weak_ptr.h" +#include "base/strings/string_util.h" +#include "base/threading/platform_thread.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_gatt_descriptor_service_provider.h" +#include "dbus/exported_object.h" +#include "dbus/message.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { +namespace { +const char kErrorInvalidArgs[] = "org.freedesktop.DBus.Error.InvalidArgs"; +const char kErrorPropertyReadOnly[] = + "org.freedesktop.DBus.Error.PropertyReadOnly"; +const char kErrorFailed[] = "org.freedesktop.DBus.Error.Failed"; +} // namespace + +// The BluetoothGattDescriptorServiceProvider implementation used in production. +class BluetoothGattDescriptorServiceProviderImpl + : public BluetoothGattDescriptorServiceProvider { + public: + BluetoothGattDescriptorServiceProviderImpl( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate, + const std::string& uuid, + const std::vector<std::string>& permissions, + const dbus::ObjectPath& characteristic_path) + : origin_thread_id_(base::PlatformThread::CurrentId()), + uuid_(uuid), + bus_(bus), + delegate_(delegate), + object_path_(object_path), + characteristic_path_(characteristic_path), + weak_ptr_factory_(this) { + VLOG(1) << "Created Bluetooth GATT characteristic descriptor: " + << object_path.value() << " UUID: " << uuid; + DCHECK(bus_); + DCHECK(delegate_); + DCHECK(!uuid_.empty()); + DCHECK(object_path_.IsValid()); + DCHECK(characteristic_path_.IsValid()); + DCHECK(base::StartsWith(object_path_.value(), + characteristic_path_.value() + "/", + base::CompareCase::SENSITIVE)); + + exported_object_ = bus_->GetExportedObject(object_path_); + + exported_object_->ExportMethod( + dbus::kDBusPropertiesInterface, dbus::kDBusPropertiesGet, + base::Bind(&BluetoothGattDescriptorServiceProviderImpl::Get, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothGattDescriptorServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + dbus::kDBusPropertiesInterface, dbus::kDBusPropertiesSet, + base::Bind(&BluetoothGattDescriptorServiceProviderImpl::Set, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothGattDescriptorServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + dbus::kDBusPropertiesInterface, dbus::kDBusPropertiesGetAll, + base::Bind(&BluetoothGattDescriptorServiceProviderImpl::GetAll, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothGattDescriptorServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + } + + ~BluetoothGattDescriptorServiceProviderImpl() override { + VLOG(1) << "Cleaning up Bluetooth GATT characteristic descriptor: " + << object_path_.value(); + bus_->UnregisterExportedObject(object_path_); + } + + // BluetoothGattDescriptorServiceProvider override. + void SendValueChanged(const std::vector<uint8>& value) override { + VLOG(2) << "Emitting a PropertiesChanged signal for descriptor value."; + dbus::Signal signal(dbus::kDBusPropertiesInterface, + dbus::kDBusPropertiesChangedSignal); + dbus::MessageWriter writer(&signal); + dbus::MessageWriter array_writer(NULL); + dbus::MessageWriter dict_entry_writer(NULL); + dbus::MessageWriter variant_writer(NULL); + + // interface_name + writer.AppendString( + bluetooth_gatt_descriptor::kBluetoothGattDescriptorInterface); + + // changed_properties + writer.OpenArray("{sv}", &array_writer); + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString(bluetooth_gatt_descriptor::kValueProperty); + dict_entry_writer.OpenVariant("ay", &variant_writer); + variant_writer.AppendArrayOfBytes(value.data(), value.size()); + dict_entry_writer.CloseContainer(&variant_writer); + array_writer.CloseContainer(&dict_entry_writer); + writer.CloseContainer(&array_writer); + + // invalidated_properties. + writer.OpenArray("s", &array_writer); + writer.CloseContainer(&array_writer); + + exported_object_->SendSignal(&signal); + } + + private: + // Returns true if the current thread is on the origin thread. + bool OnOriginThread() { + return base::PlatformThread::CurrentId() == origin_thread_id_; + } + + // Called by dbus:: when the Bluetooth daemon fetches a single property of + // the descriptor. + void Get(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "BluetoothGattDescriptorServiceProvider::Get: " + << object_path_.value(); + DCHECK(OnOriginThread()); + + dbus::MessageReader reader(method_call); + + std::string interface_name; + std::string property_name; + if (!reader.PopString(&interface_name) || + !reader.PopString(&property_name) || reader.HasMoreData()) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall(method_call, kErrorInvalidArgs, + "Expected 'ss'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Only the GATT descriptor interface is supported. + if (interface_name != + bluetooth_gatt_descriptor::kBluetoothGattDescriptorInterface) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "No such interface: '" + interface_name + "'."); + response_sender.Run(error_response.Pass()); + return; + } + + // If getting the "Value" property, obtain the value from the delegate. + if (property_name == bluetooth_gatt_descriptor::kValueProperty) { + DCHECK(delegate_); + delegate_->GetDescriptorValue( + base::Bind(&BluetoothGattDescriptorServiceProviderImpl::OnGet, + weak_ptr_factory_.GetWeakPtr(), method_call, + response_sender), + base::Bind(&BluetoothGattDescriptorServiceProviderImpl::OnFailure, + weak_ptr_factory_.GetWeakPtr(), method_call, + response_sender)); + return; + } + + scoped_ptr<dbus::Response> response = + dbus::Response::FromMethodCall(method_call); + dbus::MessageWriter writer(response.get()); + dbus::MessageWriter variant_writer(NULL); + + // TODO(armansito): Process the "Permissions" property below. + if (property_name == bluetooth_gatt_descriptor::kUUIDProperty) { + writer.OpenVariant("s", &variant_writer); + variant_writer.AppendString(uuid_); + writer.CloseContainer(&variant_writer); + } else if (property_name == + bluetooth_gatt_descriptor::kCharacteristicProperty) { + writer.OpenVariant("o", &variant_writer); + variant_writer.AppendObjectPath(characteristic_path_); + writer.CloseContainer(&variant_writer); + } else { + response = dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "No such property: '" + property_name + "'."); + } + + response_sender.Run(response.Pass()); + } + + // Called by dbus:: when the Bluetooth daemon sets a single property of the + // descriptor. + void Set(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "BluetoothGattDescriptorServiceProvider::Set: " + << object_path_.value(); + DCHECK(OnOriginThread()); + + dbus::MessageReader reader(method_call); + + std::string interface_name; + std::string property_name; + dbus::MessageReader variant_reader(NULL); + if (!reader.PopString(&interface_name) || + !reader.PopString(&property_name) || + !reader.PopVariant(&variant_reader) || reader.HasMoreData()) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall(method_call, kErrorInvalidArgs, + "Expected 'ssv'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Only the GATT descriptor interface is allowed. + if (interface_name != + bluetooth_gatt_descriptor::kBluetoothGattDescriptorInterface) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "No such interface: '" + interface_name + "'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Only the "Value" property is writeable. + if (property_name != bluetooth_gatt_descriptor::kValueProperty) { + std::string error_name; + std::string error_message; + if (property_name == bluetooth_gatt_descriptor::kUUIDProperty || + property_name == bluetooth_gatt_descriptor::kCharacteristicProperty) { + error_name = kErrorPropertyReadOnly; + error_message = "Read-only property: '" + property_name + "'."; + } else { + error_name = kErrorInvalidArgs; + error_message = "No such property: '" + property_name + "'."; + } + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall(method_call, error_name, + error_message); + response_sender.Run(error_response.Pass()); + return; + } + + // Obtain the value. + const uint8* bytes = NULL; + size_t length = 0; + if (!variant_reader.PopArrayOfBytes(&bytes, &length)) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "Property '" + property_name + "' has type 'ay'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Pass the set request onto the delegate. + std::vector<uint8> value(bytes, bytes + length); + DCHECK(delegate_); + delegate_->SetDescriptorValue( + value, base::Bind(&BluetoothGattDescriptorServiceProviderImpl::OnSet, + weak_ptr_factory_.GetWeakPtr(), method_call, + response_sender), + base::Bind(&BluetoothGattDescriptorServiceProviderImpl::OnFailure, + weak_ptr_factory_.GetWeakPtr(), method_call, + response_sender)); + } + + // Called by dbus:: when the Bluetooth daemon fetches all properties of the + // descriptor. + void GetAll(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "BluetoothGattDescriptorServiceProvider::GetAll: " + << object_path_.value(); + DCHECK(OnOriginThread()); + + dbus::MessageReader reader(method_call); + + std::string interface_name; + if (!reader.PopString(&interface_name) || reader.HasMoreData()) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall(method_call, kErrorInvalidArgs, + "Expected 's'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Only the GATT descriptor interface is supported. + if (interface_name != + bluetooth_gatt_descriptor::kBluetoothGattDescriptorInterface) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "No such interface: '" + interface_name + "'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Try to obtain the value from the delegate. We will construct the + // response in the success callback. + DCHECK(delegate_); + delegate_->GetDescriptorValue( + base::Bind(&BluetoothGattDescriptorServiceProviderImpl::OnGetAll, + weak_ptr_factory_.GetWeakPtr(), method_call, + response_sender), + base::Bind(&BluetoothGattDescriptorServiceProviderImpl::OnFailure, + weak_ptr_factory_.GetWeakPtr(), method_call, + response_sender)); + } + + // Called by dbus:: when a method is exported. + void OnExported(const std::string& interface_name, + const std::string& method_name, + bool success) { + LOG_IF(WARNING, !success) << "Failed to export " << interface_name << "." + << method_name; + } + + // Called by the Delegate in response to a method to call to get all + // properties, in which the delegate has successfully returned the + // descriptor value. + void OnGetAll(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender, + const std::vector<uint8>& value) { + VLOG(2) << "Descriptor value obtained from delegate. Responding to " + << "GetAll."; + + scoped_ptr<dbus::Response> response = + dbus::Response::FromMethodCall(method_call); + dbus::MessageWriter writer(response.get()); + dbus::MessageWriter array_writer(NULL); + dbus::MessageWriter dict_entry_writer(NULL); + dbus::MessageWriter variant_writer(NULL); + + writer.OpenArray("{sv}", &array_writer); + + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString(bluetooth_gatt_descriptor::kUUIDProperty); + dict_entry_writer.AppendVariantOfString(uuid_); + array_writer.CloseContainer(&dict_entry_writer); + + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString( + bluetooth_gatt_descriptor::kCharacteristicProperty); + dict_entry_writer.AppendVariantOfObjectPath(characteristic_path_); + array_writer.CloseContainer(&dict_entry_writer); + + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString(bluetooth_gatt_descriptor::kValueProperty); + dict_entry_writer.OpenVariant("ay", &variant_writer); + variant_writer.AppendArrayOfBytes(value.data(), value.size()); + dict_entry_writer.CloseContainer(&variant_writer); + array_writer.CloseContainer(&dict_entry_writer); + + // TODO(armansito): Process "Permissions" property. + + writer.CloseContainer(&array_writer); + + response_sender.Run(response.Pass()); + } + + // Called by the Delegate in response to a successful method call to get the + // descriptor value. + void OnGet(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender, + const std::vector<uint8>& value) { + VLOG(2) << "Returning descriptor value obtained from delegate."; + scoped_ptr<dbus::Response> response = + dbus::Response::FromMethodCall(method_call); + dbus::MessageWriter writer(response.get()); + dbus::MessageWriter variant_writer(NULL); + + writer.OpenVariant("ay", &variant_writer); + variant_writer.AppendArrayOfBytes(value.data(), value.size()); + writer.CloseContainer(&variant_writer); + + response_sender.Run(response.Pass()); + } + + // Called by the Delegate in response to a successful method call to set the + // descriptor value. + void OnSet(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "Successfully set descriptor value. Return success."; + response_sender.Run(dbus::Response::FromMethodCall(method_call)); + } + + // Called by the Delegate in response to a failed method call to get or set + // the descriptor value. + void OnFailure(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "Failed to get/set descriptor value. Report error."; + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorFailed, "Failed to get/set descriptor value."); + response_sender.Run(error_response.Pass()); + } + + // Origin thread (i.e. the UI thread in production). + base::PlatformThreadId origin_thread_id_; + + // 128-bit descriptor UUID of this object. + std::string uuid_; + + // D-Bus bus object is exported on, not owned by this object and must + // outlive it. + dbus::Bus* bus_; + + // Incoming methods to get and set the "Value" property are passed on to the + // delegate and callbacks passed to generate a reply. |delegate_| is generally + // the object that owns this one and must outlive it. + Delegate* delegate_; + + // D-Bus object path of object we are exporting, kept so we can unregister + // again in our destructor. + dbus::ObjectPath object_path_; + + // Object path of the GATT characteristic that the exported descriptor belongs + // to. + dbus::ObjectPath characteristic_path_; + + // D-Bus object we are exporting, owned by this object. + scoped_refptr<dbus::ExportedObject> exported_object_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothGattDescriptorServiceProviderImpl> + weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothGattDescriptorServiceProviderImpl); +}; + +BluetoothGattDescriptorServiceProvider:: + BluetoothGattDescriptorServiceProvider() {} + +BluetoothGattDescriptorServiceProvider:: + ~BluetoothGattDescriptorServiceProvider() {} + +// static +BluetoothGattDescriptorServiceProvider* +BluetoothGattDescriptorServiceProvider::Create( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate, + const std::string& uuid, + const std::vector<std::string>& permissions, + const dbus::ObjectPath& characteristic_path) { + if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH)) { + return new BluetoothGattDescriptorServiceProviderImpl( + bus, object_path, delegate, uuid, permissions, characteristic_path); + } + return new FakeBluetoothGattDescriptorServiceProvider( + object_path, delegate, uuid, permissions, characteristic_path); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_gatt_descriptor_service_provider.h b/chromeos/dbus/bluetooth_gatt_descriptor_service_provider.h new file mode 100644 index 0000000..4b5fef8 --- /dev/null +++ b/chromeos/dbus/bluetooth_gatt_descriptor_service_provider.h @@ -0,0 +1,109 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_GATT_DESCRIPTOR_SERVICE_PROVIDER_H_ +#define CHROMEOS_DBUS_BLUETOOTH_GATT_DESCRIPTOR_SERVICE_PROVIDER_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "chromeos/chromeos_export.h" +#include "dbus/bus.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// BluetoothGattDescriptorServiceProvider is used to provide a D-Bus object that +// represents a local GATT characteristic descriptor that the Bluetooth daemon +// can communicate with. +// +// Instantiate with a chosen D-Bus object path, delegate, and other fields. +// The Bluetooth daemon communicates with a GATT descriptor using the +// standard DBus.Properties interface. While most properties of the GATT +// descriptor interface are read-only and don't change throughout the +// life-time of the object, the "Value" property is both writeable and its +// value can change. Both Get and Set operations performed on the "Value" +// property are delegated to the Delegate object, an instance of which is +// mandatory during initialization. In addition, a "SendValueChanged" method is +// provided, which emits a DBus.Properties.PropertyChanged signal for the +// "Value" property. +class CHROMEOS_EXPORT BluetoothGattDescriptorServiceProvider { + public: + // Interface for reacting to GATT characteristic descriptor value requests. + class Delegate { + public: + virtual ~Delegate() {} + + // ValueCallback is used for methods that require a descriptor value + // to be returned. + typedef base::Callback<void(const std::vector<uint8>&)> ValueCallback; + + // ErrorCallback is used by methods to report failure. + typedef base::Closure ErrorCallback; + + // This method will be called when a remote device requests to read the + // value of the exported GATT descriptor. Invoke |callback| with a value + // to return that value to the requester. Invoke |error_callback| to report + // a failure to read the value. This can happen, for example, if the + // descriptor has no read permission set. Either callback should be + // invoked after a reasonable amount of time, since the request will time + // out if left pending for too long. + virtual void GetDescriptorValue(const ValueCallback& callback, + const ErrorCallback& error_callback) = 0; + + // This method will be called, when a remote device requests to write the + // value of the exported GATT descriptor. Invoke |callback| to report + // that the value was successfully written. Invoke |error_callback| to + // report a failure to write the value. This can happen, for example, if the + // descriptor has no write permission set. Either callback should be + // invoked after a reasonable amount of time, since the request will time + // out if left pending for too long. + // + // The delegate should use this method to perform any side-effects that may + // occur based on the set value and potentially send a property changed + // signal to notify the Bluetooth daemon that the value has changed. + virtual void SetDescriptorValue(const std::vector<uint8>& value, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + }; + + virtual ~BluetoothGattDescriptorServiceProvider(); + + // Send a PropertyChanged signal to notify the Bluetooth daemon that the value + // of the "Value" property has changed to |value|. + virtual void SendValueChanged(const std::vector<uint8>& value) = 0; + + // Creates the instance, where |bus| is the D-Bus bus connection to export + // the object onto, |uuid| is the 128-bit GATT descriptor UUID, |permissions| + // is the list of attribute permissions, |characteristic_path| is the object + // path of the exported GATT characteristic the descriptor belongs to, + // |object_path| is the object path that the descriptor should have, and + // |delegate| is the object that value Get/Set requests will be passed to and + // responses generated from. + // + // Object paths of GATT descriptors must be hierarchical to the path of the + // GATT characteristic they belong to. Hence, |object_path| must have + // |characteristic_path| as its prefix. Ownership of |delegate| is not taken, + // thus the delegate should outlive this instance. A delegate should handle + // only a single exported descriptor and own it. + static BluetoothGattDescriptorServiceProvider* Create( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate, + const std::string& uuid, + const std::vector<std::string>& permissions, + const dbus::ObjectPath& characteristic_path); + + protected: + BluetoothGattDescriptorServiceProvider(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothGattDescriptorServiceProvider); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_GATT_DESCRIPTOR_SERVICE_PROVIDER_H_ diff --git a/chromeos/dbus/bluetooth_gatt_manager_client.cc b/chromeos/dbus/bluetooth_gatt_manager_client.cc new file mode 100644 index 0000000..b3e98c6 --- /dev/null +++ b/chromeos/dbus/bluetooth_gatt_manager_client.cc @@ -0,0 +1,129 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_gatt_manager_client.h" + +#include "base/bind.h" +#include "base/memory/weak_ptr.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_proxy.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +const char BluetoothGattManagerClient::kNoResponseError[] = + "org.chromium.Error.NoResponse"; + +// The BluetoothGattManagerClient implementation used in production. +class BluetoothGattManagerClientImpl : public BluetoothGattManagerClient { + public: + BluetoothGattManagerClientImpl() + : object_proxy_(NULL), weak_ptr_factory_(this) {} + + ~BluetoothGattManagerClientImpl() override {} + + // BluetoothGattManagerClient override. + void RegisterService(const dbus::ObjectPath& service_path, + const Options& options, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call( + bluetooth_gatt_manager::kBluetoothGattManagerInterface, + bluetooth_gatt_manager::kRegisterService); + + dbus::MessageWriter writer(&method_call); + writer.AppendObjectPath(service_path); + + // TODO(armansito): The parameters of the Options dictionary are undefined + // but the method signature still requires a value dictionary. Pass an + // empty dictionary and fill in the contents later once this is defined. + dbus::MessageWriter array_writer(NULL); + writer.OpenArray("{sv}", &array_writer); + writer.CloseContainer(&array_writer); + + DCHECK(object_proxy_); + object_proxy_->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothGattManagerClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothGattManagerClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothGattManagerClient override. + void UnregisterService(const dbus::ObjectPath& service_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call( + bluetooth_gatt_manager::kBluetoothGattManagerInterface, + bluetooth_gatt_manager::kUnregisterService); + + dbus::MessageWriter writer(&method_call); + writer.AppendObjectPath(service_path); + + DCHECK(object_proxy_); + object_proxy_->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothGattManagerClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothGattManagerClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + protected: + // chromeos::DBusClient override. + void Init(dbus::Bus* bus) override { + DCHECK(bus); + object_proxy_ = bus->GetObjectProxy( + bluetooth_gatt_manager::kBluetoothGattManagerServiceName, + dbus::ObjectPath( + bluetooth_gatt_manager::kBluetoothGattManagerInterface)); + } + + private: + // Called when a response for a successful method call is received. + void OnSuccess(const base::Closure& callback, dbus::Response* response) { + DCHECK(response); + callback.Run(); + } + + // Called when a response for a failed method call is received. + void OnError(const ErrorCallback& error_callback, + dbus::ErrorResponse* response) { + // Error response has optional error message argument. + std::string error_name; + std::string error_message; + if (response) { + dbus::MessageReader reader(response); + error_name = response->GetErrorName(); + reader.PopString(&error_message); + } else { + error_name = kNoResponseError; + } + error_callback.Run(error_name, error_message); + } + + // The proxy to the remote GATT manager object. + dbus::ObjectProxy* object_proxy_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothGattManagerClientImpl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothGattManagerClientImpl); +}; + +BluetoothGattManagerClient::BluetoothGattManagerClient() {} + +BluetoothGattManagerClient::~BluetoothGattManagerClient() {} + +// static +BluetoothGattManagerClient* BluetoothGattManagerClient::Create() { + return new BluetoothGattManagerClientImpl(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_gatt_manager_client.h b/chromeos/dbus/bluetooth_gatt_manager_client.h new file mode 100644 index 0000000..7a16a0b --- /dev/null +++ b/chromeos/dbus/bluetooth_gatt_manager_client.h @@ -0,0 +1,72 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_GATT_MANAGER_CLIENT_H_ +#define CHROMEOS_DBUS_BLUETOOTH_GATT_MANAGER_CLIENT_H_ + +#include <string> + +#include "base/callback.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_client.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// BluetoothGattManagerClient is used to communicate with the GATT Service +// manager object of the Bluetooth daemon. +class CHROMEOS_EXPORT BluetoothGattManagerClient : public DBusClient { + public: + // Options used to register a GATT service hierarchy. + struct CHROMEOS_EXPORT Options { + // TODO(armansito): This parameter is not yet clearly defined. Add fields + // later as we know more about how this will be used. + }; + + ~BluetoothGattManagerClient() override; + + // The ErrorCallback is used by GATT manager methods to indicate failure. It + // receives two arguments: the name of the error in |error_name| and an + // optional message in |error_message|. + typedef base::Callback<void(const std::string& error_name, + const std::string& error_message)> ErrorCallback; + + // Registers a GATT service implementation within the local process at the + // D-Bus object path |service_path| with the remote GATT manager. The local + // service must implement the GattService1 interface. Characteristic objects + // must be hierarchical to their service and must use the interface + // GattCharacteristic1. Similarly, characteristic descriptor objects must + // implement the GattDescriptor1 interface and must be hierarchical to their + // characteristic. In a successful invocation of RegisterService, the + // Bluetooth daemon will discover all objects in the registered hierarchy by + // using D-Bus Object Manager. Hence, the object paths and the interfaces in + // the registered hierarchy must comply with the BlueZ GATT D-Bus + // specification. + virtual void RegisterService(const dbus::ObjectPath& service_path, + const Options& options, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Unregisters the GATT service with the D-Bus object path |service_path| from + // the remote GATT manager. + virtual void UnregisterService(const dbus::ObjectPath& service_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Creates the instance. + static BluetoothGattManagerClient* Create(); + + // Constants used to indicate exceptional error conditions. + static const char kNoResponseError[]; + + protected: + BluetoothGattManagerClient(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothGattManagerClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_GATT_MANAGER_CLIENT_H_ diff --git a/chromeos/dbus/bluetooth_gatt_service_client.cc b/chromeos/dbus/bluetooth_gatt_service_client.cc new file mode 100644 index 0000000..53572f0 --- /dev/null +++ b/chromeos/dbus/bluetooth_gatt_service_client.cc @@ -0,0 +1,143 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_gatt_service_client.h" + +#include "base/bind.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "dbus/bus.h" +#include "dbus/object_manager.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +BluetoothGattServiceClient::Properties::Properties( + dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const PropertyChangedCallback& callback) + : dbus::PropertySet(object_proxy, interface_name, callback) { + RegisterProperty(bluetooth_gatt_service::kUUIDProperty, &uuid); + RegisterProperty(bluetooth_gatt_service::kIncludesProperty, &includes); + RegisterProperty(bluetooth_gatt_service::kDeviceProperty, &device); + RegisterProperty(bluetooth_gatt_service::kPrimaryProperty, &primary); + RegisterProperty(bluetooth_gatt_service::kCharacteristicsProperty, + &characteristics); +} + +BluetoothGattServiceClient::Properties::~Properties() {} + +// The BluetoothGattServiceClient implementation used in production. +class BluetoothGattServiceClientImpl : public BluetoothGattServiceClient, + public dbus::ObjectManager::Interface { + public: + BluetoothGattServiceClientImpl() + : object_manager_(NULL), weak_ptr_factory_(this) {} + + ~BluetoothGattServiceClientImpl() override { + object_manager_->UnregisterInterface( + bluetooth_gatt_service::kBluetoothGattServiceInterface); + } + + // BluetoothGattServiceClientImpl override. + void AddObserver(BluetoothGattServiceClient::Observer* observer) override { + DCHECK(observer); + observers_.AddObserver(observer); + } + + // BluetoothGattServiceClientImpl override. + void RemoveObserver(BluetoothGattServiceClient::Observer* observer) override { + DCHECK(observer); + observers_.RemoveObserver(observer); + } + + // BluetoothGattServiceClientImpl override. + std::vector<dbus::ObjectPath> GetServices() override { + DCHECK(object_manager_); + return object_manager_->GetObjectsWithInterface( + bluetooth_gatt_service::kBluetoothGattServiceInterface); + } + + // BluetoothGattServiceClientImpl override. + Properties* GetProperties(const dbus::ObjectPath& object_path) override { + DCHECK(object_manager_); + return static_cast<Properties*>(object_manager_->GetProperties( + object_path, bluetooth_gatt_service::kBluetoothGattServiceInterface)); + } + + // dbus::ObjectManager::Interface override. + dbus::PropertySet* CreateProperties( + dbus::ObjectProxy* object_proxy, + const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + Properties* properties = new Properties( + object_proxy, interface_name, + base::Bind(&BluetoothGattServiceClientImpl::OnPropertyChanged, + weak_ptr_factory_.GetWeakPtr(), object_path)); + return static_cast<dbus::PropertySet*>(properties); + } + + // dbus::ObjectManager::Interface override. + void ObjectAdded(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + VLOG(2) << "Remote GATT service added: " << object_path.value(); + FOR_EACH_OBSERVER(BluetoothGattServiceClient::Observer, observers_, + GattServiceAdded(object_path)); + } + + // dbus::ObjectManager::Interface override. + void ObjectRemoved(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + VLOG(2) << "Remote GATT service removed: " << object_path.value(); + FOR_EACH_OBSERVER(BluetoothGattServiceClient::Observer, observers_, + GattServiceRemoved(object_path)); + } + + protected: + // chromeos::DBusClient override. + void Init(dbus::Bus* bus) override { + object_manager_ = bus->GetObjectManager( + bluetooth_object_manager::kBluetoothObjectManagerServiceName, + dbus::ObjectPath( + bluetooth_object_manager::kBluetoothObjectManagerServicePath)); + object_manager_->RegisterInterface( + bluetooth_gatt_service::kBluetoothGattServiceInterface, this); + } + + private: + // Called by dbus::PropertySet when a property value is changed, either by + // result of a signal or response to a GetAll() or Get() call. Informs + // observers. + virtual void OnPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) { + VLOG(2) << "Remote GATT service property changed: " << object_path.value() + << ": " << property_name; + FOR_EACH_OBSERVER(BluetoothGattServiceClient::Observer, observers_, + GattServicePropertyChanged(object_path, property_name)); + } + + dbus::ObjectManager* object_manager_; + + // List of observers interested in event notifications from us. + base::ObserverList<BluetoothGattServiceClient::Observer> observers_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothGattServiceClientImpl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothGattServiceClientImpl); +}; + +BluetoothGattServiceClient::BluetoothGattServiceClient() {} + +BluetoothGattServiceClient::~BluetoothGattServiceClient() {} + +// static +BluetoothGattServiceClient* BluetoothGattServiceClient::Create() { + return new BluetoothGattServiceClientImpl(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_gatt_service_client.h b/chromeos/dbus/bluetooth_gatt_service_client.h new file mode 100644 index 0000000..9c96bb8 --- /dev/null +++ b/chromeos/dbus/bluetooth_gatt_service_client.h @@ -0,0 +1,93 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_GATT_SERVICE_CLIENT_H_ +#define CHROMEOS_DBUS_BLUETOOTH_GATT_SERVICE_CLIENT_H_ + +#include <string> +#include <vector> + +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_client.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +// BluetoothGattServiceClient is used to communicate with remote GATT service +// objects exposed by the Bluetooth daemon. +class CHROMEOS_EXPORT BluetoothGattServiceClient : public DBusClient { + public: + // Structure of properties associated with GATT services. + struct Properties : public dbus::PropertySet { + // The 128-bit service UUID. [read-only] + dbus::Property<std::string> uuid; + + // Object path of the Bluetooth device that the GATT service belongs to. + dbus::Property<dbus::ObjectPath> device; + + // Whether or not this service is a primary service. + dbus::Property<bool> primary; + + // Array of object paths representing the characteristics of this service. + // [read-only] + dbus::Property<std::vector<dbus::ObjectPath>> characteristics; + + // Array of object paths representing the included services of this service. + // [read-only] + dbus::Property<std::vector<dbus::ObjectPath>> includes; + + Properties(dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const PropertyChangedCallback& callback); + ~Properties() override; + }; + + // Interface for observing changes from a remote GATT service. + class Observer { + public: + virtual ~Observer() {} + + // Called when the GATT service with object path |object_path| is added to + // the system. + virtual void GattServiceAdded(const dbus::ObjectPath& object_path) {} + + // Called when the GATT service with object path |object_path| is removed + // from the system. + virtual void GattServiceRemoved(const dbus::ObjectPath& object_path) {} + + // Called when the GATT service with object path |object_path| has a change + // in the value of the property named |property_name|. + virtual void GattServicePropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) {} + }; + + ~BluetoothGattServiceClient() override; + + // Adds and removes observers for events on all remote GATT services. Check + // the |object_path| parameter of observer methods to determine which GATT + // service is issuing the event. + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + + // Returns the list of GATT service object paths known to the system. + virtual std::vector<dbus::ObjectPath> GetServices() = 0; + + // Obtain the properties for the GATT service with object path |object_path|. + // Values should be copied if needed. + virtual Properties* GetProperties(const dbus::ObjectPath& object_path) = 0; + + // Creates the instance. + static BluetoothGattServiceClient* Create(); + + protected: + BluetoothGattServiceClient(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothGattServiceClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_GATT_SERVICE_CLIENT_H_ diff --git a/chromeos/dbus/bluetooth_gatt_service_service_provider.cc b/chromeos/dbus/bluetooth_gatt_service_service_provider.cc new file mode 100644 index 0000000..9de86d4 --- /dev/null +++ b/chromeos/dbus/bluetooth_gatt_service_service_provider.cc @@ -0,0 +1,272 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_gatt_service_service_provider.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/platform_thread.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_gatt_service_service_provider.h" +#include "dbus/exported_object.h" +#include "dbus/message.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { +namespace { +const char kErrorInvalidArgs[] = "org.freedesktop.DBus.Error.InvalidArgs"; +const char kErrorPropertyReadOnly[] = + "org.freedesktop.DBus.Error.PropertyReadOnly"; +} // namespace + +// The BluetoothGattServiceServiceProvider implementation used in production. +class BluetoothGattServiceServiceProviderImpl + : public BluetoothGattServiceServiceProvider { + public: + BluetoothGattServiceServiceProviderImpl( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + const std::string& uuid, + const std::vector<dbus::ObjectPath>& includes) + : origin_thread_id_(base::PlatformThread::CurrentId()), + uuid_(uuid), + includes_(includes), + bus_(bus), + object_path_(object_path), + weak_ptr_factory_(this) { + VLOG(1) << "Creating Bluetooth GATT service: " << object_path_.value() + << " UUID: " << uuid; + DCHECK(!uuid_.empty()); + DCHECK(object_path_.IsValid()); + DCHECK(bus_); + + exported_object_ = bus_->GetExportedObject(object_path_); + + exported_object_->ExportMethod( + dbus::kDBusPropertiesInterface, dbus::kDBusPropertiesGet, + base::Bind(&BluetoothGattServiceServiceProviderImpl::Get, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothGattServiceServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + dbus::kDBusPropertiesInterface, dbus::kDBusPropertiesSet, + base::Bind(&BluetoothGattServiceServiceProviderImpl::Set, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothGattServiceServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + dbus::kDBusPropertiesInterface, dbus::kDBusPropertiesGetAll, + base::Bind(&BluetoothGattServiceServiceProviderImpl::GetAll, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothGattServiceServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + } + + ~BluetoothGattServiceServiceProviderImpl() override { + VLOG(1) << "Cleaning up Bluetooth GATT service: " << object_path_.value(); + bus_->UnregisterExportedObject(object_path_); + } + + private: + // Returns true if the current thread is on the origin thread. + bool OnOriginThread() { + return base::PlatformThread::CurrentId() == origin_thread_id_; + } + + // Called by dbus:: when the Bluetooth daemon fetches a single property of + // the service. + void Get(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "BluetoothGattServiceServiceProvider::Get: " + << object_path_.value(); + DCHECK(OnOriginThread()); + + dbus::MessageReader reader(method_call); + + std::string interface_name; + std::string property_name; + if (!reader.PopString(&interface_name) || + !reader.PopString(&property_name) || reader.HasMoreData()) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall(method_call, kErrorInvalidArgs, + "Expected 'ss'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Only the GATT service interface is allowed. + if (interface_name != + bluetooth_gatt_service::kBluetoothGattServiceInterface) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "No such interface: '" + interface_name + "'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Return error if |property_name| is unknown. + if (property_name != bluetooth_gatt_service::kUUIDProperty && + property_name != bluetooth_gatt_service::kIncludesProperty) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "No such property: '" + property_name + "'."); + response_sender.Run(error_response.Pass()); + return; + } + + scoped_ptr<dbus::Response> response = + dbus::Response::FromMethodCall(method_call); + dbus::MessageWriter writer(response.get()); + dbus::MessageWriter variant_writer(NULL); + + if (property_name == bluetooth_gatt_service::kUUIDProperty) { + writer.OpenVariant("s", &variant_writer); + variant_writer.AppendString(uuid_); + writer.CloseContainer(&variant_writer); + } else { + writer.OpenVariant("ao", &variant_writer); + variant_writer.AppendArrayOfObjectPaths(includes_); + writer.CloseContainer(&variant_writer); + } + + response_sender.Run(response.Pass()); + } + + // Called by dbus:: when the Bluetooth daemon sets a single property of the + // service. + void Set(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "BluetoothGattServiceServiceProvider::Set: " + << object_path_.value(); + DCHECK(OnOriginThread()); + + // All of the properties on this interface are read-only, so just return + // error. + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall(method_call, kErrorPropertyReadOnly, + "All properties are read-only."); + response_sender.Run(error_response.Pass()); + } + + // Called by dbus:: when the Bluetooth daemon fetches all properties of the + // service. + void GetAll(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "BluetoothGattServiceServiceProvider::GetAll: " + << object_path_.value(); + DCHECK(OnOriginThread()); + + dbus::MessageReader reader(method_call); + + std::string interface_name; + if (!reader.PopString(&interface_name) || reader.HasMoreData()) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall(method_call, kErrorInvalidArgs, + "Expected 's'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Only the GATT service interface is allowed. + if (interface_name != + bluetooth_gatt_service::kBluetoothGattServiceInterface) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "No such interface: '" + interface_name + "'."); + response_sender.Run(error_response.Pass()); + return; + } + + scoped_ptr<dbus::Response> response = + dbus::Response::FromMethodCall(method_call); + dbus::MessageWriter writer(response.get()); + dbus::MessageWriter array_writer(NULL); + dbus::MessageWriter dict_entry_writer(NULL); + dbus::MessageWriter variant_writer(NULL); + + writer.OpenArray("{sv}", &array_writer); + + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString(bluetooth_gatt_service::kUUIDProperty); + dict_entry_writer.AppendVariantOfString(uuid_); + array_writer.CloseContainer(&dict_entry_writer); + + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString(bluetooth_gatt_service::kIncludesProperty); + dict_entry_writer.OpenVariant("ao", &variant_writer); + variant_writer.AppendArrayOfObjectPaths(includes_); + dict_entry_writer.CloseContainer(&variant_writer); + array_writer.CloseContainer(&dict_entry_writer); + + writer.CloseContainer(&array_writer); + + response_sender.Run(response.Pass()); + } + + // Called by dbus:: when a method is exported. + void OnExported(const std::string& interface_name, + const std::string& method_name, + bool success) { + LOG_IF(WARNING, !success) << "Failed to export " << interface_name << "." + << method_name; + } + + // Origin thread (i.e. the UI thread in production). + base::PlatformThreadId origin_thread_id_; + + // 128-bit service UUID of this object. + std::string uuid_; + + // List of object paths that represent other exported GATT services that are + // included from this service. + std::vector<dbus::ObjectPath> includes_; + + // D-Bus bus object is exported on, not owned by this object and must + // outlive it. + dbus::Bus* bus_; + + // D-Bus object path of object we are exporting, kept so we can unregister + // again in our destructor. + dbus::ObjectPath object_path_; + + // D-Bus object we are exporting, owned by this object. + scoped_refptr<dbus::ExportedObject> exported_object_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothGattServiceServiceProviderImpl> + weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothGattServiceServiceProviderImpl); +}; + +BluetoothGattServiceServiceProvider::BluetoothGattServiceServiceProvider() {} + +BluetoothGattServiceServiceProvider::~BluetoothGattServiceServiceProvider() {} + +// static +BluetoothGattServiceServiceProvider* +BluetoothGattServiceServiceProvider::Create( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + const std::string& uuid, + const std::vector<dbus::ObjectPath>& includes) { + if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH)) { + return new BluetoothGattServiceServiceProviderImpl(bus, object_path, uuid, + includes); + } + return new FakeBluetoothGattServiceServiceProvider(object_path, uuid, + includes); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_gatt_service_service_provider.h b/chromeos/dbus/bluetooth_gatt_service_service_provider.h new file mode 100644 index 0000000..258c4b4 --- /dev/null +++ b/chromeos/dbus/bluetooth_gatt_service_service_provider.h @@ -0,0 +1,52 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_GATT_SERVICE_SERVICE_PROVIDER_H_ +#define CHROMEOS_DBUS_BLUETOOTH_GATT_SERVICE_SERVICE_PROVIDER_H_ + +#include <string> +#include <vector> + +#include "chromeos/chromeos_export.h" +#include "dbus/bus.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// BluetoothGattServiceServiceProvider is used to provide a D-Bus object that +// the Bluetooth daemon can communicate with to register GATT service +// hierarchies. +// +// Instantiate with a chosen D-Bus object path (that conforms to the BlueZ GATT +// service specification), service UUID, and the list of included services, and +// pass the D-Bus object path as the |service_path| argument to the +// chromeos::BluetoothGattManagerClient::RegisterService method. Make sure to +// create characteristic and descriptor objects using the appropriate service +// providers before registering a GATT service with the Bluetooth daemon. +class CHROMEOS_EXPORT BluetoothGattServiceServiceProvider { + public: + virtual ~BluetoothGattServiceServiceProvider(); + + // Creates the instance where |bus| is the D-Bus bus connection to export the + // object onto, |object_path| is the object path that it should have, |uuid| + // is the 128-bit GATT service UUID, and |includes| are a list of object paths + // belonging to other exported GATT services that are included by the GATT + // service being created. Make sure that all included services have been + // exported before registering a GATT services with the GATT manager. + static BluetoothGattServiceServiceProvider* Create( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + const std::string& uuid, + const std::vector<dbus::ObjectPath>& includes); + + protected: + BluetoothGattServiceServiceProvider(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothGattServiceServiceProvider); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_GATT_SERVICE_SERVICE_PROVIDER_H_ diff --git a/chromeos/dbus/bluetooth_input_client.cc b/chromeos/dbus/bluetooth_input_client.cc new file mode 100644 index 0000000..e8b1c8d --- /dev/null +++ b/chromeos/dbus/bluetooth_input_client.cc @@ -0,0 +1,128 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_input_client.h" + +#include <map> + +#include "base/logging.h" +#include "base/stl_util.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_manager.h" +#include "dbus/object_proxy.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +BluetoothInputClient::Properties::Properties( + dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const PropertyChangedCallback& callback) + : dbus::PropertySet(object_proxy, interface_name, callback) { + RegisterProperty(bluetooth_input::kReconnectModeProperty, &reconnect_mode); +} + +BluetoothInputClient::Properties::~Properties() {} + +// The BluetoothInputClient implementation used in production. +class BluetoothInputClientImpl : public BluetoothInputClient, + public dbus::ObjectManager::Interface { + public: + BluetoothInputClientImpl() : object_manager_(NULL), weak_ptr_factory_(this) {} + + ~BluetoothInputClientImpl() override { + object_manager_->UnregisterInterface( + bluetooth_input::kBluetoothInputInterface); + } + + // BluetoothInputClient override. + void AddObserver(BluetoothInputClient::Observer* observer) override { + DCHECK(observer); + observers_.AddObserver(observer); + } + + // BluetoothInputClient override. + void RemoveObserver(BluetoothInputClient::Observer* observer) override { + DCHECK(observer); + observers_.RemoveObserver(observer); + } + + // dbus::ObjectManager::Interface override. + dbus::PropertySet* CreateProperties( + dbus::ObjectProxy* object_proxy, + const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + Properties* properties = + new Properties(object_proxy, interface_name, + base::Bind(&BluetoothInputClientImpl::OnPropertyChanged, + weak_ptr_factory_.GetWeakPtr(), object_path)); + return static_cast<dbus::PropertySet*>(properties); + } + + // BluetoothInputClient override. + Properties* GetProperties(const dbus::ObjectPath& object_path) override { + return static_cast<Properties*>(object_manager_->GetProperties( + object_path, bluetooth_input::kBluetoothInputInterface)); + } + + protected: + void Init(dbus::Bus* bus) override { + object_manager_ = bus->GetObjectManager( + bluetooth_object_manager::kBluetoothObjectManagerServiceName, + dbus::ObjectPath( + bluetooth_object_manager::kBluetoothObjectManagerServicePath)); + object_manager_->RegisterInterface( + bluetooth_input::kBluetoothInputInterface, this); + } + + private: + // Called by dbus::ObjectManager when an object with the input interface + // is created. Informs observers. + void ObjectAdded(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + FOR_EACH_OBSERVER(BluetoothInputClient::Observer, observers_, + InputAdded(object_path)); + } + + // Called by dbus::ObjectManager when an object with the input interface + // is removed. Informs observers. + void ObjectRemoved(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + FOR_EACH_OBSERVER(BluetoothInputClient::Observer, observers_, + InputRemoved(object_path)); + } + + // Called by BluetoothPropertySet when a property value is changed, + // either by result of a signal or response to a GetAll() or Get() + // call. Informs observers. + void OnPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) { + FOR_EACH_OBSERVER(BluetoothInputClient::Observer, observers_, + InputPropertyChanged(object_path, property_name)); + } + + dbus::ObjectManager* object_manager_; + + // List of observers interested in event notifications from us. + base::ObserverList<BluetoothInputClient::Observer> observers_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothInputClientImpl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothInputClientImpl); +}; + +BluetoothInputClient::BluetoothInputClient() {} + +BluetoothInputClient::~BluetoothInputClient() {} + +BluetoothInputClient* BluetoothInputClient::Create() { + return new BluetoothInputClientImpl(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_input_client.h b/chromeos/dbus/bluetooth_input_client.h new file mode 100644 index 0000000..02abf99 --- /dev/null +++ b/chromeos/dbus/bluetooth_input_client.h @@ -0,0 +1,81 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_INPUT_CLIENT_H_ +#define CHROMEOS_DBUS_BLUETOOTH_INPUT_CLIENT_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/observer_list.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_client.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +// BluetoothInputClient is used to communicate with objects representing +// Bluetooth Input (HID) devices. +class CHROMEOS_EXPORT BluetoothInputClient : public DBusClient { + public: + // Structure of properties associated with bluetooth input devices. + struct Properties : public dbus::PropertySet { + // The Bluetooth input device reconnect mode. Read-only. + dbus::Property<std::string> reconnect_mode; + + Properties(dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const PropertyChangedCallback& callback); + ~Properties() override; + }; + + // Interface for observing changes from a remote bluetooth input device. + class Observer { + public: + virtual ~Observer() {} + + // Called when the remote device with object path |object_path| implementing + // the Input interface is added to the set of known devices or an already + // known device implements the Input interface. + virtual void InputAdded(const dbus::ObjectPath& object_path) {} + + // Called when the remote device with object path |object_path| is removed + // from the set of known devices or does not implement the Input interface + // anymore. + virtual void InputRemoved(const dbus::ObjectPath& object_path) {} + + // Called when the device with object path |object_path| has a + // change in value of the property named |property_name| of its Input + // interface. + virtual void InputPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) {} + }; + + ~BluetoothInputClient() override; + + // Adds and removes observers for events on all remote bluetooth input + // devices. Check the |object_path| parameter of observer methods to + // determine which device is issuing the event. + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + + // Obtain the properties for the device with object path |object_path|, + // any values should be copied if needed. + virtual Properties* GetProperties(const dbus::ObjectPath& object_path) = 0; + + // Creates the instance. + static BluetoothInputClient* Create(); + + protected: + BluetoothInputClient(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothInputClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_INPUT_CLIENT_H_ diff --git a/chromeos/dbus/bluetooth_le_advertisement_service_provider.cc b/chromeos/dbus/bluetooth_le_advertisement_service_provider.cc new file mode 100644 index 0000000..dfaeb5e --- /dev/null +++ b/chromeos/dbus/bluetooth_le_advertisement_service_provider.cc @@ -0,0 +1,426 @@ +// Copyright 2015 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/bluetooth_le_advertisement_service_provider.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/stl_util.h" +#include "base/threading/platform_thread.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_le_advertisement_service_provider.h" +#include "dbus/exported_object.h" +#include "dbus/message.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +namespace { +const char kErrorInvalidArgs[] = "org.freedesktop.DBus.Error.InvalidArgs"; +} // namespace + +// The BluetoothAdvertisementServiceProvider implementation used in production. +class BluetoothAdvertisementServiceProviderImpl + : public BluetoothLEAdvertisementServiceProvider { + public: + BluetoothAdvertisementServiceProviderImpl( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate, + AdvertisementType type, + scoped_ptr<UUIDList> service_uuids, + scoped_ptr<ManufacturerData> manufacturer_data, + scoped_ptr<UUIDList> solicit_uuids, + scoped_ptr<ServiceData> service_data) + : origin_thread_id_(base::PlatformThread::CurrentId()), + bus_(bus), + delegate_(delegate), + type_(type), + service_uuids_(service_uuids.Pass()), + manufacturer_data_(manufacturer_data.Pass()), + solicit_uuids_(solicit_uuids.Pass()), + service_data_(service_data.Pass()), + weak_ptr_factory_(this) { + DCHECK(bus); + DCHECK(delegate); + + VLOG(1) << "Creating Bluetooth Advertisement: " << object_path_.value(); + + object_path_ = object_path; + exported_object_ = bus_->GetExportedObject(object_path_); + + // Export Bluetooth Advertisement interface methods. + exported_object_->ExportMethod( + bluetooth_advertisement::kBluetoothAdvertisementInterface, + bluetooth_advertisement::kRelease, + base::Bind(&BluetoothAdvertisementServiceProviderImpl::Release, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothAdvertisementServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + // Export dbus property methods. + exported_object_->ExportMethod( + dbus::kDBusPropertiesInterface, dbus::kDBusPropertiesGet, + base::Bind(&BluetoothAdvertisementServiceProviderImpl::Get, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothAdvertisementServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + dbus::kDBusPropertiesInterface, dbus::kDBusPropertiesGetAll, + base::Bind(&BluetoothAdvertisementServiceProviderImpl::GetAll, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothAdvertisementServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + } + + ~BluetoothAdvertisementServiceProviderImpl() override { + VLOG(1) << "Cleaning up Bluetooth Advertisement: " << object_path_.value(); + + // Unregister the object path so we can reuse with a new agent. + bus_->UnregisterExportedObject(object_path_); + } + + private: + // Returns true if the current thread is on the origin thread. + bool OnOriginThread() { + return base::PlatformThread::CurrentId() == origin_thread_id_; + } + + // Called by dbus:: when this advertisement is unregistered from the Bluetooth + // daemon, generally by our request. + void Release(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + delegate_->Released(); + } + + // Called by dbus:: when the Bluetooth daemon fetches a single property of + // the descriptor. + void Get(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "BluetoothAdvertisementServiceProvider::Get: " + << object_path_.value(); + DCHECK(OnOriginThread()); + + dbus::MessageReader reader(method_call); + + std::string interface_name; + std::string property_name; + if (!reader.PopString(&interface_name) || + !reader.PopString(&property_name) || reader.HasMoreData()) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall(method_call, kErrorInvalidArgs, + "Expected 'ss'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Only the advertisement interface is supported. + if (interface_name != + bluetooth_advertisement::kBluetoothAdvertisementInterface) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "No such interface: '" + interface_name + "'."); + response_sender.Run(error_response.Pass()); + return; + } + + scoped_ptr<dbus::Response> response = + dbus::Response::FromMethodCall(method_call); + dbus::MessageWriter writer(response.get()); + dbus::MessageWriter variant_writer(NULL); + + if (property_name == bluetooth_advertisement::kTypeProperty) { + writer.OpenVariant("s", &variant_writer); + if (type_ == ADVERTISEMENT_TYPE_BROADCAST) { + variant_writer.AppendString("broadcast"); + } else { + variant_writer.AppendString("peripheral"); + } + } else if ((property_name == + bluetooth_advertisement::kServiceUUIDsProperty) && + service_uuids_) { + writer.OpenVariant("as", &variant_writer); + variant_writer.AppendArrayOfStrings(*service_uuids_); + } else if ((property_name == + bluetooth_advertisement::kSolicitUUIDsProperty) && + solicit_uuids_) { + writer.OpenVariant("as", &variant_writer); + variant_writer.AppendArrayOfStrings(*solicit_uuids_); + } else if ((property_name == + bluetooth_advertisement::kManufacturerDataProperty) && + manufacturer_data_) { + writer.OpenVariant("o", &variant_writer); + AppendManufacturerDataVariant(&variant_writer); + } else if ((property_name == + bluetooth_advertisement::kServiceDataProperty) && + service_data_) { + writer.OpenVariant("o", &variant_writer); + AppendServiceDataVariant(&variant_writer); + } else { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "No such property: '" + property_name + "'."); + response_sender.Run(error_response.Pass()); + } + + writer.CloseContainer(&variant_writer); + response_sender.Run(response.Pass()); + } + + // Called by dbus:: when the Bluetooth daemon fetches all properties of the + // descriptor. + void GetAll(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(2) << "BluetoothAdvertisementServiceProvider::GetAll: " + << object_path_.value(); + DCHECK(OnOriginThread()); + + dbus::MessageReader reader(method_call); + + std::string interface_name; + if (!reader.PopString(&interface_name) || reader.HasMoreData()) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall(method_call, kErrorInvalidArgs, + "Expected 's'."); + response_sender.Run(error_response.Pass()); + return; + } + + // Only the advertisement interface is supported. + if (interface_name != + bluetooth_advertisement::kBluetoothAdvertisementInterface) { + scoped_ptr<dbus::ErrorResponse> error_response = + dbus::ErrorResponse::FromMethodCall( + method_call, kErrorInvalidArgs, + "No such interface: '" + interface_name + "'."); + response_sender.Run(error_response.Pass()); + return; + } + + response_sender.Run(CreateGetAllResponse(method_call).Pass()); + } + + // Called by dbus:: when a method is exported. + void OnExported(const std::string& interface_name, + const std::string& method_name, + bool success) { + LOG_IF(WARNING, !success) << "Failed to export " << interface_name << "." + << method_name; + } + + // Helper for populating the DBus response with the advertisement data. + scoped_ptr<dbus::Response> CreateGetAllResponse( + dbus::MethodCall* method_call) { + VLOG(2) << "Descriptor value obtained from delegate. Responding to " + << "GetAll."; + + scoped_ptr<dbus::Response> response = + dbus::Response::FromMethodCall(method_call); + + dbus::MessageWriter writer(response.get()); + dbus::MessageWriter array_writer(NULL); + + writer.OpenArray("{sv}", &array_writer); + + AppendType(&array_writer); + AppendServiceUUIDs(&array_writer); + AppendManufacturerData(&array_writer); + AppendSolicitUUIDs(&array_writer); + AppendServiceData(&array_writer); + + writer.CloseContainer(&array_writer); + return response; + } + + // Called by the Delegate in response to a successful method call to get the + // descriptor value. + void OnGet(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender, + const std::vector<uint8>& value) { + VLOG(2) << "Returning descriptor value obtained from delegate."; + scoped_ptr<dbus::Response> response = + dbus::Response::FromMethodCall(method_call); + dbus::MessageWriter writer(response.get()); + dbus::MessageWriter variant_writer(NULL); + + writer.OpenVariant("ay", &variant_writer); + variant_writer.AppendArrayOfBytes(value.data(), value.size()); + writer.CloseContainer(&variant_writer); + + response_sender.Run(response.Pass()); + } + + void AppendArrayVariantOfStrings(dbus::MessageWriter* dict_writer, + const UUIDList& strings) { + dbus::MessageWriter strings_array_variant(nullptr); + dict_writer->OpenVariant("as", &strings_array_variant); + strings_array_variant.AppendArrayOfStrings(strings); + dict_writer->CloseContainer(&strings_array_variant); + } + + void AppendType(dbus::MessageWriter* array_writer) { + dbus::MessageWriter dict_entry_writer(NULL); + array_writer->OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString(bluetooth_advertisement::kTypeProperty); + if (type_ == ADVERTISEMENT_TYPE_BROADCAST) { + dict_entry_writer.AppendVariantOfString("broadcast"); + } else { + dict_entry_writer.AppendVariantOfString("peripheral"); + } + array_writer->CloseContainer(&dict_entry_writer); + } + + void AppendServiceUUIDs(dbus::MessageWriter* array_writer) { + if (!service_uuids_) + return; + dbus::MessageWriter dict_entry_writer(NULL); + array_writer->OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString( + bluetooth_advertisement::kServiceUUIDsProperty); + AppendArrayVariantOfStrings(&dict_entry_writer, *service_uuids_); + array_writer->CloseContainer(&dict_entry_writer); + } + + void AppendManufacturerData(dbus::MessageWriter* array_writer) { + if (!manufacturer_data_) + return; + dbus::MessageWriter dict_entry_writer(NULL); + array_writer->OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString( + bluetooth_advertisement::kManufacturerDataProperty); + dbus::MessageWriter variant_writer(NULL); + dict_entry_writer.OpenVariant("a{qay}", &variant_writer); + AppendManufacturerDataVariant(&variant_writer); + dict_entry_writer.CloseContainer(&variant_writer); + array_writer->CloseContainer(&dict_entry_writer); + } + + void AppendSolicitUUIDs(dbus::MessageWriter* array_writer) { + if (!solicit_uuids_) + return; + dbus::MessageWriter dict_entry_writer(NULL); + array_writer->OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString( + bluetooth_advertisement::kSolicitUUIDsProperty); + AppendArrayVariantOfStrings(&dict_entry_writer, *solicit_uuids_); + array_writer->CloseContainer(&dict_entry_writer); + } + + void AppendServiceData(dbus::MessageWriter* array_writer) { + if (!service_data_) + return; + dbus::MessageWriter dict_entry_writer(NULL); + array_writer->OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString( + bluetooth_advertisement::kServiceDataProperty); + dbus::MessageWriter variant_writer(NULL); + dict_entry_writer.OpenVariant("a{say}", &variant_writer); + AppendServiceDataVariant(&variant_writer); + dict_entry_writer.CloseContainer(&variant_writer); + array_writer->CloseContainer(&dict_entry_writer); + } + + void AppendManufacturerDataVariant(dbus::MessageWriter* writer) { + DCHECK(manufacturer_data_); + dbus::MessageWriter array_writer(NULL); + writer->OpenArray("{qay}", &array_writer); + for (const auto& m : *manufacturer_data_) { + dbus::MessageWriter entry_writer(NULL); + + array_writer.OpenDictEntry(&entry_writer); + + entry_writer.AppendUint32(m.first); + entry_writer.AppendArrayOfBytes(vector_as_array(&m.second), + m.second.size()); + + array_writer.CloseContainer(&entry_writer); + } + writer->CloseContainer(&array_writer); + } + + void AppendServiceDataVariant(dbus::MessageWriter* writer) { + DCHECK(service_data_); + dbus::MessageWriter array_writer(NULL); + writer->OpenArray("{say}", &array_writer); + for (const auto& m : *service_data_) { + dbus::MessageWriter entry_writer(NULL); + + array_writer.OpenDictEntry(&entry_writer); + + entry_writer.AppendString(m.first); + entry_writer.AppendArrayOfBytes(vector_as_array(&m.second), + m.second.size()); + + array_writer.CloseContainer(&entry_writer); + } + writer->CloseContainer(&array_writer); + } + + // Origin thread (i.e. the UI thread in production). + base::PlatformThreadId origin_thread_id_; + + // D-Bus bus object is exported on, not owned by this object and must + // outlive it. + dbus::Bus* bus_; + + // All incoming method calls are passed on to the Delegate and a callback + // passed to generate the reply. |delegate_| is generally the object that + // owns this one, and must outlive it. + Delegate* delegate_; + + // Advertisement data that needs to be provided to BlueZ when requested. + AdvertisementType type_; + scoped_ptr<UUIDList> service_uuids_; + scoped_ptr<ManufacturerData> manufacturer_data_; + scoped_ptr<UUIDList> solicit_uuids_; + scoped_ptr<ServiceData> service_data_; + + // D-Bus object we are exporting, owned by this object. + scoped_refptr<dbus::ExportedObject> exported_object_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothAdvertisementServiceProviderImpl> + weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothAdvertisementServiceProviderImpl); +}; + +BluetoothLEAdvertisementServiceProvider:: + BluetoothLEAdvertisementServiceProvider() {} + +BluetoothLEAdvertisementServiceProvider:: + ~BluetoothLEAdvertisementServiceProvider() {} + +// static +scoped_ptr<BluetoothLEAdvertisementServiceProvider> +BluetoothLEAdvertisementServiceProvider::Create( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate, + AdvertisementType type, + scoped_ptr<UUIDList> service_uuids, + scoped_ptr<ManufacturerData> manufacturer_data, + scoped_ptr<UUIDList> solicit_uuids, + scoped_ptr<ServiceData> service_data) { + if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH)) { + return make_scoped_ptr(new BluetoothAdvertisementServiceProviderImpl( + bus, object_path, delegate, type, service_uuids.Pass(), + manufacturer_data.Pass(), solicit_uuids.Pass(), service_data.Pass())); + } else { + return make_scoped_ptr( + new FakeBluetoothLEAdvertisementServiceProvider(object_path, delegate)); + } +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_le_advertisement_service_provider.h b/chromeos/dbus/bluetooth_le_advertisement_service_provider.h new file mode 100644 index 0000000..98622a2 --- /dev/null +++ b/chromeos/dbus/bluetooth_le_advertisement_service_provider.h @@ -0,0 +1,82 @@ +// Copyright 2015 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 CHROMEOS_DBUS_BLUETOOTH_LE_ADVERTISEMENT_SERVICE_PROVIDER_H_ +#define CHROMEOS_DBUS_BLUETOOTH_LE_ADVERTISEMENT_SERVICE_PROVIDER_H_ + +#include <stdint.h> + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "chromeos/chromeos_export.h" +#include "dbus/bus.h" +#include "dbus/file_descriptor.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// BluetoothAdvertisementServiceProvider is used to provide a D-Bus object that +// the Bluetooth daemon can communicate with to advertise data. +class CHROMEOS_EXPORT BluetoothLEAdvertisementServiceProvider { + public: + using UUIDList = std::vector<std::string>; + using ManufacturerData = std::map<uint16_t, std::vector<uint8_t>>; + using ServiceData = std::map<std::string, std::vector<uint8_t>>; + + // Type of advertisement. + enum AdvertisementType { + ADVERTISEMENT_TYPE_BROADCAST, + ADVERTISEMENT_TYPE_PERIPHERAL + }; + + // Interface for reacting to advertisement changes. + class Delegate { + public: + virtual ~Delegate() {} + + // This method will be called when the advertisement is unregistered from + // the Bluetooth daemon, generally at shutdown or if the adapter goes away. + // It may be used to perform cleanup tasks. This corresponds to the + // org.bluez.LEAdvertisement1.Release method and is renamed to avoid a + // conflict with base::Refcounted<T>. + virtual void Released() = 0; + }; + + virtual ~BluetoothLEAdvertisementServiceProvider(); + + const dbus::ObjectPath& object_path() { return object_path_; } + + // Creates the instance where |bus| is the D-Bus bus connection to export + // the object onto, |object_path| is the object path that it should have + // and |delegate| is the object to which all method calls will be passed + // and responses generated from. + static scoped_ptr<BluetoothLEAdvertisementServiceProvider> Create( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate, + AdvertisementType type, + scoped_ptr<UUIDList> service_uuids, + scoped_ptr<ManufacturerData> manufacturer_data, + scoped_ptr<UUIDList> solicit_uuids, + scoped_ptr<ServiceData> service_data); + + protected: + BluetoothLEAdvertisementServiceProvider(); + + // D-Bus object path of object we are exporting, kept so we can unregister + // again in our destructor. + dbus::ObjectPath object_path_; + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothLEAdvertisementServiceProvider); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_LE_ADVERTISEMENT_SERVICE_PROVIDER_H_ diff --git a/chromeos/dbus/bluetooth_le_advertising_manager_client.cc b/chromeos/dbus/bluetooth_le_advertising_manager_client.cc new file mode 100644 index 0000000..ca2c9e2 --- /dev/null +++ b/chromeos/dbus/bluetooth_le_advertising_manager_client.cc @@ -0,0 +1,187 @@ +// Copyright 2015 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/bluetooth_le_advertising_manager_client.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/observer_list.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_manager.h" +#include "dbus/object_proxy.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +const char BluetoothLEAdvertisingManagerClient::kNoResponseError[] = + "org.chromium.Error.NoResponse"; + +// The BluetoothAdvertisementManagerClient implementation used in production. +class BluetoothAdvertisementManagerClientImpl + : public BluetoothLEAdvertisingManagerClient, + public dbus::ObjectManager::Interface { + public: + BluetoothAdvertisementManagerClientImpl() + : object_manager_(NULL), weak_ptr_factory_(this) {} + + ~BluetoothAdvertisementManagerClientImpl() override { + if (object_manager_) { + object_manager_->UnregisterInterface( + bluetooth_advertising_manager::kBluetoothAdvertisingManagerInterface); + } + } + + // BluetoothAdapterClient override. + void AddObserver( + BluetoothLEAdvertisingManagerClient::Observer* observer) override { + DCHECK(observer); + observers_.AddObserver(observer); + } + + // BluetoothAdapterClient override. + void RemoveObserver( + BluetoothLEAdvertisingManagerClient::Observer* observer) override { + DCHECK(observer); + observers_.RemoveObserver(observer); + } + + // dbus::ObjectManager::Interface override. + dbus::PropertySet* CreateProperties( + dbus::ObjectProxy* object_proxy, + const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + return new dbus::PropertySet(object_proxy, interface_name, + dbus::PropertySet::PropertyChangedCallback()); + } + + // BluetoothAdvertisementManagerClient override. + void RegisterAdvertisement(const dbus::ObjectPath& manager_object_path, + const dbus::ObjectPath& advertisement_object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call( + bluetooth_advertising_manager::kBluetoothAdvertisingManagerInterface, + bluetooth_advertising_manager::kRegisterAdvertisement); + + dbus::MessageWriter writer(&method_call); + writer.AppendObjectPath(advertisement_object_path); + + // Empty dictionary for options. + dbus::MessageWriter array_writer(NULL); + writer.OpenArray("{sv}", &array_writer); + writer.CloseContainer(&array_writer); + + DCHECK(object_manager_); + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(manager_object_path); + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothAdvertisementManagerClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothAdvertisementManagerClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothAdvertisementManagerClient override. + void UnregisterAdvertisement( + const dbus::ObjectPath& manager_object_path, + const dbus::ObjectPath& advertisement_object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call( + bluetooth_advertising_manager::kBluetoothAdvertisingManagerInterface, + bluetooth_advertising_manager::kUnregisterAdvertisement); + + dbus::MessageWriter writer(&method_call); + writer.AppendObjectPath(advertisement_object_path); + + DCHECK(object_manager_); + dbus::ObjectProxy* object_proxy = + object_manager_->GetObjectProxy(manager_object_path); + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothAdvertisementManagerClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothAdvertisementManagerClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + protected: + void Init(dbus::Bus* bus) override { + DCHECK(bus); + object_manager_ = bus->GetObjectManager( + bluetooth_object_manager::kBluetoothObjectManagerServiceName, + dbus::ObjectPath( + bluetooth_object_manager::kBluetoothObjectManagerServicePath)); + object_manager_->RegisterInterface( + bluetooth_advertising_manager::kBluetoothAdvertisingManagerInterface, + this); + } + + private: + // Called by dbus::ObjectManager when an object with the advertising manager + // interface is created. Informs observers. + void ObjectAdded(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + FOR_EACH_OBSERVER(BluetoothLEAdvertisingManagerClient::Observer, observers_, + AdvertisingManagerAdded(object_path)); + } + + // Called by dbus::ObjectManager when an object with the advertising manager + // interface is removed. Informs observers. + void ObjectRemoved(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + FOR_EACH_OBSERVER(BluetoothLEAdvertisingManagerClient::Observer, observers_, + AdvertisingManagerRemoved(object_path)); + } + + // Called when a response for successful method call is received. + void OnSuccess(const base::Closure& callback, dbus::Response* response) { + DCHECK(response); + callback.Run(); + } + + // Called when a response for a failed method call is received. + void OnError(const ErrorCallback& error_callback, + dbus::ErrorResponse* response) { + // Error response has optional error message argument. + std::string error_name; + std::string error_message; + if (response) { + dbus::MessageReader reader(response); + error_name = response->GetErrorName(); + reader.PopString(&error_message); + } else { + error_name = kNoResponseError; + error_message = ""; + } + error_callback.Run(error_name, error_message); + } + + dbus::ObjectManager* object_manager_; + + // List of observers interested in event notifications from us. + base::ObserverList<BluetoothLEAdvertisingManagerClient::Observer> observers_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothAdvertisementManagerClientImpl> + weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothAdvertisementManagerClientImpl); +}; + +BluetoothLEAdvertisingManagerClient::BluetoothLEAdvertisingManagerClient() {} + +BluetoothLEAdvertisingManagerClient::~BluetoothLEAdvertisingManagerClient() {} + +BluetoothLEAdvertisingManagerClient* +BluetoothLEAdvertisingManagerClient::Create() { + return new BluetoothAdvertisementManagerClientImpl(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_le_advertising_manager_client.h b/chromeos/dbus/bluetooth_le_advertising_manager_client.h new file mode 100644 index 0000000..5160a9f --- /dev/null +++ b/chromeos/dbus/bluetooth_le_advertising_manager_client.h @@ -0,0 +1,83 @@ +// Copyright 2015 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 CHROMEOS_DBUS_BLUETOOTH_LE_ADVERTISING_MANAGER_CLIENT_H_ +#define CHROMEOS_DBUS_BLUETOOTH_LE_ADVERTISING_MANAGER_CLIENT_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_client.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// BluetoothAdvertisingManagerClient is used to communicate with the advertising +// manager object of the BlueZ daemon. +class CHROMEOS_EXPORT BluetoothLEAdvertisingManagerClient : public DBusClient { + public: + // Interface for observing changes to advertising managers. + class Observer { + public: + virtual ~Observer() {} + + // Called when an advertising manager with object path |object_path| is + // added to the system. + virtual void AdvertisingManagerAdded(const dbus::ObjectPath& object_path) {} + + // Called when an advertising manager with object path |object_path| is + // removed from the system. + virtual void AdvertisingManagerRemoved( + const dbus::ObjectPath& object_path) {} + }; + + ~BluetoothLEAdvertisingManagerClient() override; + + // Adds and removes observers for events which change the advertising + // managers on the system. + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + + // The ErrorCallback is used by advertising manager methods to indicate + // failure. It receives two arguments: the name of the error in |error_name| + // and an optional message in |error_message|. + using ErrorCallback = base::Callback<void(const std::string& error_name, + const std::string& error_message)>; + + // Registers an advertisement with the DBus object path + // |advertisement_object_path| with BlueZ's advertising manager. + virtual void RegisterAdvertisement( + const dbus::ObjectPath& manager_object_path, + const dbus::ObjectPath& advertisement_object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Unregisters an advertisement with the DBus object path + // |advertisement_object_path| with BlueZ's advertising manager. + virtual void UnregisterAdvertisement( + const dbus::ObjectPath& manager_object_path, + const dbus::ObjectPath& advertisement_object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Creates the instance. + static BluetoothLEAdvertisingManagerClient* Create(); + + // Constants used to indicate exceptional error conditions. + static const char kNoResponseError[]; + + protected: + BluetoothLEAdvertisingManagerClient(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothLEAdvertisingManagerClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_LE_ADVERTISING_MANAGER_CLIENT_H_ diff --git a/chromeos/dbus/bluetooth_media_client.cc b/chromeos/dbus/bluetooth_media_client.cc new file mode 100644 index 0000000..301f765 --- /dev/null +++ b/chromeos/dbus/bluetooth_media_client.cc @@ -0,0 +1,227 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_media_client.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/observer_list.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_manager.h" +#include "dbus/object_proxy.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace { + +// Since there is no property associated with Media objects, an empty callback +// is used. +void DoNothing(const std::string& property_name) {} + +// TODO(mcchou): Add these service constants into dbus/service_constants.h +// later. +const char kBluetoothMediaInterface[] = "org.bluez.Media1"; + +// Method names supported by Media Interface. +const char kRegisterEndpoint[] = "RegisterEndpoint"; +const char kUnregisterEndpoint[] = "UnregisterEndpoint"; + +// The set of properties which are used to register a media endpoint. +const char kUUIDEndpointProperty[] = "UUID"; +const char kCodecEndpointProperty[] = "Codec"; +const char kCapabilitiesEndpointProperty[] = "Capabilities"; + +} // namespace + +namespace chromeos { + +// static +const char BluetoothMediaClient::kNoResponseError[] = + "org.chromium.Error.NoResponse"; + +// static +const char BluetoothMediaClient::kBluetoothAudioSinkUUID[] = + "0000110b-0000-1000-8000-00805f9b34fb"; + +BluetoothMediaClient::EndpointProperties::EndpointProperties() : codec(0x00) {} + +BluetoothMediaClient::EndpointProperties::~EndpointProperties() {} + +class BluetoothMediaClientImpl : public BluetoothMediaClient, + dbus::ObjectManager::Interface { + public: + BluetoothMediaClientImpl() + : object_manager_(nullptr), weak_ptr_factory_(this) {} + + ~BluetoothMediaClientImpl() override { + object_manager_->UnregisterInterface(kBluetoothMediaInterface); + } + + // dbus::ObjectManager::Interface overrides. + + dbus::PropertySet* CreateProperties( + dbus::ObjectProxy* object_proxy, + const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + return new dbus::PropertySet(object_proxy, interface_name, + base::Bind(&DoNothing)); + } + + void ObjectAdded(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + VLOG(1) << "Remote Media added: " << object_path.value(); + FOR_EACH_OBSERVER(BluetoothMediaClient::Observer, observers_, + MediaAdded(object_path)); + } + + void ObjectRemoved(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + VLOG(1) << "Remote Media removed: " << object_path.value(); + FOR_EACH_OBSERVER(BluetoothMediaClient::Observer, observers_, + MediaRemoved(object_path)); + } + + // BluetoothMediaClient overrides. + + void AddObserver(BluetoothMediaClient::Observer* observer) override { + DCHECK(observer); + observers_.AddObserver(observer); + } + + void RemoveObserver(BluetoothMediaClient::Observer* observer) override { + DCHECK(observer); + observers_.RemoveObserver(observer); + } + + void RegisterEndpoint(const dbus::ObjectPath& object_path, + const dbus::ObjectPath& endpoint_path, + const EndpointProperties& properties, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + VLOG(1) << "RegisterEndpoint - endpoint: " << endpoint_path.value(); + + dbus::MethodCall method_call(kBluetoothMediaInterface, kRegisterEndpoint); + + dbus::MessageWriter writer(&method_call); + dbus::MessageWriter array_writer(nullptr); + dbus::MessageWriter dict_entry_writer(nullptr); + + // Send the path to the endpoint. + writer.AppendObjectPath(endpoint_path); + + writer.OpenArray("{sv}", &array_writer); + + // Send UUID. + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString(kUUIDEndpointProperty); + dict_entry_writer.AppendVariantOfString(properties.uuid); + array_writer.CloseContainer(&dict_entry_writer); + + // Send Codec. + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString(kCodecEndpointProperty); + dict_entry_writer.AppendVariantOfByte(properties.codec); + array_writer.CloseContainer(&dict_entry_writer); + + // Send Capabilities. + dbus::MessageWriter variant_writer(nullptr); + array_writer.OpenDictEntry(&dict_entry_writer); + dict_entry_writer.AppendString(kCapabilitiesEndpointProperty); + dict_entry_writer.OpenVariant("ay", &variant_writer); + variant_writer.AppendArrayOfBytes(properties.capabilities.data(), + properties.capabilities.size()); + dict_entry_writer.CloseContainer(&variant_writer); + array_writer.CloseContainer(&dict_entry_writer); + + writer.CloseContainer(&array_writer); + + // Get Object Proxy based on the service name and the service path and call + // RegisterEndpoint medthod. + scoped_refptr<dbus::ObjectProxy> object_proxy( + object_manager_->GetObjectProxy(object_path)); + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothMediaClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothMediaClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + void UnregisterEndpoint(const dbus::ObjectPath& object_path, + const dbus::ObjectPath& endpoint_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + VLOG(1) << "UnregisterEndpoint - endpoint: " << endpoint_path.value(); + + dbus::MethodCall method_call(kBluetoothMediaInterface, kUnregisterEndpoint); + + // Send the path to the endpoint. + dbus::MessageWriter writer(&method_call); + writer.AppendObjectPath(endpoint_path); + + // Get Object Proxy based on the service name and the service path and call + // RegisterEndpoint medthod. + scoped_refptr<dbus::ObjectProxy> object_proxy( + object_manager_->GetObjectProxy(object_path)); + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothMediaClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothMediaClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + protected: + void Init(dbus::Bus* bus) override { + DCHECK(bus); + object_manager_ = bus->GetObjectManager( + bluetooth_object_manager::kBluetoothObjectManagerServiceName, + dbus::ObjectPath( + bluetooth_object_manager::kBluetoothObjectManagerServicePath)); + object_manager_->RegisterInterface(kBluetoothMediaInterface, this); + } + + private: + // Called when a response for successful method call is received. + void OnSuccess(const base::Closure& callback, dbus::Response* response) { + DCHECK(response); + callback.Run(); + } + + // Called when a response for a failed method call is received. + void OnError(const ErrorCallback& error_callback, + dbus::ErrorResponse* response) { + // Error response has an optional error message argument. + std::string error_name; + std::string error_message; + if (response) { + dbus::MessageReader reader(response); + error_name = response->GetErrorName(); + reader.PopString(&error_message); + } else { + error_name = kNoResponseError; + } + error_callback.Run(error_name, error_message); + } + + dbus::ObjectManager* object_manager_; + + // List of observers interested in event notifications from us. + base::ObserverList<BluetoothMediaClient::Observer> observers_; + + base::WeakPtrFactory<BluetoothMediaClientImpl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothMediaClientImpl); +}; + +BluetoothMediaClient::BluetoothMediaClient() {} + +BluetoothMediaClient::~BluetoothMediaClient() {} + +BluetoothMediaClient* BluetoothMediaClient::Create() { + return new BluetoothMediaClientImpl(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_media_client.h b/chromeos/dbus/bluetooth_media_client.h new file mode 100644 index 0000000..1bb5547 --- /dev/null +++ b/chromeos/dbus/bluetooth_media_client.h @@ -0,0 +1,107 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_MEDIA_CLIENT_H_ +#define CHROMEOS_DBUS_BLUETOOTH_MEDIA_CLIENT_H_ + +#include <memory> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/values.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_client.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +// BluetoothMediaClient is used to communicate with the Media interface of a +// local Bluetooth adapter. +class CHROMEOS_EXPORT BluetoothMediaClient : public DBusClient { + public: + // Properties used to register a Media Endpoint. + struct CHROMEOS_EXPORT EndpointProperties { + EndpointProperties(); + ~EndpointProperties(); + + // UUID of the profile implemented by the endpoint. + std::string uuid; + + // Assigned codec value supported by the endpoint. The byte should match the + // codec specification indicated by the UUID. + // Since SBC codec is mandatory for A2DP, the default value of codec should + // be 0x00. + uint8_t codec; + + // Capabilities of the endpoints. The order of bytes should match the bit + // arrangement in the specification indicated by the UUID. + std::vector<uint8_t> capabilities; + }; + + class Observer { + public: + virtual ~Observer() {} + + // Called when the Media object with object path |object_path| is added to + // the system. + virtual void MediaAdded(const dbus::ObjectPath& object_path) {} + + // Called when the Media object with object path |object_path| is removed + // from the system. + virtual void MediaRemoved(const dbus::ObjectPath& object_path) {} + }; + + // Constants used to indicate exceptional error conditions. + static const char kNoResponseError[]; + + // The string representation for the 128-bit UUID for A2DP Sink. + static const char kBluetoothAudioSinkUUID[]; + + ~BluetoothMediaClient() override; + + // The ErrorCallback is used by media API methods to indicate failure. + // It receives two arguments: the name of the error in |error_name| and + // an optional message in |error_message|. + typedef base::Callback<void(const std::string& error_name, + const std::string& error_message)> ErrorCallback; + + // Adds and removes observers for events on all Media objects. Check the + // |object_path| parameter of observer methods to determine which Media object + // is issuing the event. + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + + // Registers a media endpoint to sender at the D-Bus object path + // |endpoint_path|. |properties| specifies profile UUID which the endpoint is + // for, Codec implemented by the endpoint and the Capabilities of the + // endpoint. + virtual void RegisterEndpoint(const dbus::ObjectPath& object_path, + const dbus::ObjectPath& endpoint_path, + const EndpointProperties& properties, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Unregisters the media endpoint with the D-Bus object path |endpoint_path|. + virtual void UnregisterEndpoint(const dbus::ObjectPath& object_path, + const dbus::ObjectPath& endpoint_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // TODO(mcchou): The RegisterPlayer and UnregisterPlayer methods are not + // included, since they are not used. These two methods may be added later. + + static BluetoothMediaClient* Create(); + + protected: + BluetoothMediaClient(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothMediaClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_MEDIA_CLIENT_H_ diff --git a/chromeos/dbus/bluetooth_media_endpoint_service_provider.cc b/chromeos/dbus/bluetooth_media_endpoint_service_provider.cc new file mode 100644 index 0000000..9dd7720 --- /dev/null +++ b/chromeos/dbus/bluetooth_media_endpoint_service_provider.cc @@ -0,0 +1,313 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_media_endpoint_service_provider.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/platform_thread.h" +#include "chromeos/dbus/bluetooth_media_transport_client.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_media_endpoint_service_provider.h" +#include "dbus/exported_object.h" + +namespace { + +// TODO(mcchou): Move these constants to dbus/service_constants.h. +// Bluetooth Media Endpoint service identifier. +const char kBluetoothMediaEndpointInterface[] = "org.bluez.MediaEndpoint1"; + +// Method names in Bluetooth Media Endpoint interface. +const char kSetConfiguration[] = "SetConfiguration"; +const char kSelectConfiguration[] = "SelectConfiguration"; +const char kClearConfiguration[] = "ClearConfiguration"; +const char kRelease[] = "Release"; + +const uint8_t kInvalidCodec = 0xff; +const char kInvalidState[] = "unknown"; + +} // namespace + +namespace chromeos { + +// The BluetoothMediaEndopintServiceProvider implementation used in production. +class CHROMEOS_EXPORT BluetoothMediaEndpointServiceProviderImpl + : public BluetoothMediaEndpointServiceProvider { + public: + BluetoothMediaEndpointServiceProviderImpl(dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate) + : origin_thread_id_(base::PlatformThread::CurrentId()), + bus_(bus), + delegate_(delegate), + object_path_(object_path), + weak_ptr_factory_(this) { + VLOG(1) << "Creating Bluetooth Media Endpoint: " << object_path_.value(); + DCHECK(bus_); + DCHECK(delegate_); + DCHECK(object_path_.IsValid()); + + exported_object_ = bus_->GetExportedObject(object_path_); + + exported_object_->ExportMethod( + kBluetoothMediaEndpointInterface, kSetConfiguration, + base::Bind(&BluetoothMediaEndpointServiceProviderImpl::SetConfiguration, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothMediaEndpointServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + kBluetoothMediaEndpointInterface, kSelectConfiguration, + base::Bind( + &BluetoothMediaEndpointServiceProviderImpl::SelectConfiguration, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothMediaEndpointServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + kBluetoothMediaEndpointInterface, kClearConfiguration, + base::Bind( + &BluetoothMediaEndpointServiceProviderImpl::ClearConfiguration, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothMediaEndpointServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + kBluetoothMediaEndpointInterface, kRelease, + base::Bind(&BluetoothMediaEndpointServiceProviderImpl::Release, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothMediaEndpointServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + } + + ~BluetoothMediaEndpointServiceProviderImpl() override { + VLOG(1) << "Cleaning up Bluetooth Media Endpoint: " << object_path_.value(); + + bus_->UnregisterExportedObject(object_path_); + } + + private: + // Returns true if the current thread is on the origin thread, false + // otherwise. + bool OnOriginThread() const { + return base::PlatformThread::CurrentId() == origin_thread_id_; + } + + // Called by dbus:: when a method is exported. + void OnExported(const std::string& interface_name, + const std::string& method_name, + bool success) { + LOG_IF(ERROR, !success) << "Failed to export " << interface_name << "." + << method_name; + } + + // Called by dbus:: when the remote device connects to the Media Endpoint. + void SetConfiguration(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(1) << "SetConfiguration"; + + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + dbus::MessageReader reader(method_call); + dbus::ObjectPath transport_path; + dbus::MessageReader property_reader(method_call); + if (!reader.PopObjectPath(&transport_path) || + !reader.PopArray(&property_reader)) { + LOG(ERROR) << "SetConfiguration called with incorrect parameters: " + << method_call->ToString(); + return; + } + + // Parses |properties| and passes the property set as a + // Delegate::TransportProperties structure to |delegate_|. + Delegate::TransportProperties properties; + while (property_reader.HasMoreData()) { + dbus::MessageReader dict_entry_reader(nullptr); + std::string key; + if (!property_reader.PopDictEntry(&dict_entry_reader) || + !dict_entry_reader.PopString(&key)) { + LOG(ERROR) << "SetConfiguration called with incorrect parameters: " + << method_call->ToString(); + } else if (key == BluetoothMediaTransportClient::kDeviceProperty) { + dict_entry_reader.PopVariantOfObjectPath(&properties.device); + } else if (key == BluetoothMediaTransportClient::kUUIDProperty) { + dict_entry_reader.PopVariantOfString(&properties.uuid); + } else if (key == BluetoothMediaTransportClient::kCodecProperty) { + dict_entry_reader.PopVariantOfByte(&properties.codec); + } else if (key == BluetoothMediaTransportClient::kConfigurationProperty) { + dbus::MessageReader variant_reader(nullptr); + const uint8_t* bytes = nullptr; + size_t length = 0; + dict_entry_reader.PopVariant(&variant_reader); + variant_reader.PopArrayOfBytes(&bytes, &length); + properties.configuration.assign(bytes, bytes + length); + } else if (key == BluetoothMediaTransportClient::kStateProperty) { + dict_entry_reader.PopVariantOfString(&properties.state); + } else if (key == BluetoothMediaTransportClient::kDelayProperty) { + properties.delay.reset(new uint16_t()); + dict_entry_reader.PopVariantOfUint16(properties.delay.get()); + } else if (key == BluetoothMediaTransportClient::kVolumeProperty) { + properties.volume.reset(new uint16_t()); + dict_entry_reader.PopVariantOfUint16(properties.volume.get()); + } + } + + if (properties.codec != kInvalidCodec && + properties.state != kInvalidState) { + delegate_->SetConfiguration(transport_path, properties); + } else { + LOG(ERROR) << "SetConfiguration called with incorrect parameters: " + << method_call->ToString(); + } + + response_sender.Run(dbus::Response::FromMethodCall(method_call)); + } + + // Called by dbus:: when the remote device receives the configuration for + // media transport. + void SelectConfiguration( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(1) << "SelectConfiguration"; + + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + dbus::MessageReader reader(method_call); + const uint8_t* capabilities = nullptr; + size_t length = 0; + if (!reader.PopArrayOfBytes(&capabilities, &length)) { + LOG(ERROR) << "SelectConfiguration called with incorrect parameters: " + << method_call->ToString(); + return; + } + + std::vector<uint8_t> configuration(capabilities, capabilities + length); + + // |delegate_| generates the response to |SelectConfiguration| and sends it + // back via |callback|. + Delegate::SelectConfigurationCallback callback = base::Bind( + &BluetoothMediaEndpointServiceProviderImpl::OnConfiguration, + weak_ptr_factory_.GetWeakPtr(), method_call, response_sender); + + delegate_->SelectConfiguration(configuration, callback); + } + + // Called by dbus:: when the remote device is about to close the connection. + void ClearConfiguration( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(1) << "ClearConfiguration"; + + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + dbus::MessageReader reader(method_call); + dbus::ObjectPath transport_path; + if (!reader.PopObjectPath(&transport_path)) { + LOG(ERROR) << "ClearConfiguration called with incorrect parameters: " + << method_call->ToString(); + return; + } + + delegate_->ClearConfiguration(transport_path); + + response_sender.Run(dbus::Response::FromMethodCall(method_call)); + } + + // Called by Bluetooth daemon to do the clean up after unregistering the Media + // Endpoint. + void Release(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + VLOG(1) << "Release"; + + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + delegate_->Released(); + + response_sender.Run(dbus::Response::FromMethodCall(method_call)); + } + + // Called by Delegate to response to a method requiring transport + // configuration. + void OnConfiguration(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender, + const std::vector<uint8_t>& configuration) { + VLOG(1) << "OnConfiguration"; + + DCHECK(OnOriginThread()); + + // Generates the response to the method call. + scoped_ptr<dbus::Response> response( + dbus::Response::FromMethodCall(method_call)); + dbus::MessageWriter writer(response.get()); + if (configuration.empty()) { + LOG(ERROR) << "OnConfiguration called with empty configuration."; + writer.AppendArrayOfBytes(nullptr, 0); + } else { + writer.AppendArrayOfBytes(&configuration[0], configuration.size()); + } + response_sender.Run(response.Pass()); + } + + // Origin thread (i.e. the UI thread in production). + base::PlatformThreadId origin_thread_id_; + + // D-Bus Bus object is exported on. + dbus::Bus* bus_; + + // All incoming method calls are passed on to |delegate_|. |callback| passed + // to |delegate+| will generate the response for those methods whose returns + // are non-void. + Delegate* delegate_; + + // D-Bus object path of the object we are exporting, kept so we can unregister + // again in you destructor. + dbus::ObjectPath object_path_; + + // D-Bus object we are exporting, owned by this object. + scoped_refptr<dbus::ExportedObject> exported_object_; + + // Weak pointer factory for generating 'this' printers that might live longer + // than we do. + // Note This should remain the last member so it'll be destroyed and + // invalidate it's weak pointers before any other members are destroyed. + base::WeakPtrFactory<BluetoothMediaEndpointServiceProviderImpl> + weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothMediaEndpointServiceProviderImpl); +}; + +BluetoothMediaEndpointServiceProvider::Delegate::TransportProperties:: + TransportProperties() + : codec(kInvalidCodec), state(kInvalidState) {} + +BluetoothMediaEndpointServiceProvider::Delegate::TransportProperties:: + ~TransportProperties() {} + +BluetoothMediaEndpointServiceProvider::BluetoothMediaEndpointServiceProvider() { +} + +BluetoothMediaEndpointServiceProvider:: + ~BluetoothMediaEndpointServiceProvider() {} + +BluetoothMediaEndpointServiceProvider* +BluetoothMediaEndpointServiceProvider::Create( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate) { + // Returns a real implementation. + if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH)) { + return new BluetoothMediaEndpointServiceProviderImpl(bus, object_path, + delegate); + } + // Returns a fake implementation. + return new FakeBluetoothMediaEndpointServiceProvider(object_path, delegate); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_media_endpoint_service_provider.h b/chromeos/dbus/bluetooth_media_endpoint_service_provider.h new file mode 100644 index 0000000..fffaa53 --- /dev/null +++ b/chromeos/dbus/bluetooth_media_endpoint_service_provider.h @@ -0,0 +1,133 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_MEDIA_ENDPOINT_SERVICE_PROVIDER_H_ +#define CHROMEOS_DBUS_BLUETOOTH_MEDIA_ENDPOINT_SERVICE_PROVIDER_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "chromeos/chromeos_export.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// BluetoothMediaEndpointServiceProvider is used to provide a D-Bus object that +// the Bluetooth daemon can commuicate with to serve as a media source/sink. +// +// Instantiate with a chosen D-Bus object path and a delegate object, and pass +// the D-Bus object path as |endpoint_path| argument to the +// chromeos::BluetoothMediaClient::RegisterEndoint() method. +// +// After initiating a connection between an audio source and an audio sink, the +// Bluetooth daemon will make calls to this endpoint object and they will be +// passed to user's Delegate object for handling. For SelectConfiguration method +// the response is returned using the SelectConfiguration callback. +class CHROMEOS_EXPORT BluetoothMediaEndpointServiceProvider { + public: + // Delegate is the interface for reacting to endpoint requests. User + // applications will implement this interface to handle either A2DP Sink or + // Source. + class Delegate { + public: + // Transport-specific properties. + struct CHROMEOS_EXPORT TransportProperties { + TransportProperties(); + ~TransportProperties(); + + // The path to the device object which the transport is connected to. + dbus::ObjectPath device; + + // The UUID of the profile which the transport is for. + std::string uuid; + + // The Codec value agreed by the remote device and used by the media + // transport. + uint8_t codec; + + // The configuration used by the media transport. + std::vector<uint8_t> configuration; + + // The state of the transport. The values can be one of the following: + // "idle": not streaming + // "pending": streaming but not acquired + // "active": streaming and acquired + std::string state; + + // The unit of transport is in 1/10 millisecond. Optional. + scoped_ptr<uint16_t> delay; + + // The volume level of the transport. Optional. + scoped_ptr<uint16_t> volume; + + private: + DISALLOW_COPY_AND_ASSIGN(TransportProperties); + }; + + virtual ~Delegate() {} + + // SelectConfigurationCallback is used for the SelectConfiguration() method, + // it should be called with two arguements, the |configuration| which is + // agreed by the application and the |length| of |configuration|. + typedef base::Callback<void(const std::vector<uint8_t>&)> + SelectConfigurationCallback; + + // This method will be called after an Audio Source receives the agreed + // capabilities from the Audio Sink to set the configuration for the + // media transport object. |transport_path| is the path to the + // MediaTransport object, and |properties| are the properties for that + // MediaTransport object. + virtual void SetConfiguration(const dbus::ObjectPath& transport_path, + const TransportProperties& properties) = 0; + + // This method will be called when an Audio Source connects to an Audio Sink + // and asks it to decide the configuration to be used during the oncoming + // streaming. Audio Sources provide |capabilities| as a reference, where + // a user application can use these |capabilities| to figure out + // a well-matched configuration and return it to the Audio Source via + // |callback|. + virtual void SelectConfiguration( + const std::vector<uint8_t>& capabilities, + const SelectConfigurationCallback& callback) = 0; + + // This method will be called when an Audio Source disconnects from an Audio + // Sink. A user application is supposed to clear any of its resources which + // it keeps for that particular connection. |transport_path| is the Media + // Transport object which has been kept by an endpoint during the + // connection. + virtual void ClearConfiguration(const dbus::ObjectPath& transport_path) = 0; + + // This method will be called when the Bluetooth daemon unregisters the + // Media Endpoint. Media Endpoint objects can use this method to clean up + // tasks. There is no need to unregister the endpoint, since when this + // method gets called, that endpoint has been unregistered. This corresponds + // to the org.bluez.MediaEndpoint1.Release and is renamed to avoid + // a conflict with base::RefCounted<T>. + virtual void Released() = 0; + }; + + virtual ~BluetoothMediaEndpointServiceProvider(); + + // Creates the instance where |bus| is the D-Bus bus connection to export the + // object onto, |object_path| is the object path that it should have and + // |delegate| is the object to which all method calls will be passed and + // responses generated from. + static BluetoothMediaEndpointServiceProvider* Create( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate); + + protected: + BluetoothMediaEndpointServiceProvider(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothMediaEndpointServiceProvider); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_MEDIA_ENDPOINT_SERVICE_PROVIDER_H_ diff --git a/chromeos/dbus/bluetooth_media_transport_client.cc b/chromeos/dbus/bluetooth_media_transport_client.cc new file mode 100644 index 0000000..3117de2 --- /dev/null +++ b/chromeos/dbus/bluetooth_media_transport_client.cc @@ -0,0 +1,286 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_media_transport_client.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/observer_list.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_manager.h" +#include "dbus/object_proxy.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace { + +// TODO(mcchou): Add these service constants into dbus/service_constants.h +// later. +const char kBluetoothMediaTransportInterface[] = "org.bluez.MediaTransport1"; + +// Constants used to indicate exceptional error conditions. +const char kNoResponseError[] = "org.chromium.Error.NoResponse"; +const char kUnexpectedResponse[] = "org.chromium.Error.UnexpectedResponse"; + +// Method names of Media Transport interface. +const char kAcquire[] = "Acquire"; +const char kTryAcquire[] = "TryAcquire"; +const char kRelease[] = "Release"; + +} // namespace + +namespace chromeos { + +// static +const char BluetoothMediaTransportClient::kDeviceProperty[] = "Device"; +const char BluetoothMediaTransportClient::kUUIDProperty[] = "UUID"; +const char BluetoothMediaTransportClient::kCodecProperty[] = "Codec"; +const char BluetoothMediaTransportClient::kConfigurationProperty[] = + "Configuration"; +const char BluetoothMediaTransportClient::kStateProperty[] = "State"; +const char BluetoothMediaTransportClient::kDelayProperty[] = "Delay"; +const char BluetoothMediaTransportClient::kVolumeProperty[] = "Volume"; + +// static +const char BluetoothMediaTransportClient::kStateIdle[] = "idle"; +const char BluetoothMediaTransportClient::kStatePending[] = "pending"; +const char BluetoothMediaTransportClient::kStateActive[] = "active"; + +BluetoothMediaTransportClient::Properties::Properties( + dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const PropertyChangedCallback& callback) + : dbus::PropertySet(object_proxy, interface_name, callback) { + RegisterProperty(kDeviceProperty, &device); + RegisterProperty(kUUIDProperty, &uuid); + RegisterProperty(kCodecProperty, &codec); + RegisterProperty(kConfigurationProperty, &configuration); + RegisterProperty(kStateProperty, &state); + RegisterProperty(kDelayProperty, &delay); + RegisterProperty(kVolumeProperty, &volume); +} + +BluetoothMediaTransportClient::Properties::~Properties() {} + +class BluetoothMediaTransportClientImpl + : public BluetoothMediaTransportClient, + public dbus::ObjectManager::Interface { + public: + BluetoothMediaTransportClientImpl() + : object_manager_(nullptr), weak_ptr_factory_(this) {} + + ~BluetoothMediaTransportClientImpl() override { + object_manager_->UnregisterInterface(kBluetoothMediaTransportInterface); + } + + // dbus::ObjectManager::Interface overrides. + + dbus::PropertySet* CreateProperties( + dbus::ObjectProxy* object_proxy, + const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + Properties* properties = new Properties( + object_proxy, interface_name, + base::Bind(&BluetoothMediaTransportClientImpl::OnPropertyChanged, + weak_ptr_factory_.GetWeakPtr(), object_path)); + return properties; + } + + void ObjectAdded(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + VLOG(1) << "Remote Media Transport added: " << object_path.value(); + FOR_EACH_OBSERVER(BluetoothMediaTransportClient::Observer, observers_, + MediaTransportAdded(object_path)); + } + + void ObjectRemoved(const dbus::ObjectPath& object_path, + const std::string& interface_name) override { + VLOG(1) << "Remote Media Transport removed: " << object_path.value(); + FOR_EACH_OBSERVER(BluetoothMediaTransportClient::Observer, observers_, + MediaTransportRemoved(object_path)); + } + + // BluetoothMediaTransportClient overrides. + + void AddObserver(BluetoothMediaTransportClient::Observer* observer) override { + DCHECK(observer); + observers_.AddObserver(observer); + } + + void RemoveObserver( + BluetoothMediaTransportClient::Observer* observer) override { + DCHECK(observer); + observers_.RemoveObserver(observer); + } + + Properties* GetProperties(const dbus::ObjectPath& object_path) override { + DCHECK(object_manager_); + return static_cast<Properties*>(object_manager_->GetProperties( + object_path, kBluetoothMediaTransportInterface)); + } + + void Acquire(const dbus::ObjectPath& object_path, + const AcquireCallback& callback, + const ErrorCallback& error_callback) override { + VLOG(1) << "Acquire - transport: " << object_path.value(); + + DCHECK(object_manager_); + + dbus::MethodCall method_call(kBluetoothMediaTransportInterface, kAcquire); + + // Get object proxy. + scoped_refptr<dbus::ObjectProxy> object_proxy( + object_manager_->GetObjectProxy(object_path)); + + // Call Acquire method of Media Transport interface. + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothMediaTransportClientImpl::OnAcquireSuccess, + weak_ptr_factory_.GetWeakPtr(), callback, error_callback), + base::Bind(&BluetoothMediaTransportClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + void TryAcquire(const dbus::ObjectPath& object_path, + const AcquireCallback& callback, + const ErrorCallback& error_callback) override { + VLOG(1) << "TryAcquire - transport: " << object_path.value(); + + DCHECK(object_manager_); + + dbus::MethodCall method_call(kBluetoothMediaTransportInterface, + kTryAcquire); + + // Get object proxy. + scoped_refptr<dbus::ObjectProxy> object_proxy( + object_manager_->GetObjectProxy(object_path)); + + // Call TryAcquire method of Media Transport interface. + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothMediaTransportClientImpl::OnAcquireSuccess, + weak_ptr_factory_.GetWeakPtr(), callback, error_callback), + base::Bind(&BluetoothMediaTransportClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + void Release(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + VLOG(1) << "Release - transport: " << object_path.value(); + + DCHECK(object_manager_); + + dbus::MethodCall method_call(kBluetoothMediaTransportInterface, kRelease); + + // Get object proxy. + scoped_refptr<dbus::ObjectProxy> object_proxy( + object_manager_->GetObjectProxy(object_path)); + + // Call TryAcquire method of Media Transport interface. + object_proxy->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothMediaTransportClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothMediaTransportClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + protected: + void Init(dbus::Bus* bus) override { + DCHECK(bus); + object_manager_ = bus->GetObjectManager( + bluetooth_object_manager::kBluetoothObjectManagerServiceName, + dbus::ObjectPath( + bluetooth_object_manager::kBluetoothObjectManagerServicePath)); + object_manager_->RegisterInterface(kBluetoothMediaTransportInterface, this); + } + + private: + // Called by dbus::PropertySet when a property value is changed. + void OnPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name) { + VLOG(1) << "Name of the changed property: " << property_name; + + // Dispatches the change to the corresponding property-changed handler. + FOR_EACH_OBSERVER( + BluetoothMediaTransportClient::Observer, observers_, + MediaTransportPropertyChanged(object_path, property_name)); + } + + // Called when a response for successful method call is received. + void OnSuccess(const base::Closure& callback, dbus::Response* response) { + DCHECK(response); + callback.Run(); + } + + // Called when a response for |Acquire|/|TryAcquire| method call is received. + void OnAcquireSuccess(const AcquireCallback& callback, + const ErrorCallback& error_callback, + dbus::Response* response) { + DCHECK(response); + + dbus::FileDescriptor fd; + uint16_t read_mtu; + uint16_t write_mtu; + + // Parse the response. + dbus::MessageReader reader(response); + if (reader.PopFileDescriptor(&fd) && reader.PopUint16(&read_mtu) && + reader.PopUint16(&write_mtu)) { + fd.CheckValidity(); + DCHECK(fd.is_valid()); + + VLOG(1) << "OnAcquireSuccess - fd: " << fd.value() + << ", read MTU: " << read_mtu << ", write MTU: " << write_mtu; + + // The ownership of the file descriptor is transferred to the user + // application. + callback.Run(&fd, read_mtu, write_mtu); + return; + } + + error_callback.Run( + kUnexpectedResponse, + "Failed to retrieve file descriptor, read MTU and write MTU."); + } + + // Called when a response for a failed method call is received. + void OnError(const ErrorCallback& error_callback, + dbus::ErrorResponse* response) { + DCHECK(response); + + // Error response has optional error message argument. + std::string error_name; + std::string error_message; + if (response) { + dbus::MessageReader reader(response); + error_name = response->GetErrorName(); + reader.PopString(&error_message); + } else { + error_name = kNoResponseError; + } + error_callback.Run(error_name, error_message); + } + + dbus::ObjectManager* object_manager_; + + // List of observers interested in event notifications from us. + base::ObserverList<BluetoothMediaTransportClient::Observer> observers_; + + base::WeakPtrFactory<BluetoothMediaTransportClientImpl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothMediaTransportClientImpl); +}; + +BluetoothMediaTransportClient::BluetoothMediaTransportClient() {} + +BluetoothMediaTransportClient::~BluetoothMediaTransportClient() {} + +BluetoothMediaTransportClient* BluetoothMediaTransportClient::Create() { + return new BluetoothMediaTransportClientImpl(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_media_transport_client.h b/chromeos/dbus/bluetooth_media_transport_client.h new file mode 100644 index 0000000..4476ae7 --- /dev/null +++ b/chromeos/dbus/bluetooth_media_transport_client.h @@ -0,0 +1,142 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_MEDIA_TRANSPORT_CLIENT_H_ +#define CHROMEOS_DBUS_BLUETOOTH_MEDIA_TRANSPORT_CLIENT_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_client.h" +#include "dbus/file_descriptor.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +class CHROMEOS_EXPORT BluetoothMediaTransportClient : public DBusClient { + public: + struct Properties : public dbus::PropertySet { + // The path to the device object which the transport is connected to. + // Read-only. + dbus::Property<dbus::ObjectPath> device; + + // UUID of the profile which the transport is for. Read-only. + dbus::Property<std::string> uuid; + + // Assigned codec value supported by the media transport. Read-only. + dbus::Property<uint8_t> codec; + + // The configuration used by the media transport. Read-only. + dbus::Property<std::vector<uint8_t>> configuration; + + // The state of the transport. Read-only. + // The values can be one of the following: + // "idle": not streaming + // "pending": streaming but not acquired + // "active": streaming and acquired + dbus::Property<std::string> state; + + // The unit of transport delay is in 1/10 of millisecond. This property is + // only writeable when the transport was aquired by the sender. Optional. + dbus::Property<uint16_t> delay; + + // The volume level of the transport. This property is only writable when + // the transport was aquired by the sender. Optional. + dbus::Property<uint16_t> volume; + + Properties(dbus::ObjectProxy* object_proxy, + const std::string& interface_name, + const PropertyChangedCallback& callback); + ~Properties() override; + }; + + class Observer { + public: + virtual ~Observer() {} + + // Called when the Media Transport with object path |object_path| is added + // to the system. + virtual void MediaTransportAdded(const dbus::ObjectPath& object_path) {} + + // Called when the Media Transport with object path |object_path| is removed + // from the system. + virtual void MediaTransportRemoved(const dbus::ObjectPath& object_path) {} + + // Called when the Media Transport with object path |object_path| has + // a change in the value of the property with name |property_name|. + virtual void MediaTransportPropertyChanged( + const dbus::ObjectPath& object_path, + const std::string& property_name) {} + }; + + // TODO(mcchou): Move all static constants to service_constants.h. + // Constants used for the names of Media Transport's properties. + static const char kDeviceProperty[]; + static const char kUUIDProperty[]; + static const char kCodecProperty[]; + static const char kConfigurationProperty[]; + static const char kStateProperty[]; + static const char kDelayProperty[]; + static const char kVolumeProperty[]; + + // All possible states of a valid media transport object. + static const char kStateIdle[]; + static const char kStatePending[]; + static const char kStateActive[]; + + ~BluetoothMediaTransportClient() override; + + // The ErrorCallback is used by media transport API methods to indicate + // failure. It receives two arguments: the name of the error in |error_name| + // and an optional message in |error_message|. + typedef base::Callback<void(const std::string& error_name, + const std::string& error_message)> ErrorCallback; + + // The AcquireCallback is used by |Acquire| method of media tansport API tp + // indicate the success of the method. + typedef base::Callback<void(dbus::FileDescriptor* fd, + const uint16_t read_mtu, + const uint16_t write_mtu)> AcquireCallback; + + // Adds and removes observers for events on all remote Media Transports. Check + // the |object_path| parameter of observer methods to determine which Media + // Transport is issuing the event. + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + + virtual Properties* GetProperties(const dbus::ObjectPath& object_path) = 0; + + // Acquires transport file descriptor and the MTU for read and write. + virtual void Acquire(const dbus::ObjectPath& object_path, + const AcquireCallback& callback, + const ErrorCallback& error_callback) = 0; + + // Acquires transport file descriptor only if the transport is in "pending" + // state at the time the message is received by BlueZ. Otherwise no request + // will be sent to the remote device and the function will just fail with + // org.bluez.Error.NotAvailable. + virtual void TryAcquire(const dbus::ObjectPath& object_path, + const AcquireCallback& callback, + const ErrorCallback& error_callback) = 0; + + // Releases the file descriptor of the transport. + virtual void Release(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + static BluetoothMediaTransportClient* Create(); + + protected: + BluetoothMediaTransportClient(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothMediaTransportClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_MEDIA_TRANSPORT_CLIENT_H_ diff --git a/chromeos/dbus/bluetooth_profile_manager_client.cc b/chromeos/dbus/bluetooth_profile_manager_client.cc new file mode 100644 index 0000000..01301d4 --- /dev/null +++ b/chromeos/dbus/bluetooth_profile_manager_client.cc @@ -0,0 +1,234 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_profile_manager_client.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_proxy.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +const char BluetoothProfileManagerClient::kNoResponseError[] = + "org.chromium.Error.NoResponse"; + +BluetoothProfileManagerClient::Options::Options() {} + +BluetoothProfileManagerClient::Options::~Options() {} + +// The BluetoothProfileManagerClient implementation used in production. +class BluetoothProfileManagerClientImpl : public BluetoothProfileManagerClient { + public: + BluetoothProfileManagerClientImpl() : weak_ptr_factory_(this) {} + + ~BluetoothProfileManagerClientImpl() override {} + + // BluetoothProfileManagerClient override. + void RegisterProfile(const dbus::ObjectPath& profile_path, + const std::string& uuid, + const Options& options, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call( + bluetooth_profile_manager::kBluetoothProfileManagerInterface, + bluetooth_profile_manager::kRegisterProfile); + + dbus::MessageWriter writer(&method_call); + writer.AppendObjectPath(profile_path); + writer.AppendString(uuid); + + dbus::MessageWriter array_writer(NULL); + writer.OpenArray("{sv}", &array_writer); + + dbus::MessageWriter dict_writer(NULL); + + // Send Name if provided. + if (options.name.get() != NULL) { + array_writer.OpenDictEntry(&dict_writer); + dict_writer.AppendString(bluetooth_profile_manager::kNameOption); + dict_writer.AppendVariantOfString(*(options.name)); + array_writer.CloseContainer(&dict_writer); + } + + // Send Service if provided. + if (options.service.get() != NULL) { + dbus::MessageWriter dict_writer(NULL); + array_writer.OpenDictEntry(&dict_writer); + dict_writer.AppendString(bluetooth_profile_manager::kServiceOption); + dict_writer.AppendVariantOfString(*(options.service)); + array_writer.CloseContainer(&dict_writer); + } + + // Send Role if not the default value. + if (options.role != SYMMETRIC) { + dbus::MessageWriter dict_writer(NULL); + array_writer.OpenDictEntry(&dict_writer); + dict_writer.AppendString(bluetooth_profile_manager::kRoleOption); + if (options.role == CLIENT) + dict_writer.AppendVariantOfString( + bluetooth_profile_manager::kClientRoleOption); + else if (options.role == SERVER) + dict_writer.AppendVariantOfString( + bluetooth_profile_manager::kServerRoleOption); + else + dict_writer.AppendVariantOfString(""); + array_writer.CloseContainer(&dict_writer); + } + + // Send Channel if provided. + if (options.channel.get() != NULL) { + dbus::MessageWriter dict_writer(NULL); + array_writer.OpenDictEntry(&dict_writer); + dict_writer.AppendString(bluetooth_profile_manager::kChannelOption); + dict_writer.AppendVariantOfUint16(*(options.channel)); + array_writer.CloseContainer(&dict_writer); + } + + // Send PSM if provided. + if (options.psm.get() != NULL) { + dbus::MessageWriter dict_writer(NULL); + array_writer.OpenDictEntry(&dict_writer); + dict_writer.AppendString(bluetooth_profile_manager::kPSMOption); + dict_writer.AppendVariantOfUint16(*(options.psm)); + array_writer.CloseContainer(&dict_writer); + } + + // Send RequireAuthentication if provided. + if (options.require_authentication.get() != NULL) { + array_writer.OpenDictEntry(&dict_writer); + dict_writer.AppendString( + bluetooth_profile_manager::kRequireAuthenticationOption); + dict_writer.AppendVariantOfBool(*(options.require_authentication)); + array_writer.CloseContainer(&dict_writer); + } + + // Send RequireAuthorization if provided. + if (options.require_authorization.get() != NULL) { + array_writer.OpenDictEntry(&dict_writer); + dict_writer.AppendString( + bluetooth_profile_manager::kRequireAuthorizationOption); + dict_writer.AppendVariantOfBool(*(options.require_authorization)); + array_writer.CloseContainer(&dict_writer); + } + + // Send AutoConnect if provided. + if (options.auto_connect.get() != NULL) { + array_writer.OpenDictEntry(&dict_writer); + dict_writer.AppendString(bluetooth_profile_manager::kAutoConnectOption); + dict_writer.AppendVariantOfBool(*(options.auto_connect)); + array_writer.CloseContainer(&dict_writer); + } + + // Send ServiceRecord if provided. + if (options.service_record.get() != NULL) { + dbus::MessageWriter dict_writer(NULL); + array_writer.OpenDictEntry(&dict_writer); + dict_writer.AppendString(bluetooth_profile_manager::kServiceRecordOption); + dict_writer.AppendVariantOfString(*(options.service_record)); + array_writer.CloseContainer(&dict_writer); + } + + // Send Version if provided. + if (options.version.get() != NULL) { + dbus::MessageWriter dict_writer(NULL); + array_writer.OpenDictEntry(&dict_writer); + dict_writer.AppendString(bluetooth_profile_manager::kVersionOption); + dict_writer.AppendVariantOfUint16(*(options.version)); + array_writer.CloseContainer(&dict_writer); + } + + // Send Features if provided. + if (options.features.get() != NULL) { + dbus::MessageWriter dict_writer(NULL); + array_writer.OpenDictEntry(&dict_writer); + dict_writer.AppendString(bluetooth_profile_manager::kFeaturesOption); + dict_writer.AppendVariantOfUint16(*(options.features)); + array_writer.CloseContainer(&dict_writer); + } + + writer.CloseContainer(&array_writer); + + object_proxy_->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothProfileManagerClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothProfileManagerClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + // BluetoothProfileManagerClient override. + void UnregisterProfile(const dbus::ObjectPath& profile_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override { + dbus::MethodCall method_call( + bluetooth_profile_manager::kBluetoothProfileManagerInterface, + bluetooth_profile_manager::kUnregisterProfile); + + dbus::MessageWriter writer(&method_call); + writer.AppendObjectPath(profile_path); + + object_proxy_->CallMethodWithErrorCallback( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&BluetoothProfileManagerClientImpl::OnSuccess, + weak_ptr_factory_.GetWeakPtr(), callback), + base::Bind(&BluetoothProfileManagerClientImpl::OnError, + weak_ptr_factory_.GetWeakPtr(), error_callback)); + } + + protected: + void Init(dbus::Bus* bus) override { + DCHECK(bus); + object_proxy_ = bus->GetObjectProxy( + bluetooth_profile_manager::kBluetoothProfileManagerServiceName, + dbus::ObjectPath( + bluetooth_profile_manager::kBluetoothProfileManagerServicePath)); + } + + private: + // Called when a response for successful method call is received. + void OnSuccess(const base::Closure& callback, dbus::Response* response) { + DCHECK(response); + callback.Run(); + } + + // Called when a response for a failed method call is received. + void OnError(const ErrorCallback& error_callback, + dbus::ErrorResponse* response) { + // Error response has optional error message argument. + std::string error_name; + std::string error_message; + if (response) { + dbus::MessageReader reader(response); + error_name = response->GetErrorName(); + reader.PopString(&error_message); + } else { + error_name = kNoResponseError; + error_message = ""; + } + error_callback.Run(error_name, error_message); + } + + dbus::ObjectProxy* object_proxy_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothProfileManagerClientImpl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothProfileManagerClientImpl); +}; + +BluetoothProfileManagerClient::BluetoothProfileManagerClient() {} + +BluetoothProfileManagerClient::~BluetoothProfileManagerClient() {} + +BluetoothProfileManagerClient* BluetoothProfileManagerClient::Create() { + return new BluetoothProfileManagerClientImpl(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_profile_manager_client.h b/chromeos/dbus/bluetooth_profile_manager_client.h new file mode 100644 index 0000000..8f53412 --- /dev/null +++ b/chromeos/dbus/bluetooth_profile_manager_client.h @@ -0,0 +1,107 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_PROFILE_MANAGER_CLIENT_H_ +#define CHROMEOS_DBUS_BLUETOOTH_PROFILE_MANAGER_CLIENT_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_client.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// BluetoothProfileManagerClient is used to communicate with the profile +// manager object of the Bluetooth daemon. +class CHROMEOS_EXPORT BluetoothProfileManagerClient : public DBusClient { + public: + // Species the role of the object within the profile. SYMMETRIC should be + // usually used unless the profile requires you specify as a CLIENT or as a + // SERVER. + enum ProfileRole { SYMMETRIC, CLIENT, SERVER }; + + // Options used to register a Profile object. + struct CHROMEOS_EXPORT Options { + Options(); + ~Options(); + + // Human readable name for the profile. + scoped_ptr<std::string> name; + + // Primary service class UUID (if different from the actual UUID) + scoped_ptr<std::string> service; + + // Role. + enum ProfileRole role; + + // RFCOMM channel number. + scoped_ptr<uint16> channel; + + // PSM number. + scoped_ptr<uint16> psm; + + // Pairing is required before connections will be established. + scoped_ptr<bool> require_authentication; + + // Request authorization before connections will be established. + scoped_ptr<bool> require_authorization; + + // Force connections when a remote device is connected. + scoped_ptr<bool> auto_connect; + + // Manual SDP record. + scoped_ptr<std::string> service_record; + + // Profile version. + scoped_ptr<uint16> version; + + // Profile features. + scoped_ptr<uint16> features; + }; + + ~BluetoothProfileManagerClient() override; + + // The ErrorCallback is used by adapter methods to indicate failure. + // It receives two arguments: the name of the error in |error_name| and + // an optional message in |error_message|. + typedef base::Callback<void(const std::string& error_name, + const std::string& error_message)> ErrorCallback; + + // Registers a profile implementation within the local process at the + // D-bus object path |profile_path| with the remote profile manager. + // |uuid| specifies the identifier of the profile and |options| the way in + // which the profile is implemented. + virtual void RegisterProfile(const dbus::ObjectPath& profile_path, + const std::string& uuid, + const Options& options, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Unregisters the profile with the D-Bus object path |agent_path| from the + // remote profile manager. + virtual void UnregisterProfile(const dbus::ObjectPath& profile_path, + const base::Closure& callback, + const ErrorCallback& error_callback) = 0; + + // Creates the instance. + static BluetoothProfileManagerClient* Create(); + + // Constants used to indicate exceptional error conditions. + static const char kNoResponseError[]; + + protected: + BluetoothProfileManagerClient(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothProfileManagerClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_PROFILE_MANAGER_CLIENT_H_ diff --git a/chromeos/dbus/bluetooth_profile_service_provider.cc b/chromeos/dbus/bluetooth_profile_service_provider.cc new file mode 100644 index 0000000..f63f5ea --- /dev/null +++ b/chromeos/dbus/bluetooth_profile_service_provider.cc @@ -0,0 +1,248 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/bluetooth_profile_service_provider.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/threading/platform_thread.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_profile_service_provider.h" +#include "dbus/exported_object.h" +#include "dbus/message.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +// The BluetoothProfileServiceProvider implementation used in production. +class BluetoothProfileServiceProviderImpl + : public BluetoothProfileServiceProvider { + public: + BluetoothProfileServiceProviderImpl(dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate) + : origin_thread_id_(base::PlatformThread::CurrentId()), + bus_(bus), + delegate_(delegate), + object_path_(object_path), + weak_ptr_factory_(this) { + VLOG(1) << "Creating Bluetooth Profile: " << object_path_.value(); + + exported_object_ = bus_->GetExportedObject(object_path_); + + exported_object_->ExportMethod( + bluetooth_profile::kBluetoothProfileInterface, + bluetooth_profile::kRelease, + base::Bind(&BluetoothProfileServiceProviderImpl::Release, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothProfileServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + bluetooth_profile::kBluetoothProfileInterface, + bluetooth_profile::kNewConnection, + base::Bind(&BluetoothProfileServiceProviderImpl::NewConnection, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothProfileServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + bluetooth_profile::kBluetoothProfileInterface, + bluetooth_profile::kRequestDisconnection, + base::Bind(&BluetoothProfileServiceProviderImpl::RequestDisconnection, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothProfileServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + + exported_object_->ExportMethod( + bluetooth_profile::kBluetoothProfileInterface, + bluetooth_profile::kCancel, + base::Bind(&BluetoothProfileServiceProviderImpl::Cancel, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&BluetoothProfileServiceProviderImpl::OnExported, + weak_ptr_factory_.GetWeakPtr())); + } + + ~BluetoothProfileServiceProviderImpl() override { + VLOG(1) << "Cleaning up Bluetooth Profile: " << object_path_.value(); + + // Unregister the object path so we can reuse with a new agent. + bus_->UnregisterExportedObject(object_path_); + } + + private: + // Returns true if the current thread is on the origin thread. + bool OnOriginThread() { + return base::PlatformThread::CurrentId() == origin_thread_id_; + } + + // Called by dbus:: when the profile is unregistered from the Bluetooth + // daemon, generally by our request. + void Release(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + delegate_->Released(); + + response_sender.Run(dbus::Response::FromMethodCall(method_call)); + } + + // Called by dbus:: when the Bluetooth daemon establishes a new connection + // to the profile. + void NewConnection(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + dbus::MessageReader reader(method_call); + dbus::ObjectPath device_path; + scoped_ptr<dbus::FileDescriptor> fd(new dbus::FileDescriptor()); + dbus::MessageReader array_reader(NULL); + if (!reader.PopObjectPath(&device_path) || + !reader.PopFileDescriptor(fd.get()) || + !reader.PopArray(&array_reader)) { + LOG(WARNING) << "NewConnection called with incorrect paramters: " + << method_call->ToString(); + return; + } + + Delegate::Options options; + while (array_reader.HasMoreData()) { + dbus::MessageReader dict_entry_reader(NULL); + std::string key; + if (!array_reader.PopDictEntry(&dict_entry_reader) || + !dict_entry_reader.PopString(&key)) { + LOG(WARNING) << "NewConnection called with incorrect paramters: " + << method_call->ToString(); + } else { + if (key == bluetooth_profile::kVersionProperty) + dict_entry_reader.PopVariantOfUint16(&options.version); + else if (key == bluetooth_profile::kFeaturesProperty) + dict_entry_reader.PopVariantOfUint16(&options.features); + } + } + + Delegate::ConfirmationCallback callback = base::Bind( + &BluetoothProfileServiceProviderImpl::OnConfirmation, + weak_ptr_factory_.GetWeakPtr(), method_call, response_sender); + + delegate_->NewConnection(device_path, fd.Pass(), options, callback); + } + + // Called by dbus:: when the Bluetooth daemon is about to disconnect the + // profile. + void RequestDisconnection( + dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + dbus::MessageReader reader(method_call); + dbus::ObjectPath device_path; + if (!reader.PopObjectPath(&device_path)) { + LOG(WARNING) << "RequestDisconnection called with incorrect paramters: " + << method_call->ToString(); + return; + } + + Delegate::ConfirmationCallback callback = base::Bind( + &BluetoothProfileServiceProviderImpl::OnConfirmation, + weak_ptr_factory_.GetWeakPtr(), method_call, response_sender); + + delegate_->RequestDisconnection(device_path, callback); + } + + // Called by dbus:: when the request failed before a reply was returned + // from the device. + void Cancel(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender) { + DCHECK(OnOriginThread()); + DCHECK(delegate_); + + delegate_->Cancel(); + + response_sender.Run(dbus::Response::FromMethodCall(method_call)); + } + + // Called by dbus:: when a method is exported. + void OnExported(const std::string& interface_name, + const std::string& method_name, + bool success) { + LOG_IF(WARNING, !success) << "Failed to export " << interface_name << "." + << method_name; + } + + // Called by the Delegate in response to a method requiring confirmation. + void OnConfirmation(dbus::MethodCall* method_call, + dbus::ExportedObject::ResponseSender response_sender, + Delegate::Status status) { + DCHECK(OnOriginThread()); + + switch (status) { + case Delegate::SUCCESS: { + response_sender.Run(dbus::Response::FromMethodCall(method_call)); + break; + } + case Delegate::REJECTED: { + response_sender.Run(dbus::ErrorResponse::FromMethodCall( + method_call, bluetooth_profile::kErrorRejected, "rejected")); + break; + } + case Delegate::CANCELLED: { + response_sender.Run(dbus::ErrorResponse::FromMethodCall( + method_call, bluetooth_profile::kErrorCanceled, "canceled")); + break; + } + default: + NOTREACHED() << "Unexpected status code from delegate: " << status; + } + } + + // Origin thread (i.e. the UI thread in production). + base::PlatformThreadId origin_thread_id_; + + // D-Bus bus object is exported on, not owned by this object and must + // outlive it. + dbus::Bus* bus_; + + // All incoming method calls are passed on to the Delegate and a callback + // passed to generate the reply. |delegate_| is generally the object that + // owns this one, and must outlive it. + Delegate* delegate_; + + // D-Bus object path of object we are exporting, kept so we can unregister + // again in our destructor. + dbus::ObjectPath object_path_; + + // D-Bus object we are exporting, owned by this object. + scoped_refptr<dbus::ExportedObject> exported_object_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<BluetoothProfileServiceProviderImpl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(BluetoothProfileServiceProviderImpl); +}; + +BluetoothProfileServiceProvider::BluetoothProfileServiceProvider() {} + +BluetoothProfileServiceProvider::~BluetoothProfileServiceProvider() {} + +// static +BluetoothProfileServiceProvider* BluetoothProfileServiceProvider::Create( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate) { + if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH)) { + return new BluetoothProfileServiceProviderImpl(bus, object_path, delegate); + } else { + return new FakeBluetoothProfileServiceProvider(object_path, delegate); + } +} + +} // namespace chromeos diff --git a/chromeos/dbus/bluetooth_profile_service_provider.h b/chromeos/dbus/bluetooth_profile_service_provider.h new file mode 100644 index 0000000..366a354 --- /dev/null +++ b/chromeos/dbus/bluetooth_profile_service_provider.h @@ -0,0 +1,120 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_BLUETOOTH_PROFILE_SERVICE_PROVIDER_H_ +#define CHROMEOS_DBUS_BLUETOOTH_PROFILE_SERVICE_PROVIDER_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "chromeos/chromeos_export.h" +#include "dbus/bus.h" +#include "dbus/file_descriptor.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// BluetoothProfileServiceProvider is used to provide a D-Bus object that the +// Bluetooth daemon can communicate with to connect application profiles. +// +// Instantiate with a chosen D-Bus object path and delegate object, and pass +// the D-Bus object path as the |agent_path| argument to the +// chromeos::BluetoothProfileManagerClient::RegisterProfile() method. +// +// When an incoming profile connection occurs, or after initiating a connection +// using the chromeos::BluetoothDeviceClient::ConnectProfile() method, the +// Bluetooth daemon will make calls to this profile object and they will be +// passed on to your Delegate object for handling. Responses should be returned +// using the callbacks supplied to those methods. +class CHROMEOS_EXPORT BluetoothProfileServiceProvider { + public: + // Interface for reacting to profile requests. + class Delegate { + public: + virtual ~Delegate() {} + + // Possible status values that may be returned to callbacks on a new + // connection or a requested disconnection. Success indicates acceptance, + // reject indicates the user rejected or denied the request; cancelled + // means the user cancelled the request without confirming either way. + enum Status { SUCCESS, REJECTED, CANCELLED }; + + // Connection-specific options. + struct CHROMEOS_EXPORT Options { + Options() {} + ~Options() {} + + // Profile version. + uint16 version; + + // Profile features. + uint16 features; + }; + + // The ConfirmationCallback is used for methods which require confirmation; + // it should be called with one argument, the |status| of the request + // (success, rejected or cancelled). + typedef base::Callback<void(Status)> ConfirmationCallback; + + // This method will be called when the profile is unregistered from the + // Bluetooth daemon, generally at shutdown or at the applications' request. + // It may be used to perform cleanup tasks. This corresponds to the + // org.bluez.Profile1.Release method and is renamed to avoid a conflict + // with base::Refcounted<T>. + virtual void Released() = 0; + + // This method will be called when a profile connection to the device + // with object path |device_path| is established. |callback| must be called + // to confirm the connection, or indicate rejection or cancellation. + // + // A file descriptor for the connection socket is provided in |fd|, and + // details about the specific implementation of the profile in |options|. + // + // IMPORTANT: Ownership of the file descriptor object |fd| is passed to + // the delegate by this call. The delegate is responsible for checking the + // validity of |fd| on a thread where I/O is permitted before taking the + // value. If the value is not taken, the file descriptor is closed. + // + // Ownership of |options| is NOT passed so information out of it must be + // copied if required. + virtual void NewConnection(const dbus::ObjectPath& device_path, + scoped_ptr<dbus::FileDescriptor> fd, + const Options& options, + const ConfirmationCallback& callback) = 0; + + // This method will be called when a profile connection to the device + // with object path |device_path| is disconnected. Any file descriptors + // owned by the service should be cleaned up and |callback| called to + // confirm, or indicate rejection or cancellation of the disconnection. + virtual void RequestDisconnection(const dbus::ObjectPath& device_path, + const ConfirmationCallback& callback) = 0; + + // This method will be called by the Bluetooth daemon to indicate that + // a profile request failed before a reply was returned from the device. + virtual void Cancel() = 0; + }; + + virtual ~BluetoothProfileServiceProvider(); + + // Creates the instance where |bus| is the D-Bus bus connection to export + // the object onto, |object_path| is the object path that it should have + // and |delegate| is the object to which all method calls will be passed + // and responses generated from. + static BluetoothProfileServiceProvider* Create( + dbus::Bus* bus, + const dbus::ObjectPath& object_path, + Delegate* delegate); + + protected: + BluetoothProfileServiceProvider(); + + private: + DISALLOW_COPY_AND_ASSIGN(BluetoothProfileServiceProvider); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_BLUETOOTH_PROFILE_SERVICE_PROVIDER_H_ diff --git a/chromeos/dbus/dbus_client_bundle.cc b/chromeos/dbus/dbus_client_bundle.cc index 87e6598..642ffb4 100644 --- a/chromeos/dbus/dbus_client_bundle.cc +++ b/chromeos/dbus/dbus_client_bundle.cc @@ -13,6 +13,18 @@ #include "chromeos/dbus/amplifier_client.h" #include "chromeos/dbus/ap_manager_client.h" #include "chromeos/dbus/audio_dsp_client.h" +#include "chromeos/dbus/bluetooth_adapter_client.h" +#include "chromeos/dbus/bluetooth_agent_manager_client.h" +#include "chromeos/dbus/bluetooth_device_client.h" +#include "chromeos/dbus/bluetooth_gatt_characteristic_client.h" +#include "chromeos/dbus/bluetooth_gatt_descriptor_client.h" +#include "chromeos/dbus/bluetooth_gatt_manager_client.h" +#include "chromeos/dbus/bluetooth_gatt_service_client.h" +#include "chromeos/dbus/bluetooth_input_client.h" +#include "chromeos/dbus/bluetooth_le_advertising_manager_client.h" +#include "chromeos/dbus/bluetooth_media_client.h" +#include "chromeos/dbus/bluetooth_media_transport_client.h" +#include "chromeos/dbus/bluetooth_profile_manager_client.h" #include "chromeos/dbus/cras_audio_client.h" #include "chromeos/dbus/cros_disks_client.h" #include "chromeos/dbus/cryptohome_client.h" @@ -21,6 +33,18 @@ #include "chromeos/dbus/fake_amplifier_client.h" #include "chromeos/dbus/fake_ap_manager_client.h" #include "chromeos/dbus/fake_audio_dsp_client.h" +#include "chromeos/dbus/fake_bluetooth_adapter_client.h" +#include "chromeos/dbus/fake_bluetooth_agent_manager_client.h" +#include "chromeos/dbus/fake_bluetooth_device_client.h" +#include "chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h" +#include "chromeos/dbus/fake_bluetooth_gatt_descriptor_client.h" +#include "chromeos/dbus/fake_bluetooth_gatt_manager_client.h" +#include "chromeos/dbus/fake_bluetooth_gatt_service_client.h" +#include "chromeos/dbus/fake_bluetooth_input_client.h" +#include "chromeos/dbus/fake_bluetooth_le_advertising_manager_client.h" +#include "chromeos/dbus/fake_bluetooth_media_client.h" +#include "chromeos/dbus/fake_bluetooth_media_transport_client.h" +#include "chromeos/dbus/fake_bluetooth_profile_manager_client.h" #include "chromeos/dbus/fake_cras_audio_client.h" #include "chromeos/dbus/fake_cryptohome_client.h" #include "chromeos/dbus/fake_debug_daemon_client.h" @@ -133,6 +157,45 @@ DBusClientBundle::DBusClientBundle(DBusClientTypeMask unstub_client_mask) else audio_dsp_client_.reset(new FakeAudioDspClient); + if (!IsUsingStub(BLUETOOTH)) { + bluetooth_adapter_client_.reset(BluetoothAdapterClient::Create()); + bluetooth_le_advertising_manager_client_.reset( + BluetoothLEAdvertisingManagerClient::Create()); + bluetooth_agent_manager_client_.reset( + BluetoothAgentManagerClient::Create()); + bluetooth_device_client_.reset(BluetoothDeviceClient::Create()); + bluetooth_input_client_.reset(BluetoothInputClient::Create()); + bluetooth_media_client_.reset(BluetoothMediaClient::Create()); + bluetooth_media_transport_client_.reset( + BluetoothMediaTransportClient::Create()); + bluetooth_profile_manager_client_.reset( + BluetoothProfileManagerClient::Create()); + bluetooth_gatt_characteristic_client_.reset( + BluetoothGattCharacteristicClient::Create()); + bluetooth_gatt_descriptor_client_.reset( + BluetoothGattDescriptorClient::Create()); + bluetooth_gatt_manager_client_.reset(BluetoothGattManagerClient::Create()); + bluetooth_gatt_service_client_.reset(BluetoothGattServiceClient::Create()); + } else { + bluetooth_adapter_client_.reset(new FakeBluetoothAdapterClient); + bluetooth_le_advertising_manager_client_.reset( + new FakeBluetoothLEAdvertisingManagerClient); + bluetooth_agent_manager_client_.reset(new FakeBluetoothAgentManagerClient); + bluetooth_device_client_.reset(new FakeBluetoothDeviceClient); + bluetooth_input_client_.reset(new FakeBluetoothInputClient); + bluetooth_media_client_.reset(new FakeBluetoothMediaClient); + bluetooth_media_transport_client_.reset( + new FakeBluetoothMediaTransportClient); + bluetooth_profile_manager_client_.reset( + new FakeBluetoothProfileManagerClient); + bluetooth_gatt_characteristic_client_.reset( + new FakeBluetoothGattCharacteristicClient); + bluetooth_gatt_descriptor_client_.reset( + new FakeBluetoothGattDescriptorClient); + bluetooth_gatt_manager_client_.reset(new FakeBluetoothGattManagerClient); + bluetooth_gatt_service_client_.reset(new FakeBluetoothGattServiceClient); + } + if (!IsUsingStub(CRAS)) cras_audio_client_.reset(CrasAudioClient::Create()); else diff --git a/chromeos/dbus/dbus_client_bundle.h b/chromeos/dbus/dbus_client_bundle.h index ea39ad9..a9a115b 100644 --- a/chromeos/dbus/dbus_client_bundle.h +++ b/chromeos/dbus/dbus_client_bundle.h @@ -15,6 +15,18 @@ namespace chromeos { class AmplifierClient; class ApManagerClient; class AudioDspClient; +class BluetoothAdapterClient; +class BluetoothAgentManagerClient; +class BluetoothDeviceClient; +class BluetoothGattCharacteristicClient; +class BluetoothGattDescriptorClient; +class BluetoothGattManagerClient; +class BluetoothGattServiceClient; +class BluetoothInputClient; +class BluetoothLEAdvertisingManagerClient; +class BluetoothMediaClient; +class BluetoothMediaTransportClient; +class BluetoothProfileManagerClient; class CrasAudioClient; class CrosDisksClient; class CryptohomeClient; @@ -104,6 +116,55 @@ class CHROMEOS_EXPORT DBusClientBundle { AudioDspClient* audio_dsp_client() { return audio_dsp_client_.get(); } + BluetoothAdapterClient* bluetooth_adapter_client() { + return bluetooth_adapter_client_.get(); + } + + BluetoothLEAdvertisingManagerClient* + bluetooth_le_advertising_manager_client() { + return bluetooth_le_advertising_manager_client_.get(); + } + + BluetoothAgentManagerClient* bluetooth_agent_manager_client() { + return bluetooth_agent_manager_client_.get(); + } + + BluetoothDeviceClient* bluetooth_device_client() { + return bluetooth_device_client_.get(); + } + + BluetoothGattCharacteristicClient* bluetooth_gatt_characteristic_client() { + return bluetooth_gatt_characteristic_client_.get(); + } + + BluetoothGattDescriptorClient* bluetooth_gatt_descriptor_client() { + return bluetooth_gatt_descriptor_client_.get(); + } + + BluetoothGattManagerClient* bluetooth_gatt_manager_client() { + return bluetooth_gatt_manager_client_.get(); + } + + BluetoothGattServiceClient* bluetooth_gatt_service_client() { + return bluetooth_gatt_service_client_.get(); + } + + BluetoothInputClient* bluetooth_input_client() { + return bluetooth_input_client_.get(); + } + + BluetoothMediaClient* bluetooth_media_client() { + return bluetooth_media_client_.get(); + } + + BluetoothMediaTransportClient* bluetooth_media_transport_client() { + return bluetooth_media_transport_client_.get(); + } + + BluetoothProfileManagerClient* bluetooth_profile_manager_client() { + return bluetooth_profile_manager_client_.get(); + } + CrasAudioClient* cras_audio_client() { return cras_audio_client_.get(); } @@ -230,6 +291,20 @@ class CHROMEOS_EXPORT DBusClientBundle { scoped_ptr<AmplifierClient> amplifier_client_; scoped_ptr<ApManagerClient> ap_manager_client_; scoped_ptr<AudioDspClient> audio_dsp_client_; + scoped_ptr<BluetoothAdapterClient> bluetooth_adapter_client_; + scoped_ptr<BluetoothLEAdvertisingManagerClient> + bluetooth_le_advertising_manager_client_; + scoped_ptr<BluetoothAgentManagerClient> bluetooth_agent_manager_client_; + scoped_ptr<BluetoothDeviceClient> bluetooth_device_client_; + scoped_ptr<BluetoothGattCharacteristicClient> + bluetooth_gatt_characteristic_client_; + scoped_ptr<BluetoothGattDescriptorClient> bluetooth_gatt_descriptor_client_; + scoped_ptr<BluetoothGattManagerClient> bluetooth_gatt_manager_client_; + scoped_ptr<BluetoothGattServiceClient> bluetooth_gatt_service_client_; + scoped_ptr<BluetoothInputClient> bluetooth_input_client_; + scoped_ptr<BluetoothMediaClient> bluetooth_media_client_; + scoped_ptr<BluetoothMediaTransportClient> bluetooth_media_transport_client_; + scoped_ptr<BluetoothProfileManagerClient> bluetooth_profile_manager_client_; scoped_ptr<CrasAudioClient> cras_audio_client_; scoped_ptr<CrosDisksClient> cros_disks_client_; scoped_ptr<CryptohomeClient> cryptohome_client_; diff --git a/chromeos/dbus/dbus_thread_manager.cc b/chromeos/dbus/dbus_thread_manager.cc index fe59021..1addc55 100644 --- a/chromeos/dbus/dbus_thread_manager.cc +++ b/chromeos/dbus/dbus_thread_manager.cc @@ -11,6 +11,18 @@ #include "chromeos/dbus/amplifier_client.h" #include "chromeos/dbus/ap_manager_client.h" #include "chromeos/dbus/audio_dsp_client.h" +#include "chromeos/dbus/bluetooth_adapter_client.h" +#include "chromeos/dbus/bluetooth_agent_manager_client.h" +#include "chromeos/dbus/bluetooth_device_client.h" +#include "chromeos/dbus/bluetooth_gatt_characteristic_client.h" +#include "chromeos/dbus/bluetooth_gatt_descriptor_client.h" +#include "chromeos/dbus/bluetooth_gatt_manager_client.h" +#include "chromeos/dbus/bluetooth_gatt_service_client.h" +#include "chromeos/dbus/bluetooth_input_client.h" +#include "chromeos/dbus/bluetooth_le_advertising_manager_client.h" +#include "chromeos/dbus/bluetooth_media_client.h" +#include "chromeos/dbus/bluetooth_media_transport_client.h" +#include "chromeos/dbus/bluetooth_profile_manager_client.h" #include "chromeos/dbus/cras_audio_client.h" #include "chromeos/dbus/cros_disks_client.h" #include "chromeos/dbus/cryptohome_client.h" @@ -115,6 +127,60 @@ AudioDspClient* DBusThreadManager::GetAudioDspClient() { return client_bundle_->audio_dsp_client(); } +BluetoothAdapterClient* DBusThreadManager::GetBluetoothAdapterClient() { + return client_bundle_->bluetooth_adapter_client(); +} + +BluetoothLEAdvertisingManagerClient* +DBusThreadManager::GetBluetoothLEAdvertisingManagerClient() { + return client_bundle_->bluetooth_le_advertising_manager_client(); +} + +BluetoothAgentManagerClient* +DBusThreadManager::GetBluetoothAgentManagerClient() { + return client_bundle_->bluetooth_agent_manager_client(); +} + +BluetoothDeviceClient* DBusThreadManager::GetBluetoothDeviceClient() { + return client_bundle_->bluetooth_device_client(); +} + +BluetoothGattCharacteristicClient* +DBusThreadManager::GetBluetoothGattCharacteristicClient() { + return client_bundle_->bluetooth_gatt_characteristic_client(); +} + +BluetoothGattDescriptorClient* +DBusThreadManager::GetBluetoothGattDescriptorClient() { + return client_bundle_->bluetooth_gatt_descriptor_client(); +} + +BluetoothGattManagerClient* DBusThreadManager::GetBluetoothGattManagerClient() { + return client_bundle_->bluetooth_gatt_manager_client(); +} + +BluetoothGattServiceClient* DBusThreadManager::GetBluetoothGattServiceClient() { + return client_bundle_->bluetooth_gatt_service_client(); +} + +BluetoothInputClient* DBusThreadManager::GetBluetoothInputClient() { + return client_bundle_->bluetooth_input_client(); +} + +BluetoothMediaClient* DBusThreadManager::GetBluetoothMediaClient() { + return client_bundle_->bluetooth_media_client(); +} + +BluetoothMediaTransportClient* +DBusThreadManager::GetBluetoothMediaTransportClient() { + return client_bundle_->bluetooth_media_transport_client(); +} + +BluetoothProfileManagerClient* +DBusThreadManager::GetBluetoothProfileManagerClient() { + return client_bundle_->bluetooth_profile_manager_client(); +} + CrasAudioClient* DBusThreadManager::GetCrasAudioClient() { return client_bundle_->cras_audio_client(); } @@ -242,6 +308,18 @@ void DBusThreadManager::InitializeClients() { GetAmplifierClient()->Init(GetSystemBus()); GetApManagerClient()->Init(GetSystemBus()); GetAudioDspClient()->Init(GetSystemBus()); + GetBluetoothAdapterClient()->Init(GetSystemBus()); + GetBluetoothAgentManagerClient()->Init(GetSystemBus()); + GetBluetoothDeviceClient()->Init(GetSystemBus()); + GetBluetoothGattCharacteristicClient()->Init(GetSystemBus()); + GetBluetoothGattDescriptorClient()->Init(GetSystemBus()); + GetBluetoothGattManagerClient()->Init(GetSystemBus()); + GetBluetoothGattServiceClient()->Init(GetSystemBus()); + GetBluetoothInputClient()->Init(GetSystemBus()); + GetBluetoothLEAdvertisingManagerClient()->Init(GetSystemBus()); + GetBluetoothMediaClient()->Init(GetSystemBus()); + GetBluetoothMediaTransportClient()->Init(GetSystemBus()); + GetBluetoothProfileManagerClient()->Init(GetSystemBus()); GetCrasAudioClient()->Init(GetSystemBus()); GetCrosDisksClient()->Init(GetSystemBus()); GetCryptohomeClient()->Init(GetSystemBus()); @@ -396,6 +474,79 @@ void DBusThreadManagerSetter::SetAudioDspClient( DBusThreadManager::Get()->client_bundle_->audio_dsp_client_ = client.Pass(); } +void DBusThreadManagerSetter::SetBluetoothAdapterClient( + scoped_ptr<BluetoothAdapterClient> client) { + DBusThreadManager::Get()->client_bundle_->bluetooth_adapter_client_ = + client.Pass(); +} + +void DBusThreadManagerSetter::SetBluetoothLEAdvertisingManagerClient( + scoped_ptr<BluetoothLEAdvertisingManagerClient> client) { + DBusThreadManager::Get() + ->client_bundle_->bluetooth_le_advertising_manager_client_ = + client.Pass(); +} + +void DBusThreadManagerSetter::SetBluetoothAgentManagerClient( + scoped_ptr<BluetoothAgentManagerClient> client) { + DBusThreadManager::Get()->client_bundle_->bluetooth_agent_manager_client_ = + client.Pass(); +} + +void DBusThreadManagerSetter::SetBluetoothDeviceClient( + scoped_ptr<BluetoothDeviceClient> client) { + DBusThreadManager::Get()->client_bundle_->bluetooth_device_client_ = + client.Pass(); +} + +void DBusThreadManagerSetter::SetBluetoothGattCharacteristicClient( + scoped_ptr<BluetoothGattCharacteristicClient> client) { + DBusThreadManager::Get() + ->client_bundle_->bluetooth_gatt_characteristic_client_ = client.Pass(); +} + +void DBusThreadManagerSetter::SetBluetoothGattDescriptorClient( + scoped_ptr<BluetoothGattDescriptorClient> client) { + DBusThreadManager::Get()->client_bundle_->bluetooth_gatt_descriptor_client_ = + client.Pass(); +} + +void DBusThreadManagerSetter::SetBluetoothGattManagerClient( + scoped_ptr<BluetoothGattManagerClient> client) { + DBusThreadManager::Get()->client_bundle_->bluetooth_gatt_manager_client_ = + client.Pass(); +} + +void DBusThreadManagerSetter::SetBluetoothGattServiceClient( + scoped_ptr<BluetoothGattServiceClient> client) { + DBusThreadManager::Get()->client_bundle_->bluetooth_gatt_service_client_ = + client.Pass(); +} + +void DBusThreadManagerSetter::SetBluetoothInputClient( + scoped_ptr<BluetoothInputClient> client) { + DBusThreadManager::Get()->client_bundle_->bluetooth_input_client_ = + client.Pass(); +} + +void DBusThreadManagerSetter::SetBluetoothMediaClient( + scoped_ptr<BluetoothMediaClient> client) { + DBusThreadManager::Get()->client_bundle_->bluetooth_media_client_ = + client.Pass(); +} + +void DBusThreadManagerSetter::SetBluetoothMediaTransportClient( + scoped_ptr<BluetoothMediaTransportClient> client) { + DBusThreadManager::Get()->client_bundle_->bluetooth_media_transport_client_ = + client.Pass(); +} + +void DBusThreadManagerSetter::SetBluetoothProfileManagerClient( + scoped_ptr<BluetoothProfileManagerClient> client) { + DBusThreadManager::Get()->client_bundle_->bluetooth_profile_manager_client_ = + client.Pass(); +} + void DBusThreadManagerSetter::SetCrasAudioClient( scoped_ptr<CrasAudioClient> client) { DBusThreadManager::Get()->client_bundle_->cras_audio_client_ = client.Pass(); diff --git a/chromeos/dbus/dbus_thread_manager.h b/chromeos/dbus/dbus_thread_manager.h index 83f3c26..322ab28 100644 --- a/chromeos/dbus/dbus_thread_manager.h +++ b/chromeos/dbus/dbus_thread_manager.h @@ -28,6 +28,18 @@ namespace chromeos { class AmplifierClient; class ApManagerClient; class AudioDspClient; +class BluetoothAdapterClient; +class BluetoothLEAdvertisingManagerClient; +class BluetoothAgentManagerClient; +class BluetoothDeviceClient; +class BluetoothGattCharacteristicClient; +class BluetoothGattDescriptorClient; +class BluetoothGattManagerClient; +class BluetoothGattServiceClient; +class BluetoothInputClient; +class BluetoothMediaClient; +class BluetoothMediaTransportClient; +class BluetoothProfileManagerClient; class CrasAudioClient; class CrosDisksClient; class CryptohomeClient; @@ -112,6 +124,18 @@ class CHROMEOS_EXPORT DBusThreadManager { AmplifierClient* GetAmplifierClient(); ApManagerClient* GetApManagerClient(); AudioDspClient* GetAudioDspClient(); + BluetoothAdapterClient* GetBluetoothAdapterClient(); + BluetoothLEAdvertisingManagerClient* GetBluetoothLEAdvertisingManagerClient(); + BluetoothAgentManagerClient* GetBluetoothAgentManagerClient(); + BluetoothDeviceClient* GetBluetoothDeviceClient(); + BluetoothGattCharacteristicClient* GetBluetoothGattCharacteristicClient(); + BluetoothGattDescriptorClient* GetBluetoothGattDescriptorClient(); + BluetoothGattManagerClient* GetBluetoothGattManagerClient(); + BluetoothGattServiceClient* GetBluetoothGattServiceClient(); + BluetoothInputClient* GetBluetoothInputClient(); + BluetoothMediaClient* GetBluetoothMediaClient(); + BluetoothMediaTransportClient* GetBluetoothMediaTransportClient(); + BluetoothProfileManagerClient* GetBluetoothProfileManagerClient(); CrasAudioClient* GetCrasAudioClient(); CrosDisksClient* GetCrosDisksClient(); CryptohomeClient* GetCryptohomeClient(); @@ -186,6 +210,26 @@ class CHROMEOS_EXPORT DBusThreadManagerSetter { void SetAmplifierClient(scoped_ptr<AmplifierClient> client); void SetAudioDspClient(scoped_ptr<AudioDspClient> client); + void SetBluetoothAdapterClient(scoped_ptr<BluetoothAdapterClient> client); + void SetBluetoothLEAdvertisingManagerClient( + scoped_ptr<BluetoothLEAdvertisingManagerClient> client); + void SetBluetoothAgentManagerClient( + scoped_ptr<BluetoothAgentManagerClient> client); + void SetBluetoothDeviceClient(scoped_ptr<BluetoothDeviceClient> client); + void SetBluetoothGattCharacteristicClient( + scoped_ptr<BluetoothGattCharacteristicClient> client); + void SetBluetoothGattDescriptorClient( + scoped_ptr<BluetoothGattDescriptorClient> client); + void SetBluetoothGattManagerClient( + scoped_ptr<BluetoothGattManagerClient> client); + void SetBluetoothGattServiceClient( + scoped_ptr<BluetoothGattServiceClient> client); + void SetBluetoothInputClient(scoped_ptr<BluetoothInputClient> client); + void SetBluetoothMediaClient(scoped_ptr<BluetoothMediaClient> client); + void SetBluetoothMediaTransportClient( + scoped_ptr<BluetoothMediaTransportClient> client); + void SetBluetoothProfileManagerClient( + scoped_ptr<BluetoothProfileManagerClient> client); void SetCrasAudioClient(scoped_ptr<CrasAudioClient> client); void SetCrosDisksClient(scoped_ptr<CrosDisksClient> client); void SetCryptohomeClient(scoped_ptr<CryptohomeClient> client); diff --git a/chromeos/dbus/fake_bluetooth_adapter_client.cc b/chromeos/dbus/fake_bluetooth_adapter_client.cc new file mode 100644 index 0000000..1ff4bcc --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_adapter_client.cc @@ -0,0 +1,296 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_adapter_client.h" + +#include "base/location.h" +#include "base/logging.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "base/time/time.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_device_client.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +namespace { + +// Default interval for delayed tasks. +const int kSimulationIntervalMs = 750; + +} // namespace + +const char FakeBluetoothAdapterClient::kAdapterPath[] = "/fake/hci0"; +const char FakeBluetoothAdapterClient::kAdapterName[] = "Fake Adapter"; +const char FakeBluetoothAdapterClient::kAdapterAddress[] = "01:1A:2B:1A:2B:03"; + +const char FakeBluetoothAdapterClient::kSecondAdapterPath[] = "/fake/hci1"; +const char FakeBluetoothAdapterClient::kSecondAdapterName[] = + "Second Fake Adapter"; +const char FakeBluetoothAdapterClient::kSecondAdapterAddress[] = + "00:DE:51:10:01:00"; + +FakeBluetoothAdapterClient::Properties::Properties( + const PropertyChangedCallback& callback) + : BluetoothAdapterClient::Properties( + NULL, + bluetooth_adapter::kBluetoothAdapterInterface, + callback) {} + +FakeBluetoothAdapterClient::Properties::~Properties() {} + +void FakeBluetoothAdapterClient::Properties::Get( + dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) { + VLOG(1) << "Get " << property->name(); + callback.Run(false); +} + +void FakeBluetoothAdapterClient::Properties::GetAll() { + VLOG(1) << "GetAll"; +} + +void FakeBluetoothAdapterClient::Properties::Set( + dbus::PropertyBase* property, + dbus::PropertySet::SetCallback callback) { + VLOG(1) << "Set " << property->name(); + if (property->name() == powered.name() || property->name() == alias.name() || + property->name() == discoverable.name() || + property->name() == discoverable_timeout.name()) { + callback.Run(true); + property->ReplaceValueWithSetValue(); + } else { + callback.Run(false); + } +} + +FakeBluetoothAdapterClient::FakeBluetoothAdapterClient() + : visible_(true), + second_visible_(false), + discovering_count_(0), + set_discovery_filter_should_fail_(false), + simulation_interval_ms_(kSimulationIntervalMs) { + properties_.reset(new Properties(base::Bind( + &FakeBluetoothAdapterClient::OnPropertyChanged, base::Unretained(this)))); + + properties_->address.ReplaceValue(kAdapterAddress); + properties_->name.ReplaceValue("Fake Adapter (Name)"); + properties_->alias.ReplaceValue(kAdapterName); + properties_->pairable.ReplaceValue(true); + + second_properties_.reset(new Properties(base::Bind( + &FakeBluetoothAdapterClient::OnPropertyChanged, base::Unretained(this)))); + + second_properties_->address.ReplaceValue(kSecondAdapterAddress); + second_properties_->name.ReplaceValue("Second Fake Adapter (Name)"); + second_properties_->alias.ReplaceValue(kSecondAdapterName); + second_properties_->pairable.ReplaceValue(true); +} + +FakeBluetoothAdapterClient::~FakeBluetoothAdapterClient() {} + +void FakeBluetoothAdapterClient::Init(dbus::Bus* bus) {} + +void FakeBluetoothAdapterClient::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void FakeBluetoothAdapterClient::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +std::vector<dbus::ObjectPath> FakeBluetoothAdapterClient::GetAdapters() { + std::vector<dbus::ObjectPath> object_paths; + if (visible_) + object_paths.push_back(dbus::ObjectPath(kAdapterPath)); + if (second_visible_) + object_paths.push_back(dbus::ObjectPath(kSecondAdapterPath)); + return object_paths; +} + +FakeBluetoothAdapterClient::Properties* +FakeBluetoothAdapterClient::GetProperties(const dbus::ObjectPath& object_path) { + if (object_path == dbus::ObjectPath(kAdapterPath)) + return properties_.get(); + else if (object_path == dbus::ObjectPath(kSecondAdapterPath)) + return second_properties_.get(); + else + return NULL; +} + +void FakeBluetoothAdapterClient::StartDiscovery( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + if (object_path != dbus::ObjectPath(kAdapterPath)) { + PostDelayedTask(base::Bind(error_callback, kNoResponseError, "")); + return; + } + + ++discovering_count_; + VLOG(1) << "StartDiscovery: " << object_path.value() << ", " + << "count is now " << discovering_count_; + PostDelayedTask(callback); + + if (discovering_count_ == 1) { + properties_->discovering.ReplaceValue(true); + + FakeBluetoothDeviceClient* device_client = + static_cast<FakeBluetoothDeviceClient*>( + DBusThreadManager::Get()->GetBluetoothDeviceClient()); + device_client->BeginDiscoverySimulation(dbus::ObjectPath(kAdapterPath)); + } +} + +void FakeBluetoothAdapterClient::StopDiscovery( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + if (object_path != dbus::ObjectPath(kAdapterPath)) { + PostDelayedTask(base::Bind(error_callback, kNoResponseError, "")); + return; + } + + if (!discovering_count_) { + LOG(WARNING) << "StopDiscovery called when not discovering"; + PostDelayedTask(base::Bind(error_callback, kNoResponseError, "")); + return; + } + + --discovering_count_; + VLOG(1) << "StopDiscovery: " << object_path.value() << ", " + << "count is now " << discovering_count_; + PostDelayedTask(callback); + + if (discovering_count_ == 0) { + FakeBluetoothDeviceClient* device_client = + static_cast<FakeBluetoothDeviceClient*>( + DBusThreadManager::Get()->GetBluetoothDeviceClient()); + device_client->EndDiscoverySimulation(dbus::ObjectPath(kAdapterPath)); + + if (simulation_interval_ms_ > 100) { + device_client->BeginIncomingPairingSimulation( + dbus::ObjectPath(kAdapterPath)); + } + + discovery_filter_.reset(); + properties_->discovering.ReplaceValue(false); + } +} + +void FakeBluetoothAdapterClient::RemoveDevice( + const dbus::ObjectPath& object_path, + const dbus::ObjectPath& device_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + if (object_path != dbus::ObjectPath(kAdapterPath)) { + error_callback.Run(kNoResponseError, ""); + return; + } + + VLOG(1) << "RemoveDevice: " << object_path.value() << " " + << device_path.value(); + callback.Run(); + + FakeBluetoothDeviceClient* device_client = + static_cast<FakeBluetoothDeviceClient*>( + DBusThreadManager::Get()->GetBluetoothDeviceClient()); + device_client->RemoveDevice(dbus::ObjectPath(kAdapterPath), device_path); +} + +void FakeBluetoothAdapterClient::MakeSetDiscoveryFilterFail() { + set_discovery_filter_should_fail_ = true; +} + +void FakeBluetoothAdapterClient::SetDiscoveryFilter( + const dbus::ObjectPath& object_path, + const DiscoveryFilter& discovery_filter, + const base::Closure& callback, + const ErrorCallback& error_callback) { + if (object_path != dbus::ObjectPath(kAdapterPath)) { + PostDelayedTask(base::Bind(error_callback, kNoResponseError, "")); + return; + } + VLOG(1) << "SetDiscoveryFilter: " << object_path.value(); + + if (set_discovery_filter_should_fail_) { + PostDelayedTask(base::Bind(error_callback, kNoResponseError, "")); + set_discovery_filter_should_fail_ = false; + return; + } + + discovery_filter_.reset(new DiscoveryFilter()); + discovery_filter_->CopyFrom(discovery_filter); + PostDelayedTask(callback); +} + +void FakeBluetoothAdapterClient::SetSimulationIntervalMs(int interval_ms) { + simulation_interval_ms_ = interval_ms; +} + +BluetoothAdapterClient::DiscoveryFilter* +FakeBluetoothAdapterClient::GetDiscoveryFilter() { + return discovery_filter_.get(); +} + +void FakeBluetoothAdapterClient::SetVisible(bool visible) { + if (visible && !visible_) { + // Adapter becoming visible + visible_ = visible; + + FOR_EACH_OBSERVER(BluetoothAdapterClient::Observer, observers_, + AdapterAdded(dbus::ObjectPath(kAdapterPath))); + + } else if (visible_ && !visible) { + // Adapter becoming invisible + visible_ = visible; + + FOR_EACH_OBSERVER(BluetoothAdapterClient::Observer, observers_, + AdapterRemoved(dbus::ObjectPath(kAdapterPath))); + } +} + +void FakeBluetoothAdapterClient::SetSecondVisible(bool visible) { + if (visible && !second_visible_) { + // Second adapter becoming visible + second_visible_ = visible; + + FOR_EACH_OBSERVER(BluetoothAdapterClient::Observer, observers_, + AdapterAdded(dbus::ObjectPath(kSecondAdapterPath))); + + } else if (second_visible_ && !visible) { + // Second adapter becoming invisible + second_visible_ = visible; + + FOR_EACH_OBSERVER(BluetoothAdapterClient::Observer, observers_, + AdapterRemoved(dbus::ObjectPath(kSecondAdapterPath))); + } +} + +void FakeBluetoothAdapterClient::OnPropertyChanged( + const std::string& property_name) { + if (property_name == properties_->powered.name() && + !properties_->powered.value()) { + VLOG(1) << "Adapter powered off"; + + if (discovering_count_) { + discovering_count_ = 0; + properties_->discovering.ReplaceValue(false); + } + } + + FOR_EACH_OBSERVER( + BluetoothAdapterClient::Observer, observers_, + AdapterPropertyChanged(dbus::ObjectPath(kAdapterPath), property_name)); +} + +void FakeBluetoothAdapterClient::PostDelayedTask( + const base::Closure& callback) { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, callback, + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_adapter_client.h b/chromeos/dbus/fake_bluetooth_adapter_client.h new file mode 100644 index 0000000..987fc6f --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_adapter_client.h @@ -0,0 +1,118 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_ADAPTER_CLIENT_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_ADAPTER_CLIENT_H_ + +#include <vector> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/observer_list.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_adapter_client.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +// FakeBluetoothAdapterClient simulates the behavior of the Bluetooth Daemon +// adapter objects and is used both in test cases in place of a mock and on +// the Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothAdapterClient + : public BluetoothAdapterClient { + public: + struct Properties : public BluetoothAdapterClient::Properties { + explicit Properties(const PropertyChangedCallback& callback); + ~Properties() override; + + // dbus::PropertySet override + void Get(dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) override; + void GetAll() override; + void Set(dbus::PropertyBase* property, + dbus::PropertySet::SetCallback callback) override; + }; + + FakeBluetoothAdapterClient(); + ~FakeBluetoothAdapterClient() override; + + // BluetoothAdapterClient overrides + void Init(dbus::Bus* bus) override; + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + std::vector<dbus::ObjectPath> GetAdapters() override; + Properties* GetProperties(const dbus::ObjectPath& object_path) override; + void StartDiscovery(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void StopDiscovery(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void RemoveDevice(const dbus::ObjectPath& object_path, + const dbus::ObjectPath& device_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void SetDiscoveryFilter(const dbus::ObjectPath& object_path, + const DiscoveryFilter& discovery_filter, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + + // Sets the current simulation timeout interval. + void SetSimulationIntervalMs(int interval_ms); + + // Returns current discovery filter in use by this adapter. + DiscoveryFilter* GetDiscoveryFilter(); + + // Make SetDiscoveryFilter fail when called next time. + void MakeSetDiscoveryFilterFail(); + + // Mark the adapter and second adapter as visible or invisible. + void SetVisible(bool visible); + void SetSecondVisible(bool visible); + + // Object path, name and addresses of the adapters we emulate. + static const char kAdapterPath[]; + static const char kAdapterName[]; + static const char kAdapterAddress[]; + + static const char kSecondAdapterPath[]; + static const char kSecondAdapterName[]; + static const char kSecondAdapterAddress[]; + + private: + // Property callback passed when we create Properties* structures. + void OnPropertyChanged(const std::string& property_name); + + // Posts the delayed task represented by |callback| onto the current + // message loop to be executed after |simulation_interval_ms_| milliseconds. + void PostDelayedTask(const base::Closure& callback); + + // List of observers interested in event notifications from us. + base::ObserverList<Observer> observers_; + + // Static properties we return. + scoped_ptr<Properties> properties_; + scoped_ptr<Properties> second_properties_; + + // Whether the adapter and second adapter should be visible or not. + bool visible_; + bool second_visible_; + + // Number of times we've been asked to discover. + int discovering_count_; + + // Current discovery filter + scoped_ptr<DiscoveryFilter> discovery_filter_; + + // When set, next call to SetDiscoveryFilter would fail. + bool set_discovery_filter_should_fail_; + + // Current timeout interval used when posting delayed tasks. + int simulation_interval_ms_; +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_ADAPTER_CLIENT_H_ diff --git a/chromeos/dbus/fake_bluetooth_agent_manager_client.cc b/chromeos/dbus/fake_bluetooth_agent_manager_client.cc new file mode 100644 index 0000000..4ced274 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_agent_manager_client.cc @@ -0,0 +1,78 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_agent_manager_client.h" + +#include "base/logging.h" +#include "chromeos/dbus/fake_bluetooth_agent_service_provider.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +FakeBluetoothAgentManagerClient::FakeBluetoothAgentManagerClient() + : service_provider_(NULL) {} + +FakeBluetoothAgentManagerClient::~FakeBluetoothAgentManagerClient() {} + +void FakeBluetoothAgentManagerClient::Init(dbus::Bus* bus) {} + +void FakeBluetoothAgentManagerClient::RegisterAgent( + const dbus::ObjectPath& agent_path, + const std::string& capability, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "RegisterAgent: " << agent_path.value(); + + if (service_provider_ == NULL) { + error_callback.Run(bluetooth_agent_manager::kErrorInvalidArguments, + "No agent created"); + } else if (service_provider_->object_path_ != agent_path) { + error_callback.Run(bluetooth_agent_manager::kErrorAlreadyExists, + "Agent already registered"); + } else { + callback.Run(); + } +} + +void FakeBluetoothAgentManagerClient::UnregisterAgent( + const dbus::ObjectPath& agent_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "UnregisterAgent: " << agent_path.value(); + if (service_provider_ == NULL) { + error_callback.Run(bluetooth_agent_manager::kErrorDoesNotExist, + "No agent registered"); + } else if (service_provider_->object_path_ != agent_path) { + error_callback.Run(bluetooth_agent_manager::kErrorDoesNotExist, + "Agent still registered"); + } else { + callback.Run(); + } +} + +void FakeBluetoothAgentManagerClient::RequestDefaultAgent( + const dbus::ObjectPath& agent_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "RequestDefaultAgent: " << agent_path.value(); + callback.Run(); +} + +void FakeBluetoothAgentManagerClient::RegisterAgentServiceProvider( + FakeBluetoothAgentServiceProvider* service_provider) { + service_provider_ = service_provider; +} + +void FakeBluetoothAgentManagerClient::UnregisterAgentServiceProvider( + FakeBluetoothAgentServiceProvider* service_provider) { + if (service_provider_ == service_provider) + service_provider_ = NULL; +} + +FakeBluetoothAgentServiceProvider* +FakeBluetoothAgentManagerClient::GetAgentServiceProvider() { + return service_provider_; +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_agent_manager_client.h b/chromeos/dbus/fake_bluetooth_agent_manager_client.h new file mode 100644 index 0000000..68c91202c3 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_agent_manager_client.h @@ -0,0 +1,57 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_AGENT_MANAGER_CLIENT_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_AGENT_MANAGER_CLIENT_H_ + +#include "base/bind.h" +#include "base/callback.h" +#include "base/observer_list.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_agent_manager_client.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +class FakeBluetoothAgentServiceProvider; + +// FakeBluetoothAgentManagerClient simulates the behavior of the Bluetooth +// Daemon's agent manager object and is used both in test cases in place of a +// mock and on the Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothAgentManagerClient + : public BluetoothAgentManagerClient { + public: + FakeBluetoothAgentManagerClient(); + ~FakeBluetoothAgentManagerClient() override; + + // BluetoothAgentManagerClient overrides + void Init(dbus::Bus* bus) override; + void RegisterAgent(const dbus::ObjectPath& agent_path, + const std::string& capability, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void UnregisterAgent(const dbus::ObjectPath& agent_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void RequestDefaultAgent(const dbus::ObjectPath& agent_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + + // Register, unregister and retrieve pointers to agent service providers. + void RegisterAgentServiceProvider( + FakeBluetoothAgentServiceProvider* service_provider); + void UnregisterAgentServiceProvider( + FakeBluetoothAgentServiceProvider* service_provider); + FakeBluetoothAgentServiceProvider* GetAgentServiceProvider(); + + private: + // The single agent service provider we permit, owned by the application + // using it. + FakeBluetoothAgentServiceProvider* service_provider_; +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_AGENT_MANAGER_CLIENT_H_ diff --git a/chromeos/dbus/fake_bluetooth_agent_service_provider.cc b/chromeos/dbus/fake_bluetooth_agent_service_provider.cc new file mode 100644 index 0000000..de2dbd3 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_agent_service_provider.cc @@ -0,0 +1,102 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_agent_service_provider.h" + +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_agent_manager_client.h" + +namespace chromeos { + +FakeBluetoothAgentServiceProvider::FakeBluetoothAgentServiceProvider( + const dbus::ObjectPath& object_path, + Delegate* delegate) + : object_path_(object_path), delegate_(delegate) { + VLOG(1) << "Creating Bluetooth Agent: " << object_path_.value(); + + FakeBluetoothAgentManagerClient* fake_bluetooth_agent_manager_client = + static_cast<FakeBluetoothAgentManagerClient*>( + DBusThreadManager::Get()->GetBluetoothAgentManagerClient()); + fake_bluetooth_agent_manager_client->RegisterAgentServiceProvider(this); +} + +FakeBluetoothAgentServiceProvider::~FakeBluetoothAgentServiceProvider() { + VLOG(1) << "Cleaning up Bluetooth Agent: " << object_path_.value(); + + FakeBluetoothAgentManagerClient* fake_bluetooth_agent_manager_client = + static_cast<FakeBluetoothAgentManagerClient*>( + DBusThreadManager::Get()->GetBluetoothAgentManagerClient()); + fake_bluetooth_agent_manager_client->UnregisterAgentServiceProvider(this); +} + +void FakeBluetoothAgentServiceProvider::Release() { + VLOG(1) << object_path_.value() << ": Release"; + delegate_->Released(); +} + +void FakeBluetoothAgentServiceProvider::RequestPinCode( + const dbus::ObjectPath& device_path, + const Delegate::PinCodeCallback& callback) { + VLOG(1) << object_path_.value() << ": RequestPinCode for " + << device_path.value(); + delegate_->RequestPinCode(device_path, callback); +} + +void FakeBluetoothAgentServiceProvider::DisplayPinCode( + const dbus::ObjectPath& device_path, + const std::string& pincode) { + VLOG(1) << object_path_.value() << ": DisplayPincode " << pincode << " for " + << device_path.value(); + delegate_->DisplayPinCode(device_path, pincode); +} + +void FakeBluetoothAgentServiceProvider::RequestPasskey( + const dbus::ObjectPath& device_path, + const Delegate::PasskeyCallback& callback) { + VLOG(1) << object_path_.value() << ": RequestPasskey for " + << device_path.value(); + delegate_->RequestPasskey(device_path, callback); +} + +void FakeBluetoothAgentServiceProvider::DisplayPasskey( + const dbus::ObjectPath& device_path, + uint32 passkey, + int16 entered) { + VLOG(1) << object_path_.value() << ": DisplayPasskey " << passkey << " (" + << entered << " entered) for " << device_path.value(); + delegate_->DisplayPasskey(device_path, passkey, entered); +} + +void FakeBluetoothAgentServiceProvider::RequestConfirmation( + const dbus::ObjectPath& device_path, + uint32 passkey, + const Delegate::ConfirmationCallback& callback) { + VLOG(1) << object_path_.value() << ": RequestConfirmation " << passkey + << " for " << device_path.value(); + delegate_->RequestConfirmation(device_path, passkey, callback); +} + +void FakeBluetoothAgentServiceProvider::RequestAuthorization( + const dbus::ObjectPath& device_path, + const Delegate::ConfirmationCallback& callback) { + VLOG(1) << object_path_.value() << ": RequestAuthorization for " + << device_path.value(); + delegate_->RequestAuthorization(device_path, callback); +} + +void FakeBluetoothAgentServiceProvider::AuthorizeService( + const dbus::ObjectPath& device_path, + const std::string& uuid, + const Delegate::ConfirmationCallback& callback) { + VLOG(1) << object_path_.value() << ": AuthorizeService " << uuid << " for " + << device_path.value(); + delegate_->AuthorizeService(device_path, uuid, callback); +} + +void FakeBluetoothAgentServiceProvider::Cancel() { + VLOG(1) << object_path_.value() << ": Cancel"; + delegate_->Cancel(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_agent_service_provider.h b/chromeos/dbus/fake_bluetooth_agent_service_provider.h new file mode 100644 index 0000000..6d17519 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_agent_service_provider.h @@ -0,0 +1,68 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_AGENT_SERVICE_PROVIDER_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_AGENT_SERVICE_PROVIDER_H_ + +#include "base/bind.h" +#include "base/callback.h" +#include "base/observer_list.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_agent_service_provider.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +class FakeBluetoothAgentManagerClient; + +// FakeBluetoothAgentServiceProvider simulates the behavior of a local +// Bluetooth agent object and is used both in test cases in place of a +// mock and on the Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothAgentServiceProvider + : public BluetoothAgentServiceProvider { + public: + FakeBluetoothAgentServiceProvider(const dbus::ObjectPath& object_path, + Delegate* delegate); + ~FakeBluetoothAgentServiceProvider() override; + + // Each of these calls the equivalent BluetoothAgentServiceProvider::Delegate + // method on the object passed on construction. + virtual void Release(); + virtual void RequestPinCode(const dbus::ObjectPath& device_path, + const Delegate::PinCodeCallback& callback); + virtual void DisplayPinCode(const dbus::ObjectPath& device_path, + const std::string& pincode); + virtual void RequestPasskey(const dbus::ObjectPath& device_path, + const Delegate::PasskeyCallback& callback); + virtual void DisplayPasskey(const dbus::ObjectPath& device_path, + uint32 passkey, + int16 entered); + virtual void RequestConfirmation( + const dbus::ObjectPath& device_path, + uint32 passkey, + const Delegate::ConfirmationCallback& callback); + virtual void RequestAuthorization( + const dbus::ObjectPath& device_path, + const Delegate::ConfirmationCallback& callback); + virtual void AuthorizeService(const dbus::ObjectPath& device_path, + const std::string& uuid, + const Delegate::ConfirmationCallback& callback); + virtual void Cancel(); + + private: + friend class FakeBluetoothAgentManagerClient; + + // D-Bus object path we are faking. + dbus::ObjectPath object_path_; + + // All incoming method calls are passed on to the Delegate and a callback + // passed to generate the reply. |delegate_| is generally the object that + // owns this one, and must outlive it. + Delegate* delegate_; +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_AGENT_SERVICE_PROVIDER_H_ diff --git a/chromeos/dbus/fake_bluetooth_device_client.cc b/chromeos/dbus/fake_bluetooth_device_client.cc new file mode 100644 index 0000000..b910b37 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_device_client.cc @@ -0,0 +1,1661 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_device_client.h" + +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include <algorithm> +#include <string> +#include <utility> + +#include "base/location.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/rand_util.h" +#include "base/single_thread_task_runner.h" +#include "base/stl_util.h" +#include "base/thread_task_runner_handle.h" +#include "base/threading/worker_pool.h" +#include "base/time/time.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_adapter_client.h" +#include "chromeos/dbus/fake_bluetooth_agent_manager_client.h" +#include "chromeos/dbus/fake_bluetooth_agent_service_provider.h" +#include "chromeos/dbus/fake_bluetooth_gatt_service_client.h" +#include "chromeos/dbus/fake_bluetooth_input_client.h" +#include "chromeos/dbus/fake_bluetooth_profile_manager_client.h" +#include "chromeos/dbus/fake_bluetooth_profile_service_provider.h" +#include "dbus/file_descriptor.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace { + +// Default interval between simulated events. +const int kSimulationIntervalMs = 750; + +// Minimum and maximum bounds for randomly generated RSSI values. +const int kMinRSSI = -90; +const int kMaxRSSI = -30; + +// The default value of connection info properties from GetConnInfo(). +const int kUnkownPower = 127; + +// This is meant to delay the removal of a pre defined device until the +// developer has time to see it. +const int kVanishingDevicePairTimeMultiplier = 4; + +// Meant to delay a pair request for an observable amount of time. +const int kIncomingSimulationPairTimeMultiplier = 45; + +// Meant to delay a request that asks for pair requests for an observable +// amount of time. +const int kIncomingSimulationStartPairTimeMultiplier = 30; + +// This allows the PIN code dialog to be shown for a long enough time to see +// the PIN code UI in detail. +const int kPinCodeDevicePairTimeMultiplier = 7; + +// This allows the pairing dialog to be shown for a long enough time to see +// its UI in detail. +const int kSimulateNormalPairTimeMultiplier = 3; + +void SimulatedProfileSocket(int fd) { + // Simulate a server-side socket of a profile; read data from the socket, + // write it back, and then close. + char buf[1024]; + ssize_t len; + ssize_t count; + + len = read(fd, buf, sizeof buf); + if (len < 0) { + close(fd); + return; + } + + count = len; + len = write(fd, buf, count); + if (len < 0) { + close(fd); + return; + } + + close(fd); +} + +void SimpleErrorCallback(const std::string& error_name, + const std::string& error_message) { + VLOG(1) << "Bluetooth Error: " << error_name << ": " << error_message; +} + +} // namespace + +namespace chromeos { + +const char FakeBluetoothDeviceClient::kTestPinCode[] = "123456"; +const int FakeBluetoothDeviceClient::kTestPassKey = 123456; + +const char FakeBluetoothDeviceClient::kPairingMethodNone[] = "None"; +const char FakeBluetoothDeviceClient::kPairingMethodPinCode[] = "PIN Code"; +const char FakeBluetoothDeviceClient::kPairingMethodPassKey[] = "PassKey"; + +const char FakeBluetoothDeviceClient::kPairingActionConfirmation[] = + "Confirmation"; +const char FakeBluetoothDeviceClient::kPairingActionDisplay[] = "Display"; +const char FakeBluetoothDeviceClient::kPairingActionFail[] = "Fail"; +const char FakeBluetoothDeviceClient::kPairingActionRequest[] = "Request"; + +const char FakeBluetoothDeviceClient::kPairedDevicePath[] = "/fake/hci0/dev0"; +const char FakeBluetoothDeviceClient::kPairedDeviceAddress[] = + "00:11:22:33:44:55"; +const char FakeBluetoothDeviceClient::kPairedDeviceName[] = "Fake Device"; +const uint32 FakeBluetoothDeviceClient::kPairedDeviceClass = 0x000104; + +const char FakeBluetoothDeviceClient::kLegacyAutopairPath[] = "/fake/hci0/dev1"; +const char FakeBluetoothDeviceClient::kLegacyAutopairAddress[] = + "28:CF:DA:00:00:00"; +const char FakeBluetoothDeviceClient::kLegacyAutopairName[] = + "Bluetooth 2.0 Mouse"; +const uint32 FakeBluetoothDeviceClient::kLegacyAutopairClass = 0x002580; + +const char FakeBluetoothDeviceClient::kDisplayPinCodePath[] = "/fake/hci0/dev2"; +const char FakeBluetoothDeviceClient::kDisplayPinCodeAddress[] = + "28:37:37:00:00:00"; +const char FakeBluetoothDeviceClient::kDisplayPinCodeName[] = + "Bluetooth 2.0 Keyboard"; +const uint32 FakeBluetoothDeviceClient::kDisplayPinCodeClass = 0x002540; + +const char FakeBluetoothDeviceClient::kVanishingDevicePath[] = + "/fake/hci0/dev3"; +const char FakeBluetoothDeviceClient::kVanishingDeviceAddress[] = + "01:02:03:04:05:06"; +const char FakeBluetoothDeviceClient::kVanishingDeviceName[] = + "Vanishing Device"; +const uint32 FakeBluetoothDeviceClient::kVanishingDeviceClass = 0x000104; + +const char FakeBluetoothDeviceClient::kConnectUnpairablePath[] = + "/fake/hci0/dev4"; +const char FakeBluetoothDeviceClient::kConnectUnpairableAddress[] = + "7C:ED:8D:00:00:00"; +const char FakeBluetoothDeviceClient::kConnectUnpairableName[] = + "Unpairable Device"; +const uint32 FakeBluetoothDeviceClient::kConnectUnpairableClass = 0x002580; + +const char FakeBluetoothDeviceClient::kDisplayPasskeyPath[] = "/fake/hci0/dev5"; +const char FakeBluetoothDeviceClient::kDisplayPasskeyAddress[] = + "00:0F:F6:00:00:00"; +const char FakeBluetoothDeviceClient::kDisplayPasskeyName[] = + "Bluetooth 2.1+ Keyboard"; +const uint32 FakeBluetoothDeviceClient::kDisplayPasskeyClass = 0x002540; + +const char FakeBluetoothDeviceClient::kRequestPinCodePath[] = "/fake/hci0/dev6"; +const char FakeBluetoothDeviceClient::kRequestPinCodeAddress[] = + "00:24:BE:00:00:00"; +const char FakeBluetoothDeviceClient::kRequestPinCodeName[] = "PIN Device"; +const uint32 FakeBluetoothDeviceClient::kRequestPinCodeClass = 0x240408; + +const char FakeBluetoothDeviceClient::kConfirmPasskeyPath[] = "/fake/hci0/dev7"; +const char FakeBluetoothDeviceClient::kConfirmPasskeyAddress[] = + "20:7D:74:00:00:00"; +const char FakeBluetoothDeviceClient::kConfirmPasskeyName[] = "Phone"; +const uint32 FakeBluetoothDeviceClient::kConfirmPasskeyClass = 0x7a020c; + +const char FakeBluetoothDeviceClient::kRequestPasskeyPath[] = "/fake/hci0/dev8"; +const char FakeBluetoothDeviceClient::kRequestPasskeyAddress[] = + "20:7D:74:00:00:01"; +const char FakeBluetoothDeviceClient::kRequestPasskeyName[] = "Passkey Device"; +const uint32 FakeBluetoothDeviceClient::kRequestPasskeyClass = 0x7a020c; + +const char FakeBluetoothDeviceClient::kUnconnectableDevicePath[] = + "/fake/hci0/dev9"; +const char FakeBluetoothDeviceClient::kUnconnectableDeviceAddress[] = + "20:7D:74:00:00:02"; +const char FakeBluetoothDeviceClient::kUnconnectableDeviceName[] = + "Unconnectable Device"; +const uint32 FakeBluetoothDeviceClient::kUnconnectableDeviceClass = 0x7a020c; + +const char FakeBluetoothDeviceClient::kUnpairableDevicePath[] = + "/fake/hci0/devA"; +const char FakeBluetoothDeviceClient::kUnpairableDeviceAddress[] = + "20:7D:74:00:00:03"; +const char FakeBluetoothDeviceClient::kUnpairableDeviceName[] = + "Unpairable Device"; +const uint32 FakeBluetoothDeviceClient::kUnpairableDeviceClass = 0x002540; + +const char FakeBluetoothDeviceClient::kJustWorksPath[] = "/fake/hci0/devB"; +const char FakeBluetoothDeviceClient::kJustWorksAddress[] = "00:0C:8A:00:00:00"; +const char FakeBluetoothDeviceClient::kJustWorksName[] = "Just-Works Device"; +const uint32 FakeBluetoothDeviceClient::kJustWorksClass = 0x240428; + +const char FakeBluetoothDeviceClient::kLowEnergyPath[] = "/fake/hci0/devC"; +const char FakeBluetoothDeviceClient::kLowEnergyAddress[] = "00:1A:11:00:15:30"; +const char FakeBluetoothDeviceClient::kLowEnergyName[] = + "Bluetooth 4.0 Heart Rate Monitor"; +const uint32 FakeBluetoothDeviceClient::kLowEnergyClass = + 0x000918; // Major class "Health", Minor class "Heart/Pulse Rate Monitor." + +const char FakeBluetoothDeviceClient::kPairedUnconnectableDevicePath[] = + "/fake/hci0/devD"; +const char FakeBluetoothDeviceClient::kPairedUnconnectableDeviceAddress[] = + "20:7D:74:00:00:04"; +const char FakeBluetoothDeviceClient::kPairedUnconnectableDeviceName[] = + "Paired Unconnectable Device"; +const uint32 FakeBluetoothDeviceClient::kPairedUnconnectableDeviceClass = + 0x000104; + +const char FakeBluetoothDeviceClient::kConnectedTrustedNotPairedDevicePath[] = + "/fake/hci0/devE"; +const char + FakeBluetoothDeviceClient::kConnectedTrustedNotPairedDeviceAddress[] = + "11:22:33:44:55:66"; +const char FakeBluetoothDeviceClient::kConnectedTrustedNotPairedDeviceName[] = + "Connected Pairable Device"; +const uint32 FakeBluetoothDeviceClient::kConnectedTrustedNotPairedDeviceClass = + 0x7a020c; + +FakeBluetoothDeviceClient::Properties::Properties( + const PropertyChangedCallback& callback) + : BluetoothDeviceClient::Properties( + NULL, + bluetooth_device::kBluetoothDeviceInterface, + callback) {} + +FakeBluetoothDeviceClient::Properties::~Properties() {} + +void FakeBluetoothDeviceClient::Properties::Get( + dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) { + VLOG(1) << "Get " << property->name(); + callback.Run(false); +} + +void FakeBluetoothDeviceClient::Properties::GetAll() { + VLOG(1) << "GetAll"; +} + +void FakeBluetoothDeviceClient::Properties::Set( + dbus::PropertyBase* property, + dbus::PropertySet::SetCallback callback) { + VLOG(1) << "Set " << property->name(); + if (property->name() == trusted.name()) { + callback.Run(true); + property->ReplaceValueWithSetValue(); + } else { + callback.Run(false); + } +} + +FakeBluetoothDeviceClient::SimulatedPairingOptions::SimulatedPairingOptions() {} + +FakeBluetoothDeviceClient::SimulatedPairingOptions::~SimulatedPairingOptions() { +} + +FakeBluetoothDeviceClient::IncomingDeviceProperties:: + IncomingDeviceProperties() {} + +FakeBluetoothDeviceClient::IncomingDeviceProperties:: + ~IncomingDeviceProperties() {} + +FakeBluetoothDeviceClient::FakeBluetoothDeviceClient() + : simulation_interval_ms_(kSimulationIntervalMs), + discovery_simulation_step_(0), + incoming_pairing_simulation_step_(0), + pairing_cancelled_(false), + connection_rssi_(kUnkownPower), + transmit_power_(kUnkownPower), + max_transmit_power_(kUnkownPower) { + scoped_ptr<Properties> properties(new Properties( + base::Bind(&FakeBluetoothDeviceClient::OnPropertyChanged, + base::Unretained(this), dbus::ObjectPath(kPairedDevicePath)))); + properties->address.ReplaceValue(kPairedDeviceAddress); + properties->bluetooth_class.ReplaceValue(kPairedDeviceClass); + properties->name.ReplaceValue("Fake Device (Name)"); + properties->alias.ReplaceValue(kPairedDeviceName); + properties->paired.ReplaceValue(true); + properties->trusted.ReplaceValue(true); + properties->adapter.ReplaceValue( + dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); + + std::vector<std::string> uuids; + uuids.push_back("00001800-0000-1000-8000-00805f9b34fb"); + uuids.push_back("00001801-0000-1000-8000-00805f9b34fb"); + properties->uuids.ReplaceValue(uuids); + + properties->modalias.ReplaceValue("usb:v05ACp030Dd0306"); + + properties_map_.insert(dbus::ObjectPath(kPairedDevicePath), + properties.Pass()); + device_list_.push_back(dbus::ObjectPath(kPairedDevicePath)); + + properties.reset(new Properties(base::Bind( + &FakeBluetoothDeviceClient::OnPropertyChanged, base::Unretained(this), + dbus::ObjectPath(kPairedUnconnectableDevicePath)))); + properties->address.ReplaceValue(kPairedUnconnectableDeviceAddress); + properties->bluetooth_class.ReplaceValue(kPairedUnconnectableDeviceClass); + properties->name.ReplaceValue("Fake Device 2 (Unconnectable)"); + properties->alias.ReplaceValue(kPairedUnconnectableDeviceName); + properties->paired.ReplaceValue(true); + properties->trusted.ReplaceValue(true); + properties->adapter.ReplaceValue( + dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)); + + properties->uuids.ReplaceValue(uuids); + + properties->modalias.ReplaceValue("usb:v05ACp030Dd0306"); + + properties_map_.insert(dbus::ObjectPath(kPairedUnconnectableDevicePath), + properties.Pass()); + device_list_.push_back(dbus::ObjectPath(kPairedUnconnectableDevicePath)); +} + +FakeBluetoothDeviceClient::~FakeBluetoothDeviceClient() {} + +void FakeBluetoothDeviceClient::Init(dbus::Bus* bus) {} + +void FakeBluetoothDeviceClient::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void FakeBluetoothDeviceClient::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +std::vector<dbus::ObjectPath> FakeBluetoothDeviceClient::GetDevicesForAdapter( + const dbus::ObjectPath& adapter_path) { + if (adapter_path == + dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)) + return device_list_; + else + return std::vector<dbus::ObjectPath>(); +} + +FakeBluetoothDeviceClient::Properties* FakeBluetoothDeviceClient::GetProperties( + const dbus::ObjectPath& object_path) { + PropertiesMap::const_iterator iter = properties_map_.find(object_path); + if (iter != properties_map_.end()) + return iter->second; + return NULL; +} + +FakeBluetoothDeviceClient::SimulatedPairingOptions* +FakeBluetoothDeviceClient::GetPairingOptions( + const dbus::ObjectPath& object_path) { + PairingOptionsMap::const_iterator iter = + pairing_options_map_.find(object_path); + if (iter != pairing_options_map_.end()) + return iter->second; + return iter != pairing_options_map_.end() ? iter->second : nullptr; +} + +void FakeBluetoothDeviceClient::Connect(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "Connect: " << object_path.value(); + Properties* properties = GetProperties(object_path); + + if (properties->connected.value() == true) { + // Already connected. + callback.Run(); + return; + } + + if (properties->paired.value() != true && + object_path != dbus::ObjectPath(kConnectUnpairablePath) && + object_path != dbus::ObjectPath(kLowEnergyPath)) { + // Must be paired. + error_callback.Run(bluetooth_device::kErrorFailed, "Not paired"); + return; + } else if (properties->paired.value() == true && + (object_path == dbus::ObjectPath(kUnconnectableDevicePath) || + object_path == + dbus::ObjectPath(kPairedUnconnectableDevicePath))) { + // Must not be paired + error_callback.Run(bluetooth_device::kErrorFailed, + "Connection fails while paired"); + return; + } + + // The device can be connected. + properties->connected.ReplaceValue(true); + callback.Run(); + + // Expose GATT services if connected to LE device. + if (object_path == dbus::ObjectPath(kLowEnergyPath)) { + FakeBluetoothGattServiceClient* gatt_service_client = + static_cast<FakeBluetoothGattServiceClient*>( + DBusThreadManager::Get()->GetBluetoothGattServiceClient()); + gatt_service_client->ExposeHeartRateService( + dbus::ObjectPath(kLowEnergyPath)); + } + + AddInputDeviceIfNeeded(object_path, properties); +} + +void FakeBluetoothDeviceClient::Disconnect( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "Disconnect: " << object_path.value(); + Properties* properties = GetProperties(object_path); + + if (!properties->connected.value()) { + error_callback.Run("org.bluez.Error.NotConnected", "Not Connected"); + return; + } + + // Hide the Heart Rate Service if disconnected from LE device. + if (object_path == dbus::ObjectPath(kLowEnergyPath)) { + FakeBluetoothGattServiceClient* gatt_service_client = + static_cast<FakeBluetoothGattServiceClient*>( + DBusThreadManager::Get()->GetBluetoothGattServiceClient()); + gatt_service_client->HideHeartRateService(); + } + + callback.Run(); + properties->connected.ReplaceValue(false); +} + +void FakeBluetoothDeviceClient::ConnectProfile( + const dbus::ObjectPath& object_path, + const std::string& uuid, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "ConnectProfile: " << object_path.value() << " " << uuid; + + FakeBluetoothProfileManagerClient* fake_bluetooth_profile_manager_client = + static_cast<FakeBluetoothProfileManagerClient*>( + DBusThreadManager::Get()->GetBluetoothProfileManagerClient()); + FakeBluetoothProfileServiceProvider* profile_service_provider = + fake_bluetooth_profile_manager_client->GetProfileServiceProvider(uuid); + if (profile_service_provider == NULL) { + error_callback.Run(kNoResponseError, "Missing profile"); + return; + } + + if (object_path == dbus::ObjectPath(kPairedUnconnectableDevicePath)) { + error_callback.Run(bluetooth_device::kErrorFailed, "unconnectable"); + return; + } + + // Make a socket pair of a compatible type with the type used by Bluetooth; + // spin up a thread to simulate the server side and wrap the client side in + // a D-Bus file descriptor object. + int socket_type = SOCK_STREAM; + if (uuid == FakeBluetoothProfileManagerClient::kL2capUuid) + socket_type = SOCK_SEQPACKET; + + int fds[2]; + if (socketpair(AF_UNIX, socket_type, 0, fds) < 0) { + error_callback.Run(kNoResponseError, "socketpair call failed"); + return; + } + + int args; + args = fcntl(fds[1], F_GETFL, NULL); + if (args < 0) { + error_callback.Run(kNoResponseError, "failed to get socket flags"); + return; + } + + args |= O_NONBLOCK; + if (fcntl(fds[1], F_SETFL, args) < 0) { + error_callback.Run(kNoResponseError, "failed to set socket non-blocking"); + return; + } + + base::WorkerPool::GetTaskRunner(false) + ->PostTask(FROM_HERE, base::Bind(&SimulatedProfileSocket, fds[0])); + + scoped_ptr<dbus::FileDescriptor> fd(new dbus::FileDescriptor(fds[1])); + + // Post the new connection to the service provider. + BluetoothProfileServiceProvider::Delegate::Options options; + + profile_service_provider->NewConnection( + object_path, fd.Pass(), options, + base::Bind(&FakeBluetoothDeviceClient::ConnectionCallback, + base::Unretained(this), object_path, callback, + error_callback)); +} + +void FakeBluetoothDeviceClient::DisconnectProfile( + const dbus::ObjectPath& object_path, + const std::string& uuid, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "DisconnectProfile: " << object_path.value() << " " << uuid; + + FakeBluetoothProfileManagerClient* fake_bluetooth_profile_manager_client = + static_cast<FakeBluetoothProfileManagerClient*>( + DBusThreadManager::Get()->GetBluetoothProfileManagerClient()); + FakeBluetoothProfileServiceProvider* profile_service_provider = + fake_bluetooth_profile_manager_client->GetProfileServiceProvider(uuid); + if (profile_service_provider == NULL) { + error_callback.Run(kNoResponseError, "Missing profile"); + return; + } + + profile_service_provider->RequestDisconnection( + object_path, base::Bind(&FakeBluetoothDeviceClient::DisconnectionCallback, + base::Unretained(this), object_path, callback, + error_callback)); +} + +void FakeBluetoothDeviceClient::Pair(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "Pair: " << object_path.value(); + Properties* properties = GetProperties(object_path); + + if (properties->paired.value() == true) { + // Already paired. + callback.Run(); + return; + } + + SimulatePairing(object_path, false, callback, error_callback); +} + +void FakeBluetoothDeviceClient::CancelPairing( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "CancelPairing: " << object_path.value(); + pairing_cancelled_ = true; + callback.Run(); +} + +void FakeBluetoothDeviceClient::GetConnInfo( + const dbus::ObjectPath& object_path, + const ConnInfoCallback& callback, + const ErrorCallback& error_callback) { + Properties* properties = GetProperties(object_path); + if (!properties->connected.value()) { + error_callback.Run("org.bluez.Error.NotConnected", "Not Connected"); + return; + } + + callback.Run(connection_rssi_, transmit_power_, max_transmit_power_); +} + +void FakeBluetoothDeviceClient::BeginDiscoverySimulation( + const dbus::ObjectPath& adapter_path) { + VLOG(1) << "starting discovery simulation"; + + discovery_simulation_step_ = 1; + + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::DiscoverySimulationTimer, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); +} + +void FakeBluetoothDeviceClient::EndDiscoverySimulation( + const dbus::ObjectPath& adapter_path) { + VLOG(1) << "stopping discovery simulation"; + discovery_simulation_step_ = 0; +} + +void FakeBluetoothDeviceClient::BeginIncomingPairingSimulation( + const dbus::ObjectPath& adapter_path) { + VLOG(1) << "starting incoming pairing simulation"; + + incoming_pairing_simulation_step_ = 1; + + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::IncomingPairingSimulationTimer, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds( + kIncomingSimulationStartPairTimeMultiplier * + simulation_interval_ms_)); +} + +void FakeBluetoothDeviceClient::EndIncomingPairingSimulation( + const dbus::ObjectPath& adapter_path) { + VLOG(1) << "stopping incoming pairing simulation"; + incoming_pairing_simulation_step_ = 0; +} + +void FakeBluetoothDeviceClient::SetSimulationIntervalMs(int interval_ms) { + simulation_interval_ms_ = interval_ms; +} + +void FakeBluetoothDeviceClient::CreateDevice( + const dbus::ObjectPath& adapter_path, + const dbus::ObjectPath& device_path) { + if (std::find(device_list_.begin(), device_list_.end(), device_path) != + device_list_.end()) + return; + + scoped_ptr<Properties> properties( + new Properties(base::Bind(&FakeBluetoothDeviceClient::OnPropertyChanged, + base::Unretained(this), device_path))); + properties->adapter.ReplaceValue(adapter_path); + + if (device_path == dbus::ObjectPath(kLegacyAutopairPath)) { + properties->address.ReplaceValue(kLegacyAutopairAddress); + properties->bluetooth_class.ReplaceValue(kLegacyAutopairClass); + properties->name.ReplaceValue("LegacyAutopair"); + properties->alias.ReplaceValue(kLegacyAutopairName); + + std::vector<std::string> uuids; + uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); + properties->uuids.ReplaceValue(uuids); + + } else if (device_path == dbus::ObjectPath(kDisplayPinCodePath)) { + properties->address.ReplaceValue(kDisplayPinCodeAddress); + properties->bluetooth_class.ReplaceValue(kDisplayPinCodeClass); + properties->name.ReplaceValue("DisplayPinCode"); + properties->alias.ReplaceValue(kDisplayPinCodeName); + + std::vector<std::string> uuids; + uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); + properties->uuids.ReplaceValue(uuids); + + } else if (device_path == dbus::ObjectPath(kVanishingDevicePath)) { + properties->address.ReplaceValue(kVanishingDeviceAddress); + properties->bluetooth_class.ReplaceValue(kVanishingDeviceClass); + properties->name.ReplaceValue("VanishingDevice"); + properties->alias.ReplaceValue(kVanishingDeviceName); + + } else if (device_path == dbus::ObjectPath(kConnectUnpairablePath)) { + properties->address.ReplaceValue(kConnectUnpairableAddress); + properties->bluetooth_class.ReplaceValue(kConnectUnpairableClass); + properties->name.ReplaceValue("ConnectUnpairable"); + properties->alias.ReplaceValue(kConnectUnpairableName); + + std::vector<std::string> uuids; + uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); + properties->uuids.ReplaceValue(uuids); + + } else if (device_path == dbus::ObjectPath(kDisplayPasskeyPath)) { + properties->address.ReplaceValue(kDisplayPasskeyAddress); + properties->bluetooth_class.ReplaceValue(kDisplayPasskeyClass); + properties->name.ReplaceValue("DisplayPasskey"); + properties->alias.ReplaceValue(kDisplayPasskeyName); + + std::vector<std::string> uuids; + uuids.push_back("00001124-0000-1000-8000-00805f9b34fb"); + properties->uuids.ReplaceValue(uuids); + + } else if (device_path == dbus::ObjectPath(kRequestPinCodePath)) { + properties->address.ReplaceValue(kRequestPinCodeAddress); + properties->bluetooth_class.ReplaceValue(kRequestPinCodeClass); + properties->name.ReplaceValue("RequestPinCode"); + properties->alias.ReplaceValue(kRequestPinCodeName); + + } else if (device_path == dbus::ObjectPath(kConfirmPasskeyPath)) { + properties->address.ReplaceValue(kConfirmPasskeyAddress); + properties->bluetooth_class.ReplaceValue(kConfirmPasskeyClass); + properties->name.ReplaceValue("ConfirmPasskey"); + properties->alias.ReplaceValue(kConfirmPasskeyName); + + } else if (device_path == dbus::ObjectPath(kRequestPasskeyPath)) { + properties->address.ReplaceValue(kRequestPasskeyAddress); + properties->bluetooth_class.ReplaceValue(kRequestPasskeyClass); + properties->name.ReplaceValue("RequestPasskey"); + properties->alias.ReplaceValue(kRequestPasskeyName); + + } else if (device_path == dbus::ObjectPath(kUnconnectableDevicePath)) { + properties->address.ReplaceValue(kUnconnectableDeviceAddress); + properties->bluetooth_class.ReplaceValue(kUnconnectableDeviceClass); + properties->name.ReplaceValue("UnconnectableDevice"); + properties->alias.ReplaceValue(kUnconnectableDeviceName); + + } else if (device_path == dbus::ObjectPath(kUnpairableDevicePath)) { + properties->address.ReplaceValue(kUnpairableDeviceAddress); + properties->bluetooth_class.ReplaceValue(kUnpairableDeviceClass); + properties->name.ReplaceValue("Fake Unpairable Device"); + properties->alias.ReplaceValue(kUnpairableDeviceName); + + } else if (device_path == dbus::ObjectPath(kJustWorksPath)) { + properties->address.ReplaceValue(kJustWorksAddress); + properties->bluetooth_class.ReplaceValue(kJustWorksClass); + properties->name.ReplaceValue("JustWorks"); + properties->alias.ReplaceValue(kJustWorksName); + + } else if (device_path == dbus::ObjectPath(kLowEnergyPath)) { + properties->address.ReplaceValue(kLowEnergyAddress); + properties->bluetooth_class.ReplaceValue(kLowEnergyClass); + properties->name.ReplaceValue("Heart Rate Monitor"); + properties->alias.ReplaceValue(kLowEnergyName); + + std::vector<std::string> uuids; + uuids.push_back(FakeBluetoothGattServiceClient::kHeartRateServiceUUID); + properties->uuids.ReplaceValue(uuids); + } else if (device_path == + dbus::ObjectPath(kConnectedTrustedNotPairedDevicePath)) { + properties->address.ReplaceValue(kConnectedTrustedNotPairedDeviceAddress); + properties->bluetooth_class.ReplaceValue( + kConnectedTrustedNotPairedDeviceClass); + properties->trusted.ReplaceValue(true); + properties->connected.ReplaceValue(true); + properties->paired.ReplaceValue(false); + properties->name.ReplaceValue("Connected Pairable Device"); + properties->alias.ReplaceValue(kConnectedTrustedNotPairedDeviceName); + } else { + NOTREACHED(); + } + + properties_map_.insert(device_path, properties.Pass()); + device_list_.push_back(device_path); + FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, + DeviceAdded(device_path)); +} + +void FakeBluetoothDeviceClient::CreateDeviceWithProperties( + const dbus::ObjectPath& adapter_path, + const IncomingDeviceProperties& props) { + dbus::ObjectPath device_path(props.device_path); + if (std::find(device_list_.begin(), device_list_.end(), device_path) != + device_list_.end()) + return; + + scoped_ptr<Properties> properties( + new Properties(base::Bind(&FakeBluetoothDeviceClient::OnPropertyChanged, + base::Unretained(this), device_path))); + properties->adapter.ReplaceValue(adapter_path); + properties->name.ReplaceValue(props.device_name); + properties->alias.ReplaceValue(props.device_alias); + properties->address.ReplaceValue(props.device_address); + properties->bluetooth_class.ReplaceValue(props.device_class); + properties->trusted.ReplaceValue(props.is_trusted); + + if (props.is_trusted) + properties->paired.ReplaceValue(true); + + scoped_ptr<SimulatedPairingOptions> options(new SimulatedPairingOptions); + options->pairing_method = props.pairing_method; + options->pairing_auth_token = props.pairing_auth_token; + options->pairing_action = props.pairing_action; + options->incoming = props.incoming; + + properties_map_.insert(device_path, properties.Pass()); + device_list_.push_back(device_path); + pairing_options_map_.insert(device_path, options.Pass()); + FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, + DeviceAdded(device_path)); +} + +scoped_ptr<base::ListValue> +FakeBluetoothDeviceClient::GetBluetoothDevicesAsDictionaries() const { + scoped_ptr<base::ListValue> predefined_devices(new base::ListValue); + scoped_ptr<base::DictionaryValue> pairedDevice(new base::DictionaryValue); + pairedDevice->SetString("path", kPairedDevicePath); + pairedDevice->SetString("address", kPairedDeviceAddress); + pairedDevice->SetString("name", kPairedDeviceName); + pairedDevice->SetString("alias", kPairedDeviceName); + pairedDevice->SetString("pairingMethod", ""); + pairedDevice->SetString("pairingAuthToken", ""); + pairedDevice->SetString("pairingAction", ""); + pairedDevice->SetInteger("classValue", kPairedDeviceClass); + pairedDevice->SetBoolean("discoverable", true); + pairedDevice->SetBoolean("isTrusted", true); + pairedDevice->SetBoolean("paired", true); + pairedDevice->SetBoolean("incoming", false); + predefined_devices->Append(pairedDevice.Pass()); + + scoped_ptr<base::DictionaryValue> legacyDevice(new base::DictionaryValue); + legacyDevice->SetString("path", kLegacyAutopairPath); + legacyDevice->SetString("address", kLegacyAutopairAddress); + legacyDevice->SetString("name", kLegacyAutopairName); + legacyDevice->SetString("alias", kLegacyAutopairName); + legacyDevice->SetString("pairingMethod", ""); + legacyDevice->SetString("pairingAuthToken", ""); + legacyDevice->SetString("pairingAction", ""); + legacyDevice->SetInteger("classValue", kLegacyAutopairClass); + legacyDevice->SetBoolean("isTrusted", true); + legacyDevice->SetBoolean("discoverable", false); + legacyDevice->SetBoolean("paired", false); + legacyDevice->SetBoolean("incoming", false); + predefined_devices->Append(legacyDevice.Pass()); + + scoped_ptr<base::DictionaryValue> pin(new base::DictionaryValue); + pin->SetString("path", kDisplayPinCodePath); + pin->SetString("address", kDisplayPinCodeAddress); + pin->SetString("name", kDisplayPinCodeName); + pin->SetString("alias", kDisplayPinCodeName); + pin->SetString("pairingMethod", kPairingMethodPinCode); + pin->SetString("pairingAuthToken", kTestPinCode); + pin->SetString("pairingAction", kPairingActionDisplay); + pin->SetInteger("classValue", kDisplayPinCodeClass); + pin->SetBoolean("isTrusted", false); + pin->SetBoolean("discoverable", false); + pin->SetBoolean("paired", false); + pin->SetBoolean("incoming", false); + predefined_devices->Append(pin.Pass()); + + scoped_ptr<base::DictionaryValue> vanishing(new base::DictionaryValue); + vanishing->SetString("path", kVanishingDevicePath); + vanishing->SetString("address", kVanishingDeviceAddress); + vanishing->SetString("name", kVanishingDeviceName); + vanishing->SetString("alias", kVanishingDeviceName); + vanishing->SetString("pairingMethod", ""); + vanishing->SetString("pairingAuthToken", ""); + vanishing->SetString("pairingAction", ""); + vanishing->SetInteger("classValue", kVanishingDeviceClass); + vanishing->SetBoolean("isTrusted", false); + vanishing->SetBoolean("discoverable", false); + vanishing->SetBoolean("paired", false); + vanishing->SetBoolean("incoming", false); + predefined_devices->Append(vanishing.Pass()); + + scoped_ptr<base::DictionaryValue> connect_unpairable( + new base::DictionaryValue); + connect_unpairable->SetString("path", kConnectUnpairablePath); + connect_unpairable->SetString("address", kConnectUnpairableAddress); + connect_unpairable->SetString("name", kConnectUnpairableName); + connect_unpairable->SetString("pairingMethod", ""); + connect_unpairable->SetString("pairingAuthToken", ""); + connect_unpairable->SetString("pairingAction", ""); + connect_unpairable->SetString("alias", kConnectUnpairableName); + connect_unpairable->SetInteger("classValue", kConnectUnpairableClass); + connect_unpairable->SetBoolean("isTrusted", false); + connect_unpairable->SetBoolean("discoverable", false); + connect_unpairable->SetBoolean("paired", false); + connect_unpairable->SetBoolean("incoming", false); + predefined_devices->Append(connect_unpairable.Pass()); + + scoped_ptr<base::DictionaryValue> passkey(new base::DictionaryValue); + passkey->SetString("path", kDisplayPasskeyPath); + passkey->SetString("address", kDisplayPasskeyAddress); + passkey->SetString("name", kDisplayPasskeyName); + passkey->SetString("alias", kDisplayPasskeyName); + passkey->SetString("pairingMethod", kPairingMethodPassKey); + passkey->SetInteger("pairingAuthToken", kTestPassKey); + passkey->SetString("pairingAction", kPairingActionDisplay); + passkey->SetInteger("classValue", kDisplayPasskeyClass); + passkey->SetBoolean("isTrusted", false); + passkey->SetBoolean("discoverable", false); + passkey->SetBoolean("paired", false); + passkey->SetBoolean("incoming", false); + predefined_devices->Append(passkey.Pass()); + + scoped_ptr<base::DictionaryValue> request_pin(new base::DictionaryValue); + request_pin->SetString("path", kRequestPinCodePath); + request_pin->SetString("address", kRequestPinCodeAddress); + request_pin->SetString("name", kRequestPinCodeName); + request_pin->SetString("alias", kRequestPinCodeName); + request_pin->SetString("pairingMethod", ""); + request_pin->SetString("pairingAuthToken", ""); + request_pin->SetString("pairingAction", kPairingActionRequest); + request_pin->SetInteger("classValue", kRequestPinCodeClass); + request_pin->SetBoolean("isTrusted", false); + request_pin->SetBoolean("discoverable", false); + request_pin->SetBoolean("paired", false); + request_pin->SetBoolean("incoming", false); + predefined_devices->Append(request_pin.Pass()); + + scoped_ptr<base::DictionaryValue> confirm(new base::DictionaryValue); + confirm->SetString("path", kConfirmPasskeyPath); + confirm->SetString("address", kConfirmPasskeyAddress); + confirm->SetString("name", kConfirmPasskeyName); + confirm->SetString("alias", kConfirmPasskeyName); + confirm->SetString("pairingMethod", ""); + confirm->SetInteger("pairingAuthToken", kTestPassKey); + confirm->SetString("pairingAction", kPairingActionConfirmation); + confirm->SetInteger("classValue", kConfirmPasskeyClass); + confirm->SetBoolean("isTrusted", false); + confirm->SetBoolean("discoverable", false); + confirm->SetBoolean("paired", false); + confirm->SetBoolean("incoming", false); + predefined_devices->Append(confirm.Pass()); + + scoped_ptr<base::DictionaryValue> request_passkey(new base::DictionaryValue); + request_passkey->SetString("path", kRequestPasskeyPath); + request_passkey->SetString("address", kRequestPasskeyAddress); + request_passkey->SetString("name", kRequestPasskeyName); + request_passkey->SetString("alias", kRequestPasskeyName); + request_passkey->SetString("pairingMethod", kPairingMethodPassKey); + request_passkey->SetString("pairingAction", kPairingActionRequest); + request_passkey->SetInteger("pairingAuthToken", kTestPassKey); + request_passkey->SetInteger("classValue", kRequestPasskeyClass); + request_passkey->SetBoolean("isTrusted", false); + request_passkey->SetBoolean("discoverable", false); + request_passkey->SetBoolean("paired", false); + request_passkey->SetBoolean("incoming", false); + predefined_devices->Append(request_passkey.Pass()); + + scoped_ptr<base::DictionaryValue> unconnectable(new base::DictionaryValue); + unconnectable->SetString("path", kUnconnectableDevicePath); + unconnectable->SetString("address", kUnconnectableDeviceAddress); + unconnectable->SetString("name", kUnconnectableDeviceName); + unconnectable->SetString("alias", kUnconnectableDeviceName); + unconnectable->SetString("pairingMethod", ""); + unconnectable->SetString("pairingAuthToken", ""); + unconnectable->SetString("pairingAction", ""); + unconnectable->SetInteger("classValue", kUnconnectableDeviceClass); + unconnectable->SetBoolean("isTrusted", true); + unconnectable->SetBoolean("discoverable", false); + unconnectable->SetBoolean("paired", false); + unconnectable->SetBoolean("incoming", false); + predefined_devices->Append(unconnectable.Pass()); + + scoped_ptr<base::DictionaryValue> unpairable(new base::DictionaryValue); + unpairable->SetString("path", kUnpairableDevicePath); + unpairable->SetString("address", kUnpairableDeviceAddress); + unpairable->SetString("name", kUnpairableDeviceName); + unpairable->SetString("alias", kUnpairableDeviceName); + unpairable->SetString("pairingMethod", ""); + unpairable->SetString("pairingAuthToken", ""); + unpairable->SetString("pairingAction", kPairingActionFail); + unpairable->SetInteger("classValue", kUnpairableDeviceClass); + unpairable->SetBoolean("isTrusted", false); + unpairable->SetBoolean("discoverable", false); + unpairable->SetBoolean("paired", false); + unpairable->SetBoolean("incoming", false); + predefined_devices->Append(unpairable.Pass()); + + scoped_ptr<base::DictionaryValue> just_works(new base::DictionaryValue); + just_works->SetString("path", kJustWorksPath); + just_works->SetString("address", kJustWorksAddress); + just_works->SetString("name", kJustWorksName); + just_works->SetString("alias", kJustWorksName); + just_works->SetString("pairingMethod", ""); + just_works->SetString("pairingAuthToken", ""); + just_works->SetString("pairingAction", ""); + just_works->SetInteger("classValue", kJustWorksClass); + just_works->SetBoolean("isTrusted", false); + just_works->SetBoolean("discoverable", false); + just_works->SetBoolean("paired", false); + just_works->SetBoolean("incoming", false); + predefined_devices->Append(just_works.Pass()); + + scoped_ptr<base::DictionaryValue> low_energy(new base::DictionaryValue); + low_energy->SetString("path", kLowEnergyPath); + low_energy->SetString("address", kLowEnergyAddress); + low_energy->SetString("name", kLowEnergyName); + low_energy->SetString("alias", kLowEnergyName); + low_energy->SetString("pairingMethod", ""); + low_energy->SetString("pairingAuthToken", ""); + low_energy->SetString("pairingAction", ""); + low_energy->SetInteger("classValue", kLowEnergyClass); + low_energy->SetBoolean("isTrusted", false); + low_energy->SetBoolean("discoverable", false); + low_energy->SetBoolean("paireed", false); + low_energy->SetBoolean("incoming", false); + predefined_devices->Append(low_energy.Pass()); + + scoped_ptr<base::DictionaryValue> paired_unconnectable( + new base::DictionaryValue); + paired_unconnectable->SetString("path", kPairedUnconnectableDevicePath); + paired_unconnectable->SetString("address", kPairedUnconnectableDeviceAddress); + paired_unconnectable->SetString("name", kPairedUnconnectableDeviceName); + paired_unconnectable->SetString("pairingMethod", ""); + paired_unconnectable->SetString("pairingAuthToken", ""); + paired_unconnectable->SetString("pairingAction", ""); + paired_unconnectable->SetString("alias", kPairedUnconnectableDeviceName); + paired_unconnectable->SetInteger("classValue", + kPairedUnconnectableDeviceClass); + paired_unconnectable->SetBoolean("isTrusted", false); + paired_unconnectable->SetBoolean("discoverable", true); + paired_unconnectable->SetBoolean("paired", true); + paired_unconnectable->SetBoolean("incoming", false); + predefined_devices->Append(paired_unconnectable.Pass()); + + scoped_ptr<base::DictionaryValue> connected_trusted_not_paired( + new base::DictionaryValue); + connected_trusted_not_paired->SetString("path", + kConnectedTrustedNotPairedDevicePath); + connected_trusted_not_paired->SetString( + "address", kConnectedTrustedNotPairedDeviceAddress); + connected_trusted_not_paired->SetString("name", + kConnectedTrustedNotPairedDeviceName); + connected_trusted_not_paired->SetString("pairingMethod", ""); + connected_trusted_not_paired->SetString("pairingAuthToken", ""); + connected_trusted_not_paired->SetString("pairingAction", ""); + connected_trusted_not_paired->SetString("alias", + kConnectedTrustedNotPairedDeviceName); + connected_trusted_not_paired->SetInteger( + "classValue", kConnectedTrustedNotPairedDeviceClass); + connected_trusted_not_paired->SetBoolean("isTrusted", true); + connected_trusted_not_paired->SetBoolean("discoverable", true); + connected_trusted_not_paired->SetBoolean("paired", false); + connected_trusted_not_paired->SetBoolean("incoming", false); + predefined_devices->Append(connected_trusted_not_paired.Pass()); + + return predefined_devices.Pass(); +} + +void FakeBluetoothDeviceClient::RemoveDevice( + const dbus::ObjectPath& adapter_path, + const dbus::ObjectPath& device_path) { + std::vector<dbus::ObjectPath>::iterator listiter = + std::find(device_list_.begin(), device_list_.end(), device_path); + if (listiter == device_list_.end()) + return; + + PropertiesMap::const_iterator iter = properties_map_.find(device_path); + Properties* properties = iter->second; + + VLOG(1) << "removing device: " << properties->alias.value(); + device_list_.erase(listiter); + + // Remove the Input interface if it exists. This should be called before the + // BluetoothDeviceClient::Observer::DeviceRemoved because it deletes the + // BluetoothDeviceChromeOS object, including the device_path referenced here. + FakeBluetoothInputClient* fake_bluetooth_input_client = + static_cast<FakeBluetoothInputClient*>( + DBusThreadManager::Get()->GetBluetoothInputClient()); + fake_bluetooth_input_client->RemoveInputDevice(device_path); + + if (device_path == dbus::ObjectPath(kLowEnergyPath)) { + FakeBluetoothGattServiceClient* gatt_service_client = + static_cast<FakeBluetoothGattServiceClient*>( + DBusThreadManager::Get()->GetBluetoothGattServiceClient()); + gatt_service_client->HideHeartRateService(); + } + + FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, + DeviceRemoved(device_path)); + + properties_map_.erase(iter); + PairingOptionsMap::const_iterator options_iter = + pairing_options_map_.find(device_path); + + if (options_iter != pairing_options_map_.end()) { + pairing_options_map_.erase(options_iter); + } +} + +void FakeBluetoothDeviceClient::OnPropertyChanged( + const dbus::ObjectPath& object_path, + const std::string& property_name) { + VLOG(2) << "Fake Bluetooth device property changed: " << object_path.value() + << ": " << property_name; + FOR_EACH_OBSERVER(BluetoothDeviceClient::Observer, observers_, + DevicePropertyChanged(object_path, property_name)); +} + +void FakeBluetoothDeviceClient::DiscoverySimulationTimer() { + if (!discovery_simulation_step_) + return; + + // Timer fires every .75s, the numbers below are arbitrary to give a feel + // for a discovery process. + VLOG(1) << "discovery simulation, step " << discovery_simulation_step_; + if (discovery_simulation_step_ == 2) { + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kLegacyAutopairPath)); + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kLowEnergyPath)); + + } else if (discovery_simulation_step_ == 4) { + UpdateDeviceRSSI(dbus::ObjectPath(kLowEnergyPath), + base::RandInt(kMinRSSI, kMaxRSSI)); + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kDisplayPinCodePath)); + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kVanishingDevicePath)); + + } else if (discovery_simulation_step_ == 7) { + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kConnectUnpairablePath)); + UpdateDeviceRSSI(dbus::ObjectPath(kLowEnergyPath), + base::RandInt(kMinRSSI, kMaxRSSI)); + + } else if (discovery_simulation_step_ == 8) { + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kDisplayPasskeyPath)); + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kRequestPinCodePath)); + UpdateDeviceRSSI(dbus::ObjectPath(kLowEnergyPath), + base::RandInt(kMinRSSI, kMaxRSSI)); + + } else if (discovery_simulation_step_ == 10) { + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kConfirmPasskeyPath)); + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kRequestPasskeyPath)); + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kUnconnectableDevicePath)); + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kUnpairableDevicePath)); + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kJustWorksPath)); + UpdateDeviceRSSI(dbus::ObjectPath(kLowEnergyPath), + base::RandInt(kMinRSSI, kMaxRSSI)); + + } else if (discovery_simulation_step_ == 13) { + UpdateDeviceRSSI(dbus::ObjectPath(kLowEnergyPath), + base::RandInt(kMinRSSI, kMaxRSSI)); + RemoveDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kVanishingDevicePath)); + } else if (discovery_simulation_step_ == 14) { + UpdateDeviceRSSI(dbus::ObjectPath(kLowEnergyPath), + base::RandInt(kMinRSSI, kMaxRSSI)); + return; + } + + ++discovery_simulation_step_; + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::DiscoverySimulationTimer, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); +} + +void FakeBluetoothDeviceClient::IncomingPairingSimulationTimer() { + if (!incoming_pairing_simulation_step_) + return; + + VLOG(1) << "incoming pairing simulation, step " + << incoming_pairing_simulation_step_; + switch (incoming_pairing_simulation_step_) { + case 1: + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kConfirmPasskeyPath)); + SimulatePairing(dbus::ObjectPath(kConfirmPasskeyPath), true, + base::Bind(&base::DoNothing), + base::Bind(&SimpleErrorCallback)); + break; + case 2: + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kJustWorksPath)); + SimulatePairing(dbus::ObjectPath(kJustWorksPath), true, + base::Bind(&base::DoNothing), + base::Bind(&SimpleErrorCallback)); + break; + case 3: + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kDisplayPinCodePath)); + SimulatePairing(dbus::ObjectPath(kDisplayPinCodePath), true, + base::Bind(&base::DoNothing), + base::Bind(&SimpleErrorCallback)); + break; + case 4: + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kDisplayPasskeyPath)); + SimulatePairing(dbus::ObjectPath(kDisplayPasskeyPath), true, + base::Bind(&base::DoNothing), + base::Bind(&SimpleErrorCallback)); + break; + case 5: + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kRequestPinCodePath)); + SimulatePairing(dbus::ObjectPath(kRequestPinCodePath), true, + base::Bind(&base::DoNothing), + base::Bind(&SimpleErrorCallback)); + break; + case 6: + CreateDevice(dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath), + dbus::ObjectPath(kRequestPasskeyPath)); + SimulatePairing(dbus::ObjectPath(kRequestPasskeyPath), true, + base::Bind(&base::DoNothing), + base::Bind(&SimpleErrorCallback)); + break; + default: + return; + } + + ++incoming_pairing_simulation_step_; + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::IncomingPairingSimulationTimer, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(kIncomingSimulationPairTimeMultiplier * + simulation_interval_ms_)); +} + +void FakeBluetoothDeviceClient::SimulatePairing( + const dbus::ObjectPath& object_path, + bool incoming_request, + const base::Closure& callback, + const ErrorCallback& error_callback) { + pairing_cancelled_ = false; + + FakeBluetoothAgentManagerClient* fake_bluetooth_agent_manager_client = + static_cast<FakeBluetoothAgentManagerClient*>( + DBusThreadManager::Get()->GetBluetoothAgentManagerClient()); + FakeBluetoothAgentServiceProvider* agent_service_provider = + fake_bluetooth_agent_manager_client->GetAgentServiceProvider(); + CHECK(agent_service_provider != NULL); + + // Grab the device's pairing properties. + PairingOptionsMap::const_iterator iter = + pairing_options_map_.find(object_path); + + // If the device with path |object_path| has simulated pairing properties + // defined, then pair it based on its |pairing_method|. + if (iter != pairing_options_map_.end()) { + if (iter->second->pairing_action == kPairingActionFail) { + // Fails the pairing with an org.bluez.Error.Failed error. + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::FailSimulatedPairing, + base::Unretained(this), object_path, error_callback), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); + } else if (iter->second->pairing_method == kPairingMethodNone || + iter->second->pairing_method.empty()) { + if (!iter->second->incoming) { + // Simply pair and connect the device. + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, + base::Unretained(this), object_path, callback, + error_callback), + base::TimeDelta::FromMilliseconds( + kSimulateNormalPairTimeMultiplier * simulation_interval_ms_)); + } else { + agent_service_provider->RequestAuthorization( + object_path, + base::Bind(&FakeBluetoothDeviceClient::ConfirmationCallback, + base::Unretained(this), object_path, callback, + error_callback)); + } + } else if (iter->second->pairing_method == kPairingMethodPinCode) { + if (iter->second->pairing_action == kPairingActionDisplay) { + // Display a Pincode, and wait before acting as if the other end + // accepted it. + agent_service_provider->DisplayPinCode( + object_path, iter->second->pairing_auth_token); + + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, + base::Unretained(this), object_path, callback, + error_callback), + base::TimeDelta::FromMilliseconds(kPinCodeDevicePairTimeMultiplier * + simulation_interval_ms_)); + } else if (iter->second->pairing_action == kPairingActionRequest) { + // Request a pin code. + agent_service_provider->RequestPinCode( + object_path, base::Bind(&FakeBluetoothDeviceClient::PinCodeCallback, + base::Unretained(this), object_path, + callback, error_callback)); + } else if (iter->second->pairing_action == kPairingActionConfirmation) { + error_callback.Run(kNoResponseError, "No confirm for pincode pairing."); + } + } else if (iter->second->pairing_method == kPairingMethodPassKey) { + // Display a passkey, and each interval act as if another key was entered + // for it. + if (iter->second->pairing_action == kPairingActionDisplay) { + agent_service_provider->DisplayPasskey( + object_path, std::stoi(iter->second->pairing_auth_token), 0); + + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::SimulateKeypress, + base::Unretained(this), 1, object_path, + callback, error_callback), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); + } else if (iter->second->pairing_action == kPairingActionRequest) { + agent_service_provider->RequestPasskey( + object_path, base::Bind(&FakeBluetoothDeviceClient::PasskeyCallback, + base::Unretained(this), object_path, + callback, error_callback)); + } else if (iter->second->pairing_action == kPairingActionConfirmation) { + agent_service_provider->RequestConfirmation( + object_path, std::stoi(iter->second->pairing_auth_token), + base::Bind(&FakeBluetoothDeviceClient::ConfirmationCallback, + base::Unretained(this), object_path, callback, + error_callback)); + } + } + } else { + if (object_path == dbus::ObjectPath(kLegacyAutopairPath) || + object_path == dbus::ObjectPath(kConnectUnpairablePath) || + object_path == dbus::ObjectPath(kUnconnectableDevicePath) || + object_path == dbus::ObjectPath(kLowEnergyPath)) { + // No need to call anything on the pairing delegate, just wait 3 times + // the interval before acting as if the other end accepted it. + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, + base::Unretained(this), object_path, callback, + error_callback), + base::TimeDelta::FromMilliseconds(kSimulateNormalPairTimeMultiplier * + simulation_interval_ms_)); + + } else if (object_path == dbus::ObjectPath(kDisplayPinCodePath)) { + // Display a Pincode, and wait before acting as if the other end accepted + // it. + agent_service_provider->DisplayPinCode(object_path, kTestPinCode); + + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, + base::Unretained(this), object_path, callback, + error_callback), + base::TimeDelta::FromMilliseconds(kPinCodeDevicePairTimeMultiplier * + simulation_interval_ms_)); + + } else if (object_path == dbus::ObjectPath(kVanishingDevicePath)) { + // The vanishing device simulates being too far away, and thus times out. + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::TimeoutSimulatedPairing, + base::Unretained(this), object_path, error_callback), + base::TimeDelta::FromMilliseconds(kVanishingDevicePairTimeMultiplier * + simulation_interval_ms_)); + + } else if (object_path == dbus::ObjectPath(kDisplayPasskeyPath)) { + // Display a passkey, and each interval act as if another key was entered + // for it. + agent_service_provider->DisplayPasskey(object_path, kTestPassKey, 0); + + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::SimulateKeypress, + base::Unretained(this), 1, object_path, + callback, error_callback), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); + + } else if (object_path == dbus::ObjectPath(kRequestPinCodePath)) { + // Request a Pincode. + agent_service_provider->RequestPinCode( + object_path, base::Bind(&FakeBluetoothDeviceClient::PinCodeCallback, + base::Unretained(this), object_path, callback, + error_callback)); + + } else if (object_path == dbus::ObjectPath(kConfirmPasskeyPath) || + object_path == + dbus::ObjectPath(kConnectedTrustedNotPairedDevicePath)) { + // Request confirmation of a Passkey. + agent_service_provider->RequestConfirmation( + object_path, kTestPassKey, + base::Bind(&FakeBluetoothDeviceClient::ConfirmationCallback, + base::Unretained(this), object_path, callback, + error_callback)); + + } else if (object_path == dbus::ObjectPath(kRequestPasskeyPath)) { + // Request a Passkey from the user. + agent_service_provider->RequestPasskey( + object_path, base::Bind(&FakeBluetoothDeviceClient::PasskeyCallback, + base::Unretained(this), object_path, callback, + error_callback)); + + } else if (object_path == dbus::ObjectPath(kUnpairableDevicePath)) { + // Fails the pairing with an org.bluez.Error.Failed error. + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::FailSimulatedPairing, + base::Unretained(this), object_path, error_callback), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); + + } else if (object_path == dbus::ObjectPath(kJustWorksPath)) { + if (incoming_request) { + agent_service_provider->RequestAuthorization( + object_path, + base::Bind(&FakeBluetoothDeviceClient::ConfirmationCallback, + base::Unretained(this), object_path, callback, + error_callback)); + + } else { + // No need to call anything on the pairing delegate, just wait before + // acting as if the other end accepted it. + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, + base::Unretained(this), object_path, callback, + error_callback), + base::TimeDelta::FromMilliseconds( + kSimulateNormalPairTimeMultiplier * simulation_interval_ms_)); + } + + } else { + error_callback.Run(kNoResponseError, "No pairing fake"); + } + } +} + +void FakeBluetoothDeviceClient::CompleteSimulatedPairing( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "CompleteSimulatedPairing: " << object_path.value(); + if (pairing_cancelled_) { + pairing_cancelled_ = false; + + error_callback.Run(bluetooth_device::kErrorAuthenticationCanceled, + "Cancelled"); + } else { + Properties* properties = GetProperties(object_path); + + properties->paired.ReplaceValue(true); + callback.Run(); + + AddInputDeviceIfNeeded(object_path, properties); + } +} + +void FakeBluetoothDeviceClient::TimeoutSimulatedPairing( + const dbus::ObjectPath& object_path, + const ErrorCallback& error_callback) { + VLOG(1) << "TimeoutSimulatedPairing: " << object_path.value(); + + error_callback.Run(bluetooth_device::kErrorAuthenticationTimeout, + "Timed out"); +} + +void FakeBluetoothDeviceClient::CancelSimulatedPairing( + const dbus::ObjectPath& object_path, + const ErrorCallback& error_callback) { + VLOG(1) << "CancelSimulatedPairing: " << object_path.value(); + + error_callback.Run(bluetooth_device::kErrorAuthenticationCanceled, + "Canceled"); +} + +void FakeBluetoothDeviceClient::RejectSimulatedPairing( + const dbus::ObjectPath& object_path, + const ErrorCallback& error_callback) { + VLOG(1) << "RejectSimulatedPairing: " << object_path.value(); + + error_callback.Run(bluetooth_device::kErrorAuthenticationRejected, + "Rejected"); +} + +void FakeBluetoothDeviceClient::FailSimulatedPairing( + const dbus::ObjectPath& object_path, + const ErrorCallback& error_callback) { + VLOG(1) << "FailSimulatedPairing: " << object_path.value(); + + error_callback.Run(bluetooth_device::kErrorFailed, "Failed"); +} + +void FakeBluetoothDeviceClient::AddInputDeviceIfNeeded( + const dbus::ObjectPath& object_path, + Properties* properties) { + // If the paired device is a HID device based on it's bluetooth class, + // simulate the Input interface. + FakeBluetoothInputClient* fake_bluetooth_input_client = + static_cast<FakeBluetoothInputClient*>( + DBusThreadManager::Get()->GetBluetoothInputClient()); + + if ((properties->bluetooth_class.value() & 0x001f03) == 0x000500) + fake_bluetooth_input_client->AddInputDevice(object_path); +} + +void FakeBluetoothDeviceClient::UpdateDeviceRSSI( + const dbus::ObjectPath& object_path, + int16 rssi) { + PropertiesMap::const_iterator iter = properties_map_.find(object_path); + if (iter == properties_map_.end()) { + VLOG(2) << "Fake device does not exist: " << object_path.value(); + return; + } + Properties* properties = iter->second; + DCHECK(properties); + properties->rssi.ReplaceValue(rssi); +} + +void FakeBluetoothDeviceClient::UpdateConnectionInfo( + uint16 connection_rssi, + uint16 transmit_power, + uint16 max_transmit_power) { + connection_rssi_ = connection_rssi; + transmit_power_ = transmit_power; + max_transmit_power_ = max_transmit_power; +} + +void FakeBluetoothDeviceClient::PinCodeCallback( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback, + BluetoothAgentServiceProvider::Delegate::Status status, + const std::string& pincode) { + VLOG(1) << "PinCodeCallback: " << object_path.value(); + + if (status == BluetoothAgentServiceProvider::Delegate::SUCCESS) { + PairingOptionsMap::const_iterator iter = + pairing_options_map_.find(object_path); + + bool success = true; + + // If the device has pairing options defined + if (iter != pairing_options_map_.end()) { + success = iter->second->pairing_auth_token == pincode; + } + + if (success) { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, + base::Unretained(this), object_path, callback, + error_callback), + base::TimeDelta::FromMilliseconds(kSimulateNormalPairTimeMultiplier * + simulation_interval_ms_)); + } else { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::RejectSimulatedPairing, + base::Unretained(this), object_path, error_callback), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); + } + + } else if (status == BluetoothAgentServiceProvider::Delegate::CANCELLED) { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::CancelSimulatedPairing, + base::Unretained(this), object_path, error_callback), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); + + } else if (status == BluetoothAgentServiceProvider::Delegate::REJECTED) { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::RejectSimulatedPairing, + base::Unretained(this), object_path, error_callback), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); + } +} + +void FakeBluetoothDeviceClient::PasskeyCallback( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback, + BluetoothAgentServiceProvider::Delegate::Status status, + uint32 passkey) { + VLOG(1) << "PasskeyCallback: " << object_path.value(); + + if (status == BluetoothAgentServiceProvider::Delegate::SUCCESS) { + PairingOptionsMap::const_iterator iter = + pairing_options_map_.find(object_path); + bool success = true; + + if (iter != pairing_options_map_.end()) { + success = static_cast<uint32>( + std::stoi(iter->second->pairing_auth_token)) == passkey; + } + + if (success) { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, + base::Unretained(this), object_path, callback, + error_callback), + base::TimeDelta::FromMilliseconds(kSimulateNormalPairTimeMultiplier * + simulation_interval_ms_)); + } else { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::RejectSimulatedPairing, + base::Unretained(this), object_path, error_callback), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); + } + + } else if (status == BluetoothAgentServiceProvider::Delegate::CANCELLED) { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::CancelSimulatedPairing, + base::Unretained(this), object_path, error_callback), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); + + } else if (status == BluetoothAgentServiceProvider::Delegate::REJECTED) { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::RejectSimulatedPairing, + base::Unretained(this), object_path, error_callback), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); + } +} + +void FakeBluetoothDeviceClient::ConfirmationCallback( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback, + BluetoothAgentServiceProvider::Delegate::Status status) { + VLOG(1) << "ConfirmationCallback: " << object_path.value(); + + if (status == BluetoothAgentServiceProvider::Delegate::SUCCESS) { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, + base::Unretained(this), object_path, callback, + error_callback), + base::TimeDelta::FromMilliseconds(kSimulateNormalPairTimeMultiplier * + simulation_interval_ms_)); + + } else if (status == BluetoothAgentServiceProvider::Delegate::CANCELLED) { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::CancelSimulatedPairing, + base::Unretained(this), object_path, error_callback), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); + + } else if (status == BluetoothAgentServiceProvider::Delegate::REJECTED) { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::RejectSimulatedPairing, + base::Unretained(this), object_path, error_callback), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); + } +} + +void FakeBluetoothDeviceClient::SimulateKeypress( + uint16 entered, + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "SimulateKeypress " << entered << ": " << object_path.value(); + + FakeBluetoothAgentManagerClient* fake_bluetooth_agent_manager_client = + static_cast<FakeBluetoothAgentManagerClient*>( + DBusThreadManager::Get()->GetBluetoothAgentManagerClient()); + FakeBluetoothAgentServiceProvider* agent_service_provider = + fake_bluetooth_agent_manager_client->GetAgentServiceProvider(); + + // The agent service provider object could have been destroyed after the + // pairing is canceled. + if (!agent_service_provider) + return; + + agent_service_provider->DisplayPasskey(object_path, kTestPassKey, entered); + + if (entered < 7) { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&FakeBluetoothDeviceClient::SimulateKeypress, + base::Unretained(this), entered + 1, object_path, + callback, error_callback), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); + + } else { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind(&FakeBluetoothDeviceClient::CompleteSimulatedPairing, + base::Unretained(this), object_path, callback, + error_callback), + base::TimeDelta::FromMilliseconds(simulation_interval_ms_)); + } +} + +void FakeBluetoothDeviceClient::ConnectionCallback( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback, + BluetoothProfileServiceProvider::Delegate::Status status) { + VLOG(1) << "ConnectionCallback: " << object_path.value(); + + if (status == BluetoothProfileServiceProvider::Delegate::SUCCESS) { + callback.Run(); + } else if (status == BluetoothProfileServiceProvider::Delegate::CANCELLED) { + // TODO(keybuk): tear down this side of the connection + error_callback.Run(bluetooth_device::kErrorFailed, "Canceled"); + } else if (status == BluetoothProfileServiceProvider::Delegate::REJECTED) { + // TODO(keybuk): tear down this side of the connection + error_callback.Run(bluetooth_device::kErrorFailed, "Rejected"); + } +} + +void FakeBluetoothDeviceClient::DisconnectionCallback( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback, + BluetoothProfileServiceProvider::Delegate::Status status) { + VLOG(1) << "DisconnectionCallback: " << object_path.value(); + + if (status == BluetoothProfileServiceProvider::Delegate::SUCCESS) { + // TODO(keybuk): tear down this side of the connection + callback.Run(); + } else if (status == BluetoothProfileServiceProvider::Delegate::CANCELLED) { + error_callback.Run(bluetooth_device::kErrorFailed, "Canceled"); + } else if (status == BluetoothProfileServiceProvider::Delegate::REJECTED) { + error_callback.Run(bluetooth_device::kErrorFailed, "Rejected"); + } +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_device_client.h b/chromeos/dbus/fake_bluetooth_device_client.h new file mode 100644 index 0000000..03a2cb1 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_device_client.h @@ -0,0 +1,320 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_DEVICE_CLIENT_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_DEVICE_CLIENT_H_ + +#include <map> +#include <vector> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/containers/scoped_ptr_map.h" +#include "base/observer_list.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_agent_service_provider.h" +#include "chromeos/dbus/bluetooth_device_client.h" +#include "chromeos/dbus/bluetooth_profile_service_provider.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +// FakeBluetoothDeviceClient simulates the behavior of the Bluetooth Daemon +// device objects and is used both in test cases in place of a mock and on +// the Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothDeviceClient : public BluetoothDeviceClient { + public: + struct Properties : public BluetoothDeviceClient::Properties { + explicit Properties(const PropertyChangedCallback& callback); + ~Properties() override; + + // dbus::PropertySet override + void Get(dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) override; + void GetAll() override; + void Set(dbus::PropertyBase* property, + dbus::PropertySet::SetCallback callback) override; + }; + + struct SimulatedPairingOptions { + SimulatedPairingOptions(); + ~SimulatedPairingOptions(); + + bool incoming = false; + std::string pairing_method; + std::string pairing_auth_token; + std::string pairing_action; + }; + + // Stores properties of a device that is about to be created. + struct IncomingDeviceProperties { + IncomingDeviceProperties(); + ~IncomingDeviceProperties(); + + std::string device_address; + std::string device_alias; + int device_class = 0; + std::string device_name; + std::string device_path; + bool is_trusted = true; + bool incoming = false; + std::string pairing_action; + std::string pairing_auth_token; + std::string pairing_method; + }; + + FakeBluetoothDeviceClient(); + ~FakeBluetoothDeviceClient() override; + + // BluetoothDeviceClient overrides + void Init(dbus::Bus* bus) override; + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + std::vector<dbus::ObjectPath> GetDevicesForAdapter( + const dbus::ObjectPath& adapter_path) override; + Properties* GetProperties(const dbus::ObjectPath& object_path) override; + void Connect(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void Disconnect(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void ConnectProfile(const dbus::ObjectPath& object_path, + const std::string& uuid, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void DisconnectProfile(const dbus::ObjectPath& object_path, + const std::string& uuid, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void Pair(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void CancelPairing(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void GetConnInfo(const dbus::ObjectPath& object_path, + const ConnInfoCallback& callback, + const ErrorCallback& error_callback) override; + + void SetSimulationIntervalMs(int interval_ms); + + // Simulates discovery of devices for the given adapter. + void BeginDiscoverySimulation(const dbus::ObjectPath& adapter_path); + void EndDiscoverySimulation(const dbus::ObjectPath& adapter_path); + + // Simulates incoming pairing of devices for the given adapter. + void BeginIncomingPairingSimulation(const dbus::ObjectPath& adapter_path); + void EndIncomingPairingSimulation(const dbus::ObjectPath& adapter_path); + + // Creates a device from the set we return for the given adapter. + void CreateDevice(const dbus::ObjectPath& adapter_path, + const dbus::ObjectPath& device_path); + + // Creates a device with the given properties. + void CreateDeviceWithProperties(const dbus::ObjectPath& adapter_path, + const IncomingDeviceProperties& props); + + // Creates and returns a list of scoped_ptr<base::DictionaryValue> + // objects, which contain all the data from the constants for devices with + // predefined behavior. + scoped_ptr<base::ListValue> GetBluetoothDevicesAsDictionaries() const; + + SimulatedPairingOptions* GetPairingOptions( + const dbus::ObjectPath& object_path); + + // Removes a device from the set we return for the given adapter. + void RemoveDevice(const dbus::ObjectPath& adapter_path, + const dbus::ObjectPath& device_path); + + // Simulates a pairing for the device with the given D-Bus object path, + // |object_path|. Set |incoming_request| to true if simulating an incoming + // pairing request, false for an outgoing one. On successful completion + // |callback| will be called, on failure, |error_callback| is called. + void SimulatePairing(const dbus::ObjectPath& object_path, + bool incoming_request, + const base::Closure& callback, + const ErrorCallback& error_callback); + + // Updates the connection properties of the fake device that will be returned + // by GetConnInfo. + void UpdateConnectionInfo(uint16 connection_rssi, + uint16 transmit_power, + uint16 max_transmit_power); + + static const char kTestPinCode[]; + static const int kTestPassKey; + + static const char kPairingMethodNone[]; + static const char kPairingMethodPinCode[]; + static const char kPairingMethodPassKey[]; + + static const char kPairingActionConfirmation[]; + static const char kPairingActionDisplay[]; + static const char kPairingActionFail[]; + static const char kPairingActionRequest[]; + + // Object paths, names, addresses and bluetooth classes of the devices + // we can emulate. + static const char kPairedDevicePath[]; + static const char kPairedDeviceName[]; + static const char kPairedDeviceAddress[]; + static const uint32 kPairedDeviceClass; + + static const char kLegacyAutopairPath[]; + static const char kLegacyAutopairName[]; + static const char kLegacyAutopairAddress[]; + static const uint32 kLegacyAutopairClass; + + static const char kDisplayPinCodePath[]; + static const char kDisplayPinCodeName[]; + static const char kDisplayPinCodeAddress[]; + static const uint32 kDisplayPinCodeClass; + + static const char kVanishingDevicePath[]; + static const char kVanishingDeviceName[]; + static const char kVanishingDeviceAddress[]; + static const uint32 kVanishingDeviceClass; + + static const char kConnectUnpairablePath[]; + static const char kConnectUnpairableName[]; + static const char kConnectUnpairableAddress[]; + static const uint32 kConnectUnpairableClass; + + static const char kDisplayPasskeyPath[]; + static const char kDisplayPasskeyName[]; + static const char kDisplayPasskeyAddress[]; + static const uint32 kDisplayPasskeyClass; + + static const char kRequestPinCodePath[]; + static const char kRequestPinCodeName[]; + static const char kRequestPinCodeAddress[]; + static const uint32 kRequestPinCodeClass; + + static const char kConfirmPasskeyPath[]; + static const char kConfirmPasskeyName[]; + static const char kConfirmPasskeyAddress[]; + static const uint32 kConfirmPasskeyClass; + + static const char kRequestPasskeyPath[]; + static const char kRequestPasskeyName[]; + static const char kRequestPasskeyAddress[]; + static const uint32 kRequestPasskeyClass; + + static const char kUnconnectableDevicePath[]; + static const char kUnconnectableDeviceName[]; + static const char kUnconnectableDeviceAddress[]; + static const uint32 kUnconnectableDeviceClass; + + static const char kUnpairableDevicePath[]; + static const char kUnpairableDeviceName[]; + static const char kUnpairableDeviceAddress[]; + static const uint32 kUnpairableDeviceClass; + + static const char kJustWorksPath[]; + static const char kJustWorksName[]; + static const char kJustWorksAddress[]; + static const uint32 kJustWorksClass; + + static const char kLowEnergyPath[]; + static const char kLowEnergyName[]; + static const char kLowEnergyAddress[]; + static const uint32 kLowEnergyClass; + + static const char kPairedUnconnectableDevicePath[]; + static const char kPairedUnconnectableDeviceName[]; + static const char kPairedUnconnectableDeviceAddress[]; + static const uint32 kPairedUnconnectableDeviceClass; + + static const char kConnectedTrustedNotPairedDevicePath[]; + static const char kConnectedTrustedNotPairedDeviceAddress[]; + static const char kConnectedTrustedNotPairedDeviceName[]; + static const uint32 kConnectedTrustedNotPairedDeviceClass; + + private: + // Property callback passed when we create Properties* structures. + void OnPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name); + + void DiscoverySimulationTimer(); + void IncomingPairingSimulationTimer(); + + void CompleteSimulatedPairing(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback); + void TimeoutSimulatedPairing(const dbus::ObjectPath& object_path, + const ErrorCallback& error_callback); + void CancelSimulatedPairing(const dbus::ObjectPath& object_path, + const ErrorCallback& error_callback); + void RejectSimulatedPairing(const dbus::ObjectPath& object_path, + const ErrorCallback& error_callback); + void FailSimulatedPairing(const dbus::ObjectPath& object_path, + const ErrorCallback& error_callback); + void AddInputDeviceIfNeeded(const dbus::ObjectPath& object_path, + Properties* properties); + + // Updates the inquiry RSSI property of fake device with object path + // |object_path| to |rssi|, if the fake device exists. + void UpdateDeviceRSSI(const dbus::ObjectPath& object_path, int16 rssi); + + void PinCodeCallback(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback, + BluetoothAgentServiceProvider::Delegate::Status status, + const std::string& pincode); + void PasskeyCallback(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback, + BluetoothAgentServiceProvider::Delegate::Status status, + uint32 passkey); + void ConfirmationCallback( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback, + BluetoothAgentServiceProvider::Delegate::Status status); + void SimulateKeypress(uint16 entered, + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback); + + void ConnectionCallback( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback, + BluetoothProfileServiceProvider::Delegate::Status status); + void DisconnectionCallback( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback, + BluetoothProfileServiceProvider::Delegate::Status status); + + // List of observers interested in event notifications from us. + base::ObserverList<Observer> observers_; + + using PropertiesMap = + base::ScopedPtrMap<const dbus::ObjectPath, scoped_ptr<Properties>>; + PropertiesMap properties_map_; + std::vector<dbus::ObjectPath> device_list_; + + // Properties which are used to decied which method of pairing should + // be done on request. + using PairingOptionsMap = + base::ScopedPtrMap<const dbus::ObjectPath, + scoped_ptr<SimulatedPairingOptions>>; + PairingOptionsMap pairing_options_map_; + + int simulation_interval_ms_; + uint32_t discovery_simulation_step_; + uint32_t incoming_pairing_simulation_step_; + bool pairing_cancelled_; + + int16 connection_rssi_; + int16 transmit_power_; + int16 max_transmit_power_; +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_DEVICE_CLIENT_H_ diff --git a/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc b/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc new file mode 100644 index 0000000..69779ec --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.cc @@ -0,0 +1,562 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/rand_util.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "base/time/time.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_gatt_descriptor_client.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +namespace { + +const int kStartNotifyResponseIntervalMs = 200; +const int kHeartRateMeasurementNotificationIntervalMs = 2000; + +} // namespace + +FakeBluetoothGattCharacteristicClient::DelayedCallback::DelayedCallback( + base::Closure callback, + size_t delay) + : callback_(callback), delay_(delay) {} + +FakeBluetoothGattCharacteristicClient::DelayedCallback::~DelayedCallback() {} + +// static +const char FakeBluetoothGattCharacteristicClient:: + kHeartRateMeasurementPathComponent[] = "char0000"; +const char + FakeBluetoothGattCharacteristicClient::kBodySensorLocationPathComponent[] = + "char0001"; +const char FakeBluetoothGattCharacteristicClient:: + kHeartRateControlPointPathComponent[] = "char0002"; + +// static +const char FakeBluetoothGattCharacteristicClient::kHeartRateMeasurementUUID[] = + "00002a37-0000-1000-8000-00805f9b34fb"; +const char FakeBluetoothGattCharacteristicClient::kBodySensorLocationUUID[] = + "00002a38-0000-1000-8000-00805f9b34fb"; +const char FakeBluetoothGattCharacteristicClient::kHeartRateControlPointUUID[] = + "00002a39-0000-1000-8000-00805f9b34fb"; + +FakeBluetoothGattCharacteristicClient::Properties::Properties( + const PropertyChangedCallback& callback) + : BluetoothGattCharacteristicClient::Properties( + NULL, + bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface, + callback) {} + +FakeBluetoothGattCharacteristicClient::Properties::~Properties() {} + +void FakeBluetoothGattCharacteristicClient::Properties::Get( + dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) { + VLOG(1) << "Get " << property->name(); + callback.Run(true); +} + +void FakeBluetoothGattCharacteristicClient::Properties::GetAll() { + VLOG(1) << "GetAll"; +} + +void FakeBluetoothGattCharacteristicClient::Properties::Set( + dbus::PropertyBase* property, + dbus::PropertySet::SetCallback callback) { + VLOG(1) << "Set " << property->name(); + callback.Run(false); +} + +FakeBluetoothGattCharacteristicClient::FakeBluetoothGattCharacteristicClient() + : heart_rate_visible_(false), + authorized_(true), + authenticated_(true), + calories_burned_(0), + extra_requests_(0), + weak_ptr_factory_(this) {} + +FakeBluetoothGattCharacteristicClient:: + ~FakeBluetoothGattCharacteristicClient() { + for (const auto& it : action_extra_requests_) { + delete it.second; + } + action_extra_requests_.clear(); +} + +void FakeBluetoothGattCharacteristicClient::Init(dbus::Bus* bus) {} + +void FakeBluetoothGattCharacteristicClient::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void FakeBluetoothGattCharacteristicClient::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +std::vector<dbus::ObjectPath> +FakeBluetoothGattCharacteristicClient::GetCharacteristics() { + std::vector<dbus::ObjectPath> paths; + if (IsHeartRateVisible()) { + paths.push_back(dbus::ObjectPath(heart_rate_measurement_path_)); + paths.push_back(dbus::ObjectPath(body_sensor_location_path_)); + paths.push_back(dbus::ObjectPath(heart_rate_control_point_path_)); + } + return paths; +} + +FakeBluetoothGattCharacteristicClient::Properties* +FakeBluetoothGattCharacteristicClient::GetProperties( + const dbus::ObjectPath& object_path) { + if (object_path.value() == heart_rate_measurement_path_) { + DCHECK(heart_rate_measurement_properties_.get()); + return heart_rate_measurement_properties_.get(); + } + if (object_path.value() == body_sensor_location_path_) { + DCHECK(body_sensor_location_properties_.get()); + return body_sensor_location_properties_.get(); + } + if (object_path.value() == heart_rate_control_point_path_) { + DCHECK(heart_rate_control_point_properties_.get()); + return heart_rate_control_point_properties_.get(); + } + return NULL; +} + +void FakeBluetoothGattCharacteristicClient::ReadValue( + const dbus::ObjectPath& object_path, + const ValueCallback& callback, + const ErrorCallback& error_callback) { + if (!authenticated_) { + error_callback.Run("org.bluez.Error.NotPaired", "Please login"); + return; + } + + if (!authorized_) { + error_callback.Run("org.bluez.Error.NotAuthorized", "Authorize first"); + return; + } + + if (object_path.value() == heart_rate_control_point_path_) { + error_callback.Run("org.bluez.Error.NotPermitted", + "Reads of this value are not allowed"); + return; + } + + if (object_path.value() == heart_rate_measurement_path_) { + error_callback.Run("org.bluez.Error.NotSupported", + "Action not supported on this characteristic"); + return; + } + + if (object_path.value() != body_sensor_location_path_) { + error_callback.Run(kUnknownCharacteristicError, ""); + return; + } + + if (action_extra_requests_.find("ReadValue") != + action_extra_requests_.end()) { + DelayedCallback* delayed = action_extra_requests_["ReadValue"]; + delayed->delay_--; + error_callback.Run("org.bluez.Error.InProgress", + "Another read is currenty in progress"); + if (delayed->delay_ == 0) { + delayed->callback_.Run(); + action_extra_requests_.erase("ReadValue"); + delete delayed; + } + return; + } + + base::Closure completed_callback; + if (!IsHeartRateVisible()) { + completed_callback = + base::Bind(error_callback, kUnknownCharacteristicError, ""); + } else { + std::vector<uint8> value = {0x06}; // Location is "foot". + completed_callback = base::Bind( + &FakeBluetoothGattCharacteristicClient::DelayedReadValueCallback, + weak_ptr_factory_.GetWeakPtr(), object_path, callback, value); + } + + if (extra_requests_ > 0) { + action_extra_requests_["ReadValue"] = + new DelayedCallback(completed_callback, extra_requests_); + return; + } + + completed_callback.Run(); +} + +void FakeBluetoothGattCharacteristicClient::WriteValue( + const dbus::ObjectPath& object_path, + const std::vector<uint8>& value, + const base::Closure& callback, + const ErrorCallback& error_callback) { + if (!authenticated_) { + error_callback.Run("org.bluez.Error.NotPaired", "Please login"); + return; + } + + if (!authorized_) { + error_callback.Run("org.bluez.Error.NotAuthorized", "Authorize first"); + return; + } + + if (!IsHeartRateVisible()) { + error_callback.Run(kUnknownCharacteristicError, ""); + return; + } + + if (object_path.value() == heart_rate_measurement_path_) { + error_callback.Run("org.bluez.Error.NotSupported", + "Action not supported on this characteristic"); + return; + } + + if (object_path.value() != heart_rate_control_point_path_) { + error_callback.Run("org.bluez.Error.NotPermitted", + "Writes of this value are not allowed"); + return; + } + + DCHECK(heart_rate_control_point_properties_.get()); + if (action_extra_requests_.find("WriteValue") != + action_extra_requests_.end()) { + DelayedCallback* delayed = action_extra_requests_["WriteValue"]; + delayed->delay_--; + error_callback.Run("org.bluez.Error.InProgress", + "Another write is in progress"); + if (delayed->delay_ == 0) { + delayed->callback_.Run(); + action_extra_requests_.erase("WriteValue"); + delete delayed; + } + return; + } + base::Closure completed_callback; + if (value.size() != 1) { + completed_callback = + base::Bind(error_callback, "org.bluez.Error.InvalidValueLength", + "Invalid length for write"); + } else if (value[0] > 1) { + completed_callback = base::Bind(error_callback, "org.bluez.Error.Failed", + "Invalid value given for write"); + } else if (value[0] == 1) { + // TODO(jamuraa): make this happen when the callback happens + calories_burned_ = 0; + completed_callback = callback; + } + + if (extra_requests_ > 0) { + action_extra_requests_["WriteValue"] = + new DelayedCallback(completed_callback, extra_requests_); + return; + } + completed_callback.Run(); +} + +void FakeBluetoothGattCharacteristicClient::StartNotify( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + if (!IsHeartRateVisible()) { + error_callback.Run(kUnknownCharacteristicError, ""); + return; + } + + if (object_path.value() != heart_rate_measurement_path_) { + error_callback.Run("org.bluez.Error.NotSupported", + "This characteristic does not support notifications"); + return; + } + + if (heart_rate_measurement_properties_->notifying.value()) { + error_callback.Run("org.bluez.Error.InProgress", + "Characteristic already notifying"); + return; + } + + heart_rate_measurement_properties_->notifying.ReplaceValue(true); + ScheduleHeartRateMeasurementValueChange(); + + // Respond asynchronously. + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, callback, + base::TimeDelta::FromMilliseconds(kStartNotifyResponseIntervalMs)); +} + +void FakeBluetoothGattCharacteristicClient::StopNotify( + const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + if (!IsHeartRateVisible()) { + error_callback.Run(kUnknownCharacteristicError, ""); + return; + } + + if (object_path.value() != heart_rate_measurement_path_) { + error_callback.Run("org.bluez.Error.NotSupported", + "This characteristic does not support notifications"); + return; + } + + if (!heart_rate_measurement_properties_->notifying.value()) { + error_callback.Run("org.bluez.Error.Failed", "Not notifying"); + return; + } + + heart_rate_measurement_properties_->notifying.ReplaceValue(false); + + callback.Run(); +} + +void FakeBluetoothGattCharacteristicClient::ExposeHeartRateCharacteristics( + const dbus::ObjectPath& service_path) { + if (IsHeartRateVisible()) { + VLOG(2) << "Fake Heart Rate characteristics are already visible."; + return; + } + + VLOG(2) << "Exposing fake Heart Rate characteristics."; + + std::vector<std::string> flags; + + // ==== Heart Rate Measurement Characteristic ==== + heart_rate_measurement_path_ = + service_path.value() + "/" + kHeartRateMeasurementPathComponent; + heart_rate_measurement_properties_.reset(new Properties( + base::Bind(&FakeBluetoothGattCharacteristicClient::OnPropertyChanged, + weak_ptr_factory_.GetWeakPtr(), + dbus::ObjectPath(heart_rate_measurement_path_)))); + heart_rate_measurement_properties_->uuid.ReplaceValue( + kHeartRateMeasurementUUID); + heart_rate_measurement_properties_->service.ReplaceValue(service_path); + flags.push_back(bluetooth_gatt_characteristic::kFlagNotify); + heart_rate_measurement_properties_->flags.ReplaceValue(flags); + + // ==== Body Sensor Location Characteristic ==== + body_sensor_location_path_ = + service_path.value() + "/" + kBodySensorLocationPathComponent; + body_sensor_location_properties_.reset(new Properties( + base::Bind(&FakeBluetoothGattCharacteristicClient::OnPropertyChanged, + weak_ptr_factory_.GetWeakPtr(), + dbus::ObjectPath(body_sensor_location_path_)))); + body_sensor_location_properties_->uuid.ReplaceValue(kBodySensorLocationUUID); + body_sensor_location_properties_->service.ReplaceValue(service_path); + flags.clear(); + flags.push_back(bluetooth_gatt_characteristic::kFlagRead); + body_sensor_location_properties_->flags.ReplaceValue(flags); + + // ==== Heart Rate Control Point Characteristic ==== + heart_rate_control_point_path_ = + service_path.value() + "/" + kHeartRateControlPointPathComponent; + heart_rate_control_point_properties_.reset(new Properties( + base::Bind(&FakeBluetoothGattCharacteristicClient::OnPropertyChanged, + weak_ptr_factory_.GetWeakPtr(), + dbus::ObjectPath(heart_rate_control_point_path_)))); + heart_rate_control_point_properties_->uuid.ReplaceValue( + kHeartRateControlPointUUID); + heart_rate_control_point_properties_->service.ReplaceValue(service_path); + flags.clear(); + flags.push_back(bluetooth_gatt_characteristic::kFlagWrite); + heart_rate_control_point_properties_->flags.ReplaceValue(flags); + + heart_rate_visible_ = true; + + NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_measurement_path_)); + NotifyCharacteristicAdded(dbus::ObjectPath(body_sensor_location_path_)); + NotifyCharacteristicAdded(dbus::ObjectPath(heart_rate_control_point_path_)); + + // Expose CCC descriptor for Heart Rate Measurement characteristic. + FakeBluetoothGattDescriptorClient* descriptor_client = + static_cast<FakeBluetoothGattDescriptorClient*>( + DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()); + dbus::ObjectPath ccc_path(descriptor_client->ExposeDescriptor( + dbus::ObjectPath(heart_rate_measurement_path_), + FakeBluetoothGattDescriptorClient:: + kClientCharacteristicConfigurationUUID)); + DCHECK(ccc_path.IsValid()); + heart_rate_measurement_ccc_desc_path_ = ccc_path.value(); + + std::vector<dbus::ObjectPath> desc_paths; + desc_paths.push_back(ccc_path); + + heart_rate_measurement_properties_->descriptors.ReplaceValue(desc_paths); +} + +void FakeBluetoothGattCharacteristicClient::HideHeartRateCharacteristics() { + VLOG(2) << "Hiding fake Heart Rate characteristics."; + + // Hide the descriptors. + FakeBluetoothGattDescriptorClient* descriptor_client = + static_cast<FakeBluetoothGattDescriptorClient*>( + DBusThreadManager::Get()->GetBluetoothGattDescriptorClient()); + descriptor_client->HideDescriptor( + dbus::ObjectPath(heart_rate_measurement_ccc_desc_path_)); + + // Notify the observers before deleting the properties structures so that they + // can be accessed from the observer method. + NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_measurement_path_)); + NotifyCharacteristicRemoved(dbus::ObjectPath(body_sensor_location_path_)); + NotifyCharacteristicRemoved(dbus::ObjectPath(heart_rate_control_point_path_)); + + heart_rate_measurement_properties_.reset(); + body_sensor_location_properties_.reset(); + heart_rate_control_point_properties_.reset(); + + heart_rate_measurement_path_.clear(); + body_sensor_location_path_.clear(); + heart_rate_control_point_path_.clear(); + heart_rate_visible_ = false; +} + +void FakeBluetoothGattCharacteristicClient::SetExtraProcessing( + size_t requests) { + extra_requests_ = requests; + if (extra_requests_ == 0) { + for (const auto& it : action_extra_requests_) { + it.second->callback_.Run(); + delete it.second; + } + action_extra_requests_.clear(); + return; + } + VLOG(2) << "Requests SLOW now, " << requests << " InProgress errors each."; +} + +size_t FakeBluetoothGattCharacteristicClient::GetExtraProcessing() const { + return extra_requests_; +} + +dbus::ObjectPath +FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementPath() const { + return dbus::ObjectPath(heart_rate_measurement_path_); +} + +dbus::ObjectPath +FakeBluetoothGattCharacteristicClient::GetBodySensorLocationPath() const { + return dbus::ObjectPath(body_sensor_location_path_); +} + +dbus::ObjectPath +FakeBluetoothGattCharacteristicClient::GetHeartRateControlPointPath() const { + return dbus::ObjectPath(heart_rate_control_point_path_); +} + +void FakeBluetoothGattCharacteristicClient::OnPropertyChanged( + const dbus::ObjectPath& object_path, + const std::string& property_name) { + VLOG(2) << "Characteristic property changed: " << object_path.value() << ": " + << property_name; + + FOR_EACH_OBSERVER( + BluetoothGattCharacteristicClient::Observer, observers_, + GattCharacteristicPropertyChanged(object_path, property_name)); +} + +void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicAdded( + const dbus::ObjectPath& object_path) { + VLOG(2) << "GATT characteristic added: " << object_path.value(); + FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, + GattCharacteristicAdded(object_path)); +} + +void FakeBluetoothGattCharacteristicClient::NotifyCharacteristicRemoved( + const dbus::ObjectPath& object_path) { + VLOG(2) << "GATT characteristic removed: " << object_path.value(); + FOR_EACH_OBSERVER(BluetoothGattCharacteristicClient::Observer, observers_, + GattCharacteristicRemoved(object_path)); +} + +void FakeBluetoothGattCharacteristicClient:: + ScheduleHeartRateMeasurementValueChange() { + if (!IsHeartRateVisible()) + return; + + // Don't send updates if the characteristic is not notifying. + if (!heart_rate_measurement_properties_->notifying.value()) + return; + + VLOG(2) << "Updating heart rate value."; + std::vector<uint8> measurement = GetHeartRateMeasurementValue(); + heart_rate_measurement_properties_->value.ReplaceValue(measurement); + + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&FakeBluetoothGattCharacteristicClient:: + ScheduleHeartRateMeasurementValueChange, + weak_ptr_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds( + kHeartRateMeasurementNotificationIntervalMs)); +} + +void FakeBluetoothGattCharacteristicClient::DelayedReadValueCallback( + const dbus::ObjectPath& object_path, + const ValueCallback& callback, + const std::vector<uint8_t>& value) { + Properties* properties = GetProperties(object_path); + DCHECK(properties); + + properties->value.ReplaceValue(value); + callback.Run(value); +} + +std::vector<uint8> +FakeBluetoothGattCharacteristicClient::GetHeartRateMeasurementValue() { + // TODO(armansito): We should make sure to properly pack this struct to ensure + // correct byte alignment and endianness. It doesn't matter too much right now + // as this is a fake and GCC on Linux seems to do the right thing. + struct { + uint8 flags; + uint8 bpm; + uint16 energy_expanded; + uint16 rr_interval; + } value; + + // Flags in LSB: 0 11 1 1 000 + // | | | | | + // 8-bit bpm format -- | | | | + // Sensor contact supported -- | | | + // Energy expanded field present -- | | + // RR-Interval values present ------- | + // Reserved for future use ------------ + value.flags = 0x0; + value.flags |= (0x03 << 1); + value.flags |= (0x01 << 3); + value.flags |= (0x01 << 4); + + // Pick a value between 117 bpm and 153 bpm for heart rate. + value.bpm = static_cast<uint8>(base::RandInt(117, 153)); + + // Total calories burned in kJoules since the last reset. Increment this by 1 + // every time. It's fine if it overflows: it becomes 0 when the user resets + // the heart rate monitor (or pretend that he had a lot of cheeseburgers). + value.energy_expanded = calories_burned_++; + + // Include one RR-Interval value, in seconds. + value.rr_interval = 60 / value.bpm; + + // Return the bytes in an array. + uint8* bytes = reinterpret_cast<uint8*>(&value); + std::vector<uint8> return_value; + return_value.assign(bytes, bytes + sizeof(value)); + return return_value; +} + +bool FakeBluetoothGattCharacteristicClient::IsHeartRateVisible() const { + DCHECK(heart_rate_visible_ != heart_rate_measurement_path_.empty()); + DCHECK(heart_rate_visible_ != body_sensor_location_path_.empty()); + DCHECK(heart_rate_visible_ != heart_rate_control_point_path_.empty()); + DCHECK(heart_rate_visible_ == !!heart_rate_measurement_properties_.get()); + DCHECK(heart_rate_visible_ == !!body_sensor_location_properties_.get()); + DCHECK(heart_rate_visible_ == !!heart_rate_control_point_properties_.get()); + return heart_rate_visible_; +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h b/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h new file mode 100644 index 0000000..dc1eab3 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h @@ -0,0 +1,194 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_CHARACTERISTIC_CLIENT_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_CHARACTERISTIC_CLIENT_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_gatt_characteristic_client.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +// FakeBluetoothGattCharacteristicClient simulates the behavior of the +// Bluetooth Daemon GATT characteristic objects and is used in test cases in +// place of a mock and on the Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothGattCharacteristicClient + : public BluetoothGattCharacteristicClient { + public: + struct Properties : public BluetoothGattCharacteristicClient::Properties { + explicit Properties(const PropertyChangedCallback& callback); + ~Properties() override; + + // dbus::PropertySet override + void Get(dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) override; + void GetAll() override; + void Set(dbus::PropertyBase* property, + dbus::PropertySet::SetCallback callback) override; + }; + + FakeBluetoothGattCharacteristicClient(); + ~FakeBluetoothGattCharacteristicClient() override; + + // DBusClient override. + void Init(dbus::Bus* bus) override; + + // BluetoothGattCharacteristicClient overrides. + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + std::vector<dbus::ObjectPath> GetCharacteristics() override; + Properties* GetProperties(const dbus::ObjectPath& object_path) override; + void ReadValue(const dbus::ObjectPath& object_path, + const ValueCallback& callback, + const ErrorCallback& error_callback) override; + void WriteValue(const dbus::ObjectPath& object_path, + const std::vector<uint8>& value, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void StartNotify(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void StopNotify(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + + // Makes the group of characteristics belonging to a particular GATT based + // profile available under the GATT service with object path |service_path|. + // Characteristic paths are hierarchical to service paths. + void ExposeHeartRateCharacteristics(const dbus::ObjectPath& service_path); + void HideHeartRateCharacteristics(); + + // Returns whether or not the heart rate characteristics are visible and + // performs the appropriate assertions. + bool IsHeartRateVisible() const; + + // Makes this characteristic client really slow. + // So slow, that it is guaranteed that |requests| requests will + // come in while the client is doing the previous request. + // Setting |requests| to zero will cause all delayed actions to + // complete immediately. + void SetExtraProcessing(size_t requests); + + size_t GetExtraProcessing() const; + + // Sets whether the client is authorized or not. + // Defaults to authorized. + void SetAuthorized(bool authorized) { authorized_ = authorized; } + + // Get the current Authorization state. + bool IsAuthorized() const { return authorized_; } + + // Whether the client is Authenticated + // Defaults to authenticated. + void SetAuthenticated(bool authenticated) { authenticated_ = authenticated; } + + // Get the current Authenticated state. + bool IsAuthenticated() const { return authenticated_; } + + // Returns the current object paths of exposed characteristics. If the + // characteristic is not visible, returns an invalid empty path. + dbus::ObjectPath GetHeartRateMeasurementPath() const; + dbus::ObjectPath GetBodySensorLocationPath() const; + dbus::ObjectPath GetHeartRateControlPointPath() const; + + // Object path components and UUIDs of GATT characteristics. + // Heart Rate Service: + static const char kHeartRateMeasurementPathComponent[]; + static const char kHeartRateMeasurementUUID[]; + static const char kBodySensorLocationPathComponent[]; + static const char kBodySensorLocationUUID[]; + static const char kHeartRateControlPointPathComponent[]; + static const char kHeartRateControlPointUUID[]; + + private: + // Property callback passed when we create Properties structures. + void OnPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name); + + // Notifies observers. + void NotifyCharacteristicAdded(const dbus::ObjectPath& object_path); + void NotifyCharacteristicRemoved(const dbus::ObjectPath& object_path); + + // Schedules a heart rate measurement value change, if the heart rate + // characteristics are visible. + void ScheduleHeartRateMeasurementValueChange(); + + // Returns a random Heart Rate Measurement value. All the fields of the value + // are populated according to the the fake behavior. The measurement value + // is a random value within a reasonable range. + std::vector<uint8> GetHeartRateMeasurementValue(); + + // Callback that executes a delayed ReadValue action by updating the + // appropriate "Value" property and invoking the ValueCallback. + void DelayedReadValueCallback(const dbus::ObjectPath& object_path, + const ValueCallback& callback, + const std::vector<uint8_t>& value); + + // If true, characteristics of the Heart Rate Service are visible. Use + // IsHeartRateVisible() to check the value. + bool heart_rate_visible_; + + // If true, the client is authorized to read and write. + bool authorized_; + + // If true, the client is authenticated. + bool authenticated_; + + // Total calories burned, used for the Heart Rate Measurement characteristic. + uint16 calories_burned_; + + // Static properties returned for simulated characteristics for the Heart + // Rate Service. These pointers are not NULL only if the characteristics are + // actually exposed. + scoped_ptr<Properties> heart_rate_measurement_properties_; + scoped_ptr<Properties> body_sensor_location_properties_; + scoped_ptr<Properties> heart_rate_control_point_properties_; + + // Object paths of the exposed characteristics. If a characteristic is not + // exposed, these will be empty. + std::string heart_rate_measurement_path_; + std::string heart_rate_measurement_ccc_desc_path_; + std::string body_sensor_location_path_; + std::string heart_rate_control_point_path_; + + // Number of extra requests that need to come in simulating slowness. + size_t extra_requests_; + + // Current countdowns for extra requests for various actions. + struct DelayedCallback { + public: + DelayedCallback(base::Closure callback, size_t delay); + ~DelayedCallback(); + + base::Closure callback_; + size_t delay_; + }; + + // Map of delayed callbacks. + std::map<std::string, DelayedCallback*> action_extra_requests_; + + // List of observers interested in event notifications from us. + base::ObserverList<Observer> observers_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<FakeBluetoothGattCharacteristicClient> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothGattCharacteristicClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_CHARACTERISTIC_CLIENT_H_ diff --git a/chromeos/dbus/fake_bluetooth_gatt_characteristic_service_provider.cc b/chromeos/dbus/fake_bluetooth_gatt_characteristic_service_provider.cc new file mode 100644 index 0000000..39132d9 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_gatt_characteristic_service_provider.cc @@ -0,0 +1,105 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_gatt_characteristic_service_provider.h" + +#include "base/logging.h" +#include "base/strings/string_util.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_gatt_manager_client.h" + +namespace chromeos { + +FakeBluetoothGattCharacteristicServiceProvider:: + FakeBluetoothGattCharacteristicServiceProvider( + const dbus::ObjectPath& object_path, + Delegate* delegate, + const std::string& uuid, + const std::vector<std::string>& flags, + const std::vector<std::string>& permissions, + const dbus::ObjectPath& service_path) + : object_path_(object_path), + uuid_(uuid), + service_path_(service_path), + delegate_(delegate) { + VLOG(1) << "Creating Bluetooth GATT characteristic: " << object_path_.value(); + + DCHECK(object_path_.IsValid()); + DCHECK(service_path_.IsValid()); + DCHECK(!uuid.empty()); + DCHECK(delegate_); + DCHECK(base::StartsWith(object_path_.value(), service_path_.value() + "/", + base::CompareCase::SENSITIVE)); + + // TODO(armansito): Do something with |flags| and |permissions|. + + FakeBluetoothGattManagerClient* fake_bluetooth_gatt_manager_client = + static_cast<FakeBluetoothGattManagerClient*>( + DBusThreadManager::Get()->GetBluetoothGattManagerClient()); + fake_bluetooth_gatt_manager_client->RegisterCharacteristicServiceProvider( + this); +} + +FakeBluetoothGattCharacteristicServiceProvider:: + ~FakeBluetoothGattCharacteristicServiceProvider() { + VLOG(1) << "Cleaning up Bluetooth GATT characteristic: " + << object_path_.value(); + + FakeBluetoothGattManagerClient* fake_bluetooth_gatt_manager_client = + static_cast<FakeBluetoothGattManagerClient*>( + DBusThreadManager::Get()->GetBluetoothGattManagerClient()); + fake_bluetooth_gatt_manager_client->UnregisterCharacteristicServiceProvider( + this); +} + +void FakeBluetoothGattCharacteristicServiceProvider::SendValueChanged( + const std::vector<uint8>& value) { + VLOG(1) << "Sent characteristic value changed: " << object_path_.value() + << " UUID: " << uuid_; +} + +void FakeBluetoothGattCharacteristicServiceProvider::GetValue( + const Delegate::ValueCallback& callback, + const Delegate::ErrorCallback& error_callback) { + VLOG(1) << "GATT characteristic value Get request: " << object_path_.value() + << " UUID: " << uuid_; + + // Check if this characteristic is registered. + FakeBluetoothGattManagerClient* fake_bluetooth_gatt_manager_client = + static_cast<FakeBluetoothGattManagerClient*>( + DBusThreadManager::Get()->GetBluetoothGattManagerClient()); + if (!fake_bluetooth_gatt_manager_client->IsServiceRegistered(service_path_)) { + VLOG(1) << "GATT characteristic not registered."; + error_callback.Run(); + return; + } + + // Pass on to the delegate. + DCHECK(delegate_); + delegate_->GetCharacteristicValue(callback, error_callback); +} + +void FakeBluetoothGattCharacteristicServiceProvider::SetValue( + const std::vector<uint8>& value, + const base::Closure& callback, + const Delegate::ErrorCallback& error_callback) { + VLOG(1) << "GATT characteristic value Set request: " << object_path_.value() + << " UUID: " << uuid_; + + // Check if this characteristic is registered. + FakeBluetoothGattManagerClient* fake_bluetooth_gatt_manager_client = + static_cast<FakeBluetoothGattManagerClient*>( + DBusThreadManager::Get()->GetBluetoothGattManagerClient()); + if (!fake_bluetooth_gatt_manager_client->IsServiceRegistered(service_path_)) { + VLOG(1) << "GATT characteristic not registered."; + error_callback.Run(); + return; + } + + // Pass on to the delegate. + DCHECK(delegate_); + delegate_->SetCharacteristicValue(value, callback, error_callback); +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_gatt_characteristic_service_provider.h b/chromeos/dbus/fake_bluetooth_gatt_characteristic_service_provider.h new file mode 100644 index 0000000..8a8440b --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_gatt_characteristic_service_provider.h @@ -0,0 +1,66 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_CHARACTERISTIC_SERVICE_PROVIDER_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_CHARACTERISTIC_SERVICE_PROVIDER_H_ + +#include <string> +#include <vector> + +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_gatt_characteristic_service_provider.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// FakeBluetoothGattCharacteristicServiceProvider simulates behavior of a local +// GATT characteristic object and is used both in test cases in place of a mock +// and on the Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothGattCharacteristicServiceProvider + : public BluetoothGattCharacteristicServiceProvider { + public: + FakeBluetoothGattCharacteristicServiceProvider( + const dbus::ObjectPath& object_path, + Delegate* delegate, + const std::string& uuid, + const std::vector<std::string>& flags, + const std::vector<std::string>& permissions, + const dbus::ObjectPath& service_path); + ~FakeBluetoothGattCharacteristicServiceProvider() override; + + // BluetoothGattCharacteristicServiceProvider override. + void SendValueChanged(const std::vector<uint8>& value) override; + + // Methods to simulate value get/set requests issued from a remote device. The + // methods do nothing, if the associated service was not registered with the + // GATT manager. + void GetValue(const Delegate::ValueCallback& callback, + const Delegate::ErrorCallback& error_callback); + void SetValue(const std::vector<uint8>& value, + const base::Closure& callback, + const Delegate::ErrorCallback& error_callback); + + const dbus::ObjectPath& object_path() const { return object_path_; } + const std::string& uuid() const { return uuid_; } + const dbus::ObjectPath& service_path() const { return service_path_; } + + private: + // D-Bus object path of the fake GATT characteristic. + dbus::ObjectPath object_path_; + + // 128-bit GATT characteristic UUID. + std::string uuid_; + + // Object path of the service that this characteristic belongs to. + dbus::ObjectPath service_path_; + + // The delegate that method calls are passed on to. + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothGattCharacteristicServiceProvider); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_CHARACTERISTIC_SERVICE_PROVIDER_H_ diff --git a/chromeos/dbus/fake_bluetooth_gatt_descriptor_client.cc b/chromeos/dbus/fake_bluetooth_gatt_descriptor_client.cc new file mode 100644 index 0000000..2e9cdf0 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_gatt_descriptor_client.cc @@ -0,0 +1,209 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_gatt_descriptor_client.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "chromeos/dbus/bluetooth_gatt_characteristic_client.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +const char FakeBluetoothGattDescriptorClient:: + kClientCharacteristicConfigurationPathComponent[] = "desc0000"; +const char FakeBluetoothGattDescriptorClient:: + kClientCharacteristicConfigurationUUID[] = + "00002902-0000-1000-8000-00805f9b34fb"; + +FakeBluetoothGattDescriptorClient::Properties::Properties( + const PropertyChangedCallback& callback) + : BluetoothGattDescriptorClient::Properties( + NULL, + bluetooth_gatt_descriptor::kBluetoothGattDescriptorInterface, + callback) {} + +FakeBluetoothGattDescriptorClient::Properties::~Properties() {} + +void FakeBluetoothGattDescriptorClient::Properties::Get( + dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) { + VLOG(1) << "Get " << property->name(); + callback.Run(true); +} + +void FakeBluetoothGattDescriptorClient::Properties::GetAll() { + VLOG(1) << "GetAll"; +} + +void FakeBluetoothGattDescriptorClient::Properties::Set( + dbus::PropertyBase* property, + dbus::PropertySet::SetCallback callback) { + VLOG(1) << "Set " << property->name(); + callback.Run(false); +} + +FakeBluetoothGattDescriptorClient::DescriptorData::DescriptorData() {} + +FakeBluetoothGattDescriptorClient::DescriptorData::~DescriptorData() {} + +FakeBluetoothGattDescriptorClient::FakeBluetoothGattDescriptorClient() + : weak_ptr_factory_(this) {} + +FakeBluetoothGattDescriptorClient::~FakeBluetoothGattDescriptorClient() { + for (PropertiesMap::iterator iter = properties_.begin(); + iter != properties_.end(); iter++) + delete iter->second; +} + +void FakeBluetoothGattDescriptorClient::Init(dbus::Bus* bus) {} + +void FakeBluetoothGattDescriptorClient::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void FakeBluetoothGattDescriptorClient::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +std::vector<dbus::ObjectPath> +FakeBluetoothGattDescriptorClient::GetDescriptors() { + std::vector<dbus::ObjectPath> descriptors; + for (PropertiesMap::const_iterator iter = properties_.begin(); + iter != properties_.end(); ++iter) { + descriptors.push_back(iter->first); + } + return descriptors; +} + +FakeBluetoothGattDescriptorClient::Properties* +FakeBluetoothGattDescriptorClient::GetProperties( + const dbus::ObjectPath& object_path) { + PropertiesMap::const_iterator iter = properties_.find(object_path); + if (iter == properties_.end()) + return NULL; + return iter->second->properties.get(); +} + +void FakeBluetoothGattDescriptorClient::ReadValue( + const dbus::ObjectPath& object_path, + const ValueCallback& callback, + const ErrorCallback& error_callback) { + PropertiesMap::iterator iter = properties_.find(object_path); + if (iter == properties_.end()) { + error_callback.Run(kUnknownDescriptorError, ""); + return; + } + + // Assign the value of the descriptor as necessary + Properties* properties = iter->second->properties.get(); + if (properties->uuid.value() == kClientCharacteristicConfigurationUUID) { + BluetoothGattCharacteristicClient::Properties* chrc_props = + DBusThreadManager::Get() + ->GetBluetoothGattCharacteristicClient() + ->GetProperties(properties->characteristic.value()); + DCHECK(chrc_props); + + uint8_t value_byte = chrc_props->notifying.value() ? 0x01 : 0x00; + const std::vector<uint8_t>& cur_value = properties->value.value(); + + if (!cur_value.size() || cur_value[0] != value_byte) { + std::vector<uint8_t> value = {value_byte, 0x00}; + properties->value.ReplaceValue(value); + } + } + + callback.Run(iter->second->properties->value.value()); +} + +void FakeBluetoothGattDescriptorClient::WriteValue( + const dbus::ObjectPath& object_path, + const std::vector<uint8>& value, + const base::Closure& callback, + const ErrorCallback& error_callback) { + if (properties_.find(object_path) == properties_.end()) { + error_callback.Run(kUnknownDescriptorError, ""); + return; + } + + // Since the only fake descriptor is "Client Characteristic Configuration" + // and BlueZ doesn't allow writing to it, return failure. + error_callback.Run("org.bluez.Error.NotPermitted", + "Writing to the Client Characteristic Configuration " + "descriptor not allowed"); +} + +dbus::ObjectPath FakeBluetoothGattDescriptorClient::ExposeDescriptor( + const dbus::ObjectPath& characteristic_path, + const std::string& uuid) { + if (uuid != kClientCharacteristicConfigurationUUID) { + VLOG(2) << "Unsupported UUID: " << uuid; + return dbus::ObjectPath(); + } + + // CCC descriptor is the only one supported at the moment. + DCHECK(characteristic_path.IsValid()); + dbus::ObjectPath object_path(characteristic_path.value() + "/" + + kClientCharacteristicConfigurationPathComponent); + DCHECK(object_path.IsValid()); + PropertiesMap::const_iterator iter = properties_.find(object_path); + if (iter != properties_.end()) { + VLOG(1) << "Descriptor already exposed: " << object_path.value(); + return dbus::ObjectPath(); + } + + Properties* properties = new Properties( + base::Bind(&FakeBluetoothGattDescriptorClient::OnPropertyChanged, + weak_ptr_factory_.GetWeakPtr(), object_path)); + properties->uuid.ReplaceValue(uuid); + properties->characteristic.ReplaceValue(characteristic_path); + + DescriptorData* data = new DescriptorData(); + data->properties.reset(properties); + + properties_[object_path] = data; + + NotifyDescriptorAdded(object_path); + + return object_path; +} + +void FakeBluetoothGattDescriptorClient::HideDescriptor( + const dbus::ObjectPath& descriptor_path) { + PropertiesMap::iterator iter = properties_.find(descriptor_path); + if (iter == properties_.end()) { + VLOG(1) << "Descriptor not exposed: " << descriptor_path.value(); + return; + } + + NotifyDescriptorRemoved(descriptor_path); + + delete iter->second; + properties_.erase(iter); +} + +void FakeBluetoothGattDescriptorClient::OnPropertyChanged( + const dbus::ObjectPath& object_path, + const std::string& property_name) { + VLOG(2) << "Descriptor property changed: " << object_path.value() << ": " + << property_name; + + FOR_EACH_OBSERVER(BluetoothGattDescriptorClient::Observer, observers_, + GattDescriptorPropertyChanged(object_path, property_name)); +} + +void FakeBluetoothGattDescriptorClient::NotifyDescriptorAdded( + const dbus::ObjectPath& object_path) { + FOR_EACH_OBSERVER(BluetoothGattDescriptorClient::Observer, observers_, + GattDescriptorAdded(object_path)); +} + +void FakeBluetoothGattDescriptorClient::NotifyDescriptorRemoved( + const dbus::ObjectPath& object_path) { + FOR_EACH_OBSERVER(BluetoothGattDescriptorClient::Observer, observers_, + GattDescriptorRemoved(object_path)); +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_gatt_descriptor_client.h b/chromeos/dbus/fake_bluetooth_gatt_descriptor_client.h new file mode 100644 index 0000000..e6106c1 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_gatt_descriptor_client.h @@ -0,0 +1,103 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_DESCRIPTOR_CLIENT_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_DESCRIPTOR_CLIENT_H_ + +#include <map> +#include <string> + +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_gatt_descriptor_client.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// FakeBluetoothGattDescriptorClient simulates the behavior of the Bluetooth +// Daemon GATT characteristic descriptor objects and is used in test cases in +// place of a mock and on the Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothGattDescriptorClient + : public BluetoothGattDescriptorClient { + public: + struct Properties : public BluetoothGattDescriptorClient::Properties { + explicit Properties(const PropertyChangedCallback& callback); + ~Properties() override; + + // dbus::PropertySet override + void Get(dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) override; + void GetAll() override; + void Set(dbus::PropertyBase* property, + dbus::PropertySet::SetCallback callback) override; + }; + + FakeBluetoothGattDescriptorClient(); + ~FakeBluetoothGattDescriptorClient() override; + + // DBusClient override. + void Init(dbus::Bus* bus) override; + + // BluetoothGattDescriptorClient overrides. + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + std::vector<dbus::ObjectPath> GetDescriptors() override; + Properties* GetProperties(const dbus::ObjectPath& object_path) override; + void ReadValue(const dbus::ObjectPath& object_path, + const ValueCallback& callback, + const ErrorCallback& error_callback) override; + void WriteValue(const dbus::ObjectPath& object_path, + const std::vector<uint8>& value, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + + // Makes the descriptor with the UUID |uuid| visible under the characteristic + // with object path |characteristic_path|. Descriptor object paths are + // hierarchical to their characteristics. |uuid| must belong to a descriptor + // for which there is a constant defined below, otherwise this method has no + // effect. Returns the object path of the created descriptor. In the no-op + // case, returns an invalid path. + dbus::ObjectPath ExposeDescriptor(const dbus::ObjectPath& characteristic_path, + const std::string& uuid); + void HideDescriptor(const dbus::ObjectPath& descriptor_path); + + // Object path components and UUIDs of GATT characteristic descriptors. + static const char kClientCharacteristicConfigurationPathComponent[]; + static const char kClientCharacteristicConfigurationUUID[]; + + private: + // Property callback passed when we create Properties structures. + void OnPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name); + + // Notifies observers. + void NotifyDescriptorAdded(const dbus::ObjectPath& object_path); + void NotifyDescriptorRemoved(const dbus::ObjectPath& object_path); + + // Mapping from object paths to Properties structures. + struct DescriptorData { + DescriptorData(); + ~DescriptorData(); + + scoped_ptr<Properties> properties; + }; + typedef std::map<dbus::ObjectPath, DescriptorData*> PropertiesMap; + PropertiesMap properties_; + + // List of observers interested in event notifications from us. + base::ObserverList<Observer> observers_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<FakeBluetoothGattDescriptorClient> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothGattDescriptorClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_DESCRIPTOR_CLIENT_H_ diff --git a/chromeos/dbus/fake_bluetooth_gatt_descriptor_service_provider.cc b/chromeos/dbus/fake_bluetooth_gatt_descriptor_service_provider.cc new file mode 100644 index 0000000..ad002ab --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_gatt_descriptor_service_provider.cc @@ -0,0 +1,121 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_gatt_descriptor_service_provider.h" + +#include "base/logging.h" +#include "base/strings/string_util.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_gatt_characteristic_service_provider.h" +#include "chromeos/dbus/fake_bluetooth_gatt_manager_client.h" + +namespace chromeos { + +FakeBluetoothGattDescriptorServiceProvider:: + FakeBluetoothGattDescriptorServiceProvider( + const dbus::ObjectPath& object_path, + Delegate* delegate, + const std::string& uuid, + const std::vector<std::string>& permissions, + const dbus::ObjectPath& characteristic_path) + : object_path_(object_path), + uuid_(uuid), + characteristic_path_(characteristic_path), + delegate_(delegate) { + VLOG(1) << "Creating Bluetooth GATT descriptor: " << object_path_.value(); + + DCHECK(object_path_.IsValid()); + DCHECK(characteristic_path_.IsValid()); + DCHECK(!uuid.empty()); + DCHECK(delegate_); + DCHECK(base::StartsWith(object_path_.value(), + characteristic_path_.value() + "/", + base::CompareCase::SENSITIVE)); + + // TODO(armansito): Do something with |permissions|. + + FakeBluetoothGattManagerClient* fake_bluetooth_gatt_manager_client = + static_cast<FakeBluetoothGattManagerClient*>( + DBusThreadManager::Get()->GetBluetoothGattManagerClient()); + fake_bluetooth_gatt_manager_client->RegisterDescriptorServiceProvider(this); +} + +FakeBluetoothGattDescriptorServiceProvider:: + ~FakeBluetoothGattDescriptorServiceProvider() { + VLOG(1) << "Cleaning up Bluetooth GATT descriptor: " << object_path_.value(); + + FakeBluetoothGattManagerClient* fake_bluetooth_gatt_manager_client = + static_cast<FakeBluetoothGattManagerClient*>( + DBusThreadManager::Get()->GetBluetoothGattManagerClient()); + fake_bluetooth_gatt_manager_client->UnregisterDescriptorServiceProvider(this); +} + +void FakeBluetoothGattDescriptorServiceProvider::SendValueChanged( + const std::vector<uint8>& value) { + VLOG(1) << "Sent descriptor value changed: " << object_path_.value() + << " UUID: " << uuid_; +} + +void FakeBluetoothGattDescriptorServiceProvider::GetValue( + const Delegate::ValueCallback& callback, + const Delegate::ErrorCallback& error_callback) { + VLOG(1) << "GATT descriptor value Get request: " << object_path_.value() + << " UUID: " << uuid_; + + // Check if this descriptor is registered. + FakeBluetoothGattManagerClient* fake_bluetooth_gatt_manager_client = + static_cast<FakeBluetoothGattManagerClient*>( + DBusThreadManager::Get()->GetBluetoothGattManagerClient()); + FakeBluetoothGattCharacteristicServiceProvider* characteristic = + fake_bluetooth_gatt_manager_client->GetCharacteristicServiceProvider( + characteristic_path_); + if (!characteristic) { + VLOG(1) << "GATT characteristic for descriptor does not exist: " + << characteristic_path_.value(); + return; + } + if (!fake_bluetooth_gatt_manager_client->IsServiceRegistered( + characteristic->service_path())) { + VLOG(1) << "GATT descriptor not registered."; + error_callback.Run(); + return; + } + + // Pass on to the delegate. + DCHECK(delegate_); + delegate_->GetDescriptorValue(callback, error_callback); +} + +void FakeBluetoothGattDescriptorServiceProvider::SetValue( + const std::vector<uint8>& value, + const base::Closure& callback, + const Delegate::ErrorCallback& error_callback) { + VLOG(1) << "GATT descriptor value Set request: " << object_path_.value() + << " UUID: " << uuid_; + + // Check if this descriptor is registered. + FakeBluetoothGattManagerClient* fake_bluetooth_gatt_manager_client = + static_cast<FakeBluetoothGattManagerClient*>( + DBusThreadManager::Get()->GetBluetoothGattManagerClient()); + FakeBluetoothGattCharacteristicServiceProvider* characteristic = + fake_bluetooth_gatt_manager_client->GetCharacteristicServiceProvider( + characteristic_path_); + if (!characteristic) { + VLOG(1) << "GATT characteristic for descriptor does not exist: " + << characteristic_path_.value(); + return; + } + if (!fake_bluetooth_gatt_manager_client->IsServiceRegistered( + characteristic->service_path())) { + VLOG(1) << "GATT descriptor not registered."; + error_callback.Run(); + return; + } + + // Pass on to the delegate. + DCHECK(delegate_); + delegate_->SetDescriptorValue(value, callback, error_callback); +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_gatt_descriptor_service_provider.h b/chromeos/dbus/fake_bluetooth_gatt_descriptor_service_provider.h new file mode 100644 index 0000000..054c21d --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_gatt_descriptor_service_provider.h @@ -0,0 +1,67 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_DESCRIPTOR_SERVICE_PROVIDER_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_DESCRIPTOR_SERVICE_PROVIDER_H_ + +#include <string> +#include <vector> + +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_gatt_descriptor_service_provider.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// FakeBluetoothGattDescriptorServiceProvider simulates behavior of a local +// GATT descriptor object and is used both in test cases in place of a mock +// and on the Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothGattDescriptorServiceProvider + : public BluetoothGattDescriptorServiceProvider { + public: + FakeBluetoothGattDescriptorServiceProvider( + const dbus::ObjectPath& object_path, + Delegate* delegate, + const std::string& uuid, + const std::vector<std::string>& permissions, + const dbus::ObjectPath& characteristic_path); + ~FakeBluetoothGattDescriptorServiceProvider() override; + + // BluetoothGattDescriptorServiceProvider override. + void SendValueChanged(const std::vector<uint8>& value) override; + + // Methods to simulate value get/set requests issued from a remote device. The + // methods do nothing, if the associated service was not registered with the + // GATT manager. + void GetValue(const Delegate::ValueCallback& callback, + const Delegate::ErrorCallback& error_callback); + void SetValue(const std::vector<uint8>& value, + const base::Closure& callback, + const Delegate::ErrorCallback& error_callback); + + const dbus::ObjectPath& object_path() const { return object_path_; } + const std::string& uuid() const { return uuid_; } + const dbus::ObjectPath& characteristic_path() const { + return characteristic_path_; + } + + private: + // D-Bus object path of the fake GATT descriptor. + dbus::ObjectPath object_path_; + + // 128-bit GATT descriptor UUID. + std::string uuid_; + + // Object path of the characteristic that this descriptor belongs to. + dbus::ObjectPath characteristic_path_; + + // The delegate that method calls are passed on to. + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothGattDescriptorServiceProvider); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_DESCRIPTOR_SERVICE_PROVIDER_H_ diff --git a/chromeos/dbus/fake_bluetooth_gatt_manager_client.cc b/chromeos/dbus/fake_bluetooth_gatt_manager_client.cc new file mode 100644 index 0000000..1127eae --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_gatt_manager_client.cc @@ -0,0 +1,169 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_gatt_manager_client.h" + +#include "base/logging.h" +#include "chromeos/dbus/fake_bluetooth_gatt_characteristic_service_provider.h" +#include "chromeos/dbus/fake_bluetooth_gatt_descriptor_service_provider.h" +#include "chromeos/dbus/fake_bluetooth_gatt_service_service_provider.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +FakeBluetoothGattManagerClient::FakeBluetoothGattManagerClient() {} + +FakeBluetoothGattManagerClient::~FakeBluetoothGattManagerClient() {} + +// DBusClient override. +void FakeBluetoothGattManagerClient::Init(dbus::Bus* bus) {} + +// BluetoothGattManagerClient overrides. +void FakeBluetoothGattManagerClient::RegisterService( + const dbus::ObjectPath& service_path, + const Options& options, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "Register GATT service: " << service_path.value(); + + // If a service provider wasn't created before, return error. + ServiceMap::iterator iter = service_map_.find(service_path); + if (iter == service_map_.end()) { + error_callback.Run(bluetooth_gatt_manager::kErrorInvalidArguments, + "GATT service doesn't exist: " + service_path.value()); + return; + } + + // Check to see if this GATT service was already registered. + ServiceProvider* provider = &iter->second; + if (provider->first) { + error_callback.Run( + bluetooth_gatt_manager::kErrorAlreadyExists, + "GATT service already registered: " + service_path.value()); + return; + } + + // Success! + provider->first = true; + callback.Run(); +} + +void FakeBluetoothGattManagerClient::UnregisterService( + const dbus::ObjectPath& service_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "Unregister GATT service: " << service_path.value(); + + // If a service provider wasn't created before, return error. + ServiceMap::iterator iter = service_map_.find(service_path); + if (iter == service_map_.end()) { + error_callback.Run(bluetooth_gatt_manager::kErrorInvalidArguments, + "GATT service doesn't exist: " + service_path.value()); + return; + } + + // Return error if the GATT service wasn't registered before. + ServiceProvider* provider = &iter->second; + if (!provider->first) { + error_callback.Run(bluetooth_gatt_manager::kErrorDoesNotExist, + "GATT service not registered: " + service_path.value()); + return; + } + + // Success! + provider->first = false; + callback.Run(); +} + +void FakeBluetoothGattManagerClient::RegisterServiceServiceProvider( + FakeBluetoothGattServiceServiceProvider* provider) { + // Ignore, if a service provider is already registered for the object path. + ServiceMap::iterator iter = service_map_.find(provider->object_path()); + if (iter != service_map_.end()) { + VLOG(1) << "GATT service service provider already registered for " + << "object path: " << provider->object_path().value(); + return; + } + service_map_[provider->object_path()] = std::make_pair(false, provider); +} + +void FakeBluetoothGattManagerClient::RegisterCharacteristicServiceProvider( + FakeBluetoothGattCharacteristicServiceProvider* provider) { + // Ignore, if a service provider is already registered for the object path. + CharacteristicMap::iterator iter = + characteristic_map_.find(provider->object_path()); + if (iter != characteristic_map_.end()) { + VLOG(1) << "GATT characteristic service provider already registered for " + << "object path: " << provider->object_path().value(); + return; + } + characteristic_map_[provider->object_path()] = provider; +} + +void FakeBluetoothGattManagerClient::RegisterDescriptorServiceProvider( + FakeBluetoothGattDescriptorServiceProvider* provider) { + // Ignore, if a service provider is already registered for the object path. + DescriptorMap::iterator iter = descriptor_map_.find(provider->object_path()); + if (iter != descriptor_map_.end()) { + VLOG(1) << "GATT descriptor service provider already registered for " + << "object path: " << provider->object_path().value(); + return; + } + descriptor_map_[provider->object_path()] = provider; +} + +void FakeBluetoothGattManagerClient::UnregisterServiceServiceProvider( + FakeBluetoothGattServiceServiceProvider* provider) { + ServiceMap::iterator iter = service_map_.find(provider->object_path()); + if (iter != service_map_.end() && iter->second.second == provider) + service_map_.erase(iter); +} + +void FakeBluetoothGattManagerClient::UnregisterCharacteristicServiceProvider( + FakeBluetoothGattCharacteristicServiceProvider* provider) { + characteristic_map_.erase(provider->object_path()); +} + +void FakeBluetoothGattManagerClient::UnregisterDescriptorServiceProvider( + FakeBluetoothGattDescriptorServiceProvider* provider) { + descriptor_map_.erase(provider->object_path()); +} + +FakeBluetoothGattServiceServiceProvider* +FakeBluetoothGattManagerClient::GetServiceServiceProvider( + const dbus::ObjectPath& object_path) const { + ServiceMap::const_iterator iter = service_map_.find(object_path); + if (iter == service_map_.end()) + return NULL; + return iter->second.second; +} + +FakeBluetoothGattCharacteristicServiceProvider* +FakeBluetoothGattManagerClient::GetCharacteristicServiceProvider( + const dbus::ObjectPath& object_path) const { + CharacteristicMap::const_iterator iter = + characteristic_map_.find(object_path); + if (iter == characteristic_map_.end()) + return NULL; + return iter->second; +} + +FakeBluetoothGattDescriptorServiceProvider* +FakeBluetoothGattManagerClient::GetDescriptorServiceProvider( + const dbus::ObjectPath& object_path) const { + DescriptorMap::const_iterator iter = descriptor_map_.find(object_path); + if (iter == descriptor_map_.end()) + return NULL; + return iter->second; +} + +bool FakeBluetoothGattManagerClient::IsServiceRegistered( + const dbus::ObjectPath& object_path) const { + ServiceMap::const_iterator iter = service_map_.find(object_path); + if (iter == service_map_.end()) + return false; + return iter->second.first; +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_gatt_manager_client.h b/chromeos/dbus/fake_bluetooth_gatt_manager_client.h new file mode 100644 index 0000000..cd5d3b0 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_gatt_manager_client.h @@ -0,0 +1,100 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_MANAGER_CLIENT_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_MANAGER_CLIENT_H_ + +#include <map> +#include <string> +#include <utility> + +#include "base/callback.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_gatt_manager_client.h" +#include "dbus/object_path.h" + +namespace chromeos { + +class FakeBluetoothGattCharacteristicServiceProvider; +class FakeBluetoothGattDescriptorServiceProvider; +class FakeBluetoothGattServiceServiceProvider; + +// FakeBluetoothGattManagerClient simulates the behavior of the Bluetooth +// daemon's GATT manager object and is used both in test cases in place of a +// mock and on the Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothGattManagerClient + : public BluetoothGattManagerClient { + public: + FakeBluetoothGattManagerClient(); + ~FakeBluetoothGattManagerClient() override; + + // DBusClient override. + void Init(dbus::Bus* bus) override; + + // BluetoothGattManagerClient overrides. + void RegisterService(const dbus::ObjectPath& service_path, + const Options& options, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void UnregisterService(const dbus::ObjectPath& service_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + + // Register, unregister, and retrieve pointers to service, characteristic, and + // descriptor service providers. Automatically called from the service + // provider constructor and destructors. + void RegisterServiceServiceProvider( + FakeBluetoothGattServiceServiceProvider* provider); + void RegisterCharacteristicServiceProvider( + FakeBluetoothGattCharacteristicServiceProvider* provider); + void RegisterDescriptorServiceProvider( + FakeBluetoothGattDescriptorServiceProvider* provider); + + void UnregisterServiceServiceProvider( + FakeBluetoothGattServiceServiceProvider* provider); + void UnregisterCharacteristicServiceProvider( + FakeBluetoothGattCharacteristicServiceProvider* provider); + void UnregisterDescriptorServiceProvider( + FakeBluetoothGattDescriptorServiceProvider* provider); + + // Return a pointer to the service provider that corresponds to the object + // path |object_path| if it exists. + FakeBluetoothGattServiceServiceProvider* GetServiceServiceProvider( + const dbus::ObjectPath& object_path) const; + FakeBluetoothGattCharacteristicServiceProvider* + GetCharacteristicServiceProvider(const dbus::ObjectPath& object_path) const; + FakeBluetoothGattDescriptorServiceProvider* GetDescriptorServiceProvider( + const dbus::ObjectPath& object_path) const; + + // Returns true, if a GATT service with object path |object_path| was + // registered with the GATT manager using RegisterService. + bool IsServiceRegistered(const dbus::ObjectPath& object_path) const; + + private: + // Mappings for GATT service, characteristic, and descriptor service + // providers. The fake GATT manager stores references to all instances + // created so that they can be obtained by tests. + typedef std::map<dbus::ObjectPath, + FakeBluetoothGattCharacteristicServiceProvider*> + CharacteristicMap; + typedef std::map<dbus::ObjectPath, + FakeBluetoothGattDescriptorServiceProvider*> DescriptorMap; + + // The mapping for services is from object paths to pairs of boolean and + // service provider pointer, where the boolean denotes whether or not the + // service is already registered. + typedef std::pair<bool, FakeBluetoothGattServiceServiceProvider*> + ServiceProvider; + typedef std::map<dbus::ObjectPath, ServiceProvider> ServiceMap; + + ServiceMap service_map_; + CharacteristicMap characteristic_map_; + DescriptorMap descriptor_map_; + + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothGattManagerClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_MANAGER_CLIENT_H_ diff --git a/chromeos/dbus/fake_bluetooth_gatt_service_client.cc b/chromeos/dbus/fake_bluetooth_gatt_service_client.cc new file mode 100644 index 0000000..47b39b6 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_gatt_service_client.cc @@ -0,0 +1,187 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_gatt_service_client.h" + +#include "base/bind.h" +#include "base/location.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "base/time/time.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +namespace { + +const int kExposeCharacteristicsDelayIntervalMs = 100; + +} // namespace + +// static +const char FakeBluetoothGattServiceClient::kHeartRateServicePathComponent[] = + "service0000"; +const char FakeBluetoothGattServiceClient::kHeartRateServiceUUID[] = + "0000180d-0000-1000-8000-00805f9b34fb"; + +FakeBluetoothGattServiceClient::Properties::Properties( + const PropertyChangedCallback& callback) + : BluetoothGattServiceClient::Properties( + NULL, + bluetooth_gatt_service::kBluetoothGattServiceInterface, + callback) {} + +FakeBluetoothGattServiceClient::Properties::~Properties() {} + +void FakeBluetoothGattServiceClient::Properties::Get( + dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) { + VLOG(1) << "Get " << property->name(); + callback.Run(false); +} + +void FakeBluetoothGattServiceClient::Properties::GetAll() { + VLOG(1) << "GetAll"; +} + +void FakeBluetoothGattServiceClient::Properties::Set( + dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) { + VLOG(1) << "Set " << property->name(); + callback.Run(false); +} + +FakeBluetoothGattServiceClient::FakeBluetoothGattServiceClient() + : weak_ptr_factory_(this) {} + +FakeBluetoothGattServiceClient::~FakeBluetoothGattServiceClient() {} + +void FakeBluetoothGattServiceClient::Init(dbus::Bus* bus) {} + +void FakeBluetoothGattServiceClient::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void FakeBluetoothGattServiceClient::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +std::vector<dbus::ObjectPath> FakeBluetoothGattServiceClient::GetServices() { + std::vector<dbus::ObjectPath> paths; + if (heart_rate_service_properties_.get()) { + DCHECK(!heart_rate_service_path_.empty()); + paths.push_back(dbus::ObjectPath(heart_rate_service_path_)); + } + return paths; +} + +FakeBluetoothGattServiceClient::Properties* +FakeBluetoothGattServiceClient::GetProperties( + const dbus::ObjectPath& object_path) { + if (object_path.value() == heart_rate_service_path_) + return heart_rate_service_properties_.get(); + return NULL; +} + +void FakeBluetoothGattServiceClient::ExposeHeartRateService( + const dbus::ObjectPath& device_path) { + if (IsHeartRateVisible()) { + DCHECK(!heart_rate_service_path_.empty()); + VLOG(1) << "Fake Heart Rate Service already exposed."; + return; + } + VLOG(2) << "Exposing fake Heart Rate Service."; + heart_rate_service_path_ = + device_path.value() + "/" + kHeartRateServicePathComponent; + heart_rate_service_properties_.reset(new Properties(base::Bind( + &FakeBluetoothGattServiceClient::OnPropertyChanged, + base::Unretained(this), dbus::ObjectPath(heart_rate_service_path_)))); + heart_rate_service_properties_->uuid.ReplaceValue(kHeartRateServiceUUID); + heart_rate_service_properties_->device.ReplaceValue(device_path); + heart_rate_service_properties_->primary.ReplaceValue(true); + + NotifyServiceAdded(dbus::ObjectPath(heart_rate_service_path_)); + + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, + base::Bind( + &FakeBluetoothGattServiceClient::ExposeHeartRateCharacteristics, + weak_ptr_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(kExposeCharacteristicsDelayIntervalMs)); +} + +void FakeBluetoothGattServiceClient::HideHeartRateService() { + if (!IsHeartRateVisible()) { + DCHECK(heart_rate_service_path_.empty()); + VLOG(1) << "Fake Heart Rate Service already hidden."; + return; + } + VLOG(2) << "Hiding fake Heart Rate Service."; + FakeBluetoothGattCharacteristicClient* char_client = + static_cast<FakeBluetoothGattCharacteristicClient*>( + DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()); + char_client->HideHeartRateCharacteristics(); + + // Notify observers before deleting the properties structure so that it + // can be accessed from the observer method. + NotifyServiceRemoved(dbus::ObjectPath(heart_rate_service_path_)); + + heart_rate_service_properties_.reset(); + heart_rate_service_path_.clear(); +} + +bool FakeBluetoothGattServiceClient::IsHeartRateVisible() const { + return !!heart_rate_service_properties_.get(); +} + +dbus::ObjectPath FakeBluetoothGattServiceClient::GetHeartRateServicePath() + const { + return dbus::ObjectPath(heart_rate_service_path_); +} + +void FakeBluetoothGattServiceClient::OnPropertyChanged( + const dbus::ObjectPath& object_path, + const std::string& property_name) { + VLOG(2) << "Fake GATT Service property changed: " << object_path.value() + << ": " << property_name; + FOR_EACH_OBSERVER(BluetoothGattServiceClient::Observer, observers_, + GattServicePropertyChanged(object_path, property_name)); +} + +void FakeBluetoothGattServiceClient::NotifyServiceAdded( + const dbus::ObjectPath& object_path) { + VLOG(2) << "GATT service added: " << object_path.value(); + FOR_EACH_OBSERVER(BluetoothGattServiceClient::Observer, observers_, + GattServiceAdded(object_path)); +} + +void FakeBluetoothGattServiceClient::NotifyServiceRemoved( + const dbus::ObjectPath& object_path) { + VLOG(2) << "GATT service removed: " << object_path.value(); + FOR_EACH_OBSERVER(BluetoothGattServiceClient::Observer, observers_, + GattServiceRemoved(object_path)); +} + +void FakeBluetoothGattServiceClient::ExposeHeartRateCharacteristics() { + if (!IsHeartRateVisible()) { + VLOG(2) << "Heart Rate service not visible. Not exposing characteristics."; + return; + } + FakeBluetoothGattCharacteristicClient* char_client = + static_cast<FakeBluetoothGattCharacteristicClient*>( + DBusThreadManager::Get()->GetBluetoothGattCharacteristicClient()); + char_client->ExposeHeartRateCharacteristics( + dbus::ObjectPath(heart_rate_service_path_)); + + std::vector<dbus::ObjectPath> char_paths; + char_paths.push_back(char_client->GetHeartRateMeasurementPath()); + char_paths.push_back(char_client->GetBodySensorLocationPath()); + char_paths.push_back(char_client->GetHeartRateControlPointPath()); + + heart_rate_service_properties_->characteristics.ReplaceValue(char_paths); +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_gatt_service_client.h b/chromeos/dbus/fake_bluetooth_gatt_service_client.h new file mode 100644 index 0000000..f719727 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_gatt_service_client.h @@ -0,0 +1,106 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_SERVICE_CLIENT_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_SERVICE_CLIENT_H_ + +#include <string> +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_gatt_service_client.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +// FakeBluetoothGattServiceClient simulates the behavior of the Bluetooth Daemon +// GATT service objects and is used in test cases in place of a mock and on the +// Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothGattServiceClient + : public BluetoothGattServiceClient { + public: + struct Properties : public BluetoothGattServiceClient::Properties { + explicit Properties(const PropertyChangedCallback& callback); + ~Properties() override; + + // dbus::PropertySet override + void Get(dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) override; + void GetAll() override; + void Set(dbus::PropertyBase* property, + dbus::PropertySet::SetCallback callback) override; + }; + + FakeBluetoothGattServiceClient(); + ~FakeBluetoothGattServiceClient() override; + + // DBusClient override. + void Init(dbus::Bus* bus) override; + + // BluetoothGattServiceClient overrides. + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + std::vector<dbus::ObjectPath> GetServices() override; + Properties* GetProperties(const dbus::ObjectPath& object_path) override; + + // Makes a service visible for device with object path |device_path|. Note + // that only one instance of a specific service is simulated at a time. Hence, + // this method will fail, if the service is already visible. + void ExposeHeartRateService(const dbus::ObjectPath& device_path); + void HideHeartRateService(); + + // Returns whether or not the Heart Rate Service is visible. + bool IsHeartRateVisible() const; + + // Returns the current object path of the visible Heart Rate service. If the + // service is not visible, returns an invalid empty path. + dbus::ObjectPath GetHeartRateServicePath() const; + + // Final object path components and the corresponding UUIDs of the GATT + // services that we emulate. Service paths are hierarchical to Bluetooth + // device paths, so if the path component is "service0000", and the device + // path is "/org/foo/device0", the service path is + // "/org/foo/device0/service0000". + static const char kHeartRateServicePathComponent[]; + static const char kHeartRateServiceUUID[]; + + private: + // Property callback passed when we create Properties structures. + void OnPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name); + + // Notifies observers. + void NotifyServiceAdded(const dbus::ObjectPath& object_path); + void NotifyServiceRemoved(const dbus::ObjectPath& object_path); + + // Tells FakeBluetoothGattCharacteristicClient to expose GATT characteristics. + // This is scheduled from ExposeHeartRateService to simulate asynchronous + // retrieval of characteristics. If the Heart Rate Service is hidden at the + // time this method is called, then it does nothing. + void ExposeHeartRateCharacteristics(); + + // Static properties we return. As long as a service is exposed, this will be + // non-null. Otherwise it will be null. + scoped_ptr<Properties> heart_rate_service_properties_; + std::string heart_rate_service_path_; + + // List of observers interested in event notifications from us. + base::ObserverList<Observer> observers_; + + // Weak pointer factory for generating 'this' pointers that might live longer + // than we do. + // 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<FakeBluetoothGattServiceClient> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothGattServiceClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_SERVICE_CLIENT_H_ diff --git a/chromeos/dbus/fake_bluetooth_gatt_service_service_provider.cc b/chromeos/dbus/fake_bluetooth_gatt_service_service_provider.cc new file mode 100644 index 0000000..f59cf98 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_gatt_service_service_provider.cc @@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_gatt_service_service_provider.h" + +#include "base/logging.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_gatt_manager_client.h" + +namespace chromeos { + +FakeBluetoothGattServiceServiceProvider:: + FakeBluetoothGattServiceServiceProvider( + const dbus::ObjectPath& object_path, + const std::string& uuid, + const std::vector<dbus::ObjectPath>& includes) + : object_path_(object_path), uuid_(uuid), includes_(includes) { + VLOG(1) << "Creating Bluetooth GATT service: " << object_path_.value(); + + FakeBluetoothGattManagerClient* fake_bluetooth_gatt_manager_client = + static_cast<FakeBluetoothGattManagerClient*>( + DBusThreadManager::Get()->GetBluetoothGattManagerClient()); + fake_bluetooth_gatt_manager_client->RegisterServiceServiceProvider(this); +} + +FakeBluetoothGattServiceServiceProvider:: + ~FakeBluetoothGattServiceServiceProvider() { + VLOG(1) << "Cleaning up Bluetooth GATT service: " << object_path_.value(); + + FakeBluetoothGattManagerClient* fake_bluetooth_gatt_manager_client = + static_cast<FakeBluetoothGattManagerClient*>( + DBusThreadManager::Get()->GetBluetoothGattManagerClient()); + fake_bluetooth_gatt_manager_client->UnregisterServiceServiceProvider(this); +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_gatt_service_service_provider.h b/chromeos/dbus/fake_bluetooth_gatt_service_service_provider.h new file mode 100644 index 0000000..6b49d78 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_gatt_service_service_provider.h @@ -0,0 +1,47 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_SERVICE_SERVICE_PROVIDER_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_SERVICE_SERVICE_PROVIDER_H_ + +#include <string> +#include <vector> + +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_gatt_service_service_provider.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// FakeBluetoothGattServiceServiceProvider simulates behavior of a local GATT +// service object and is used both in test cases in place of a mock and on the +// Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothGattServiceServiceProvider + : public BluetoothGattServiceServiceProvider { + public: + FakeBluetoothGattServiceServiceProvider( + const dbus::ObjectPath& object_path, + const std::string& uuid, + const std::vector<dbus::ObjectPath>& includes); + ~FakeBluetoothGattServiceServiceProvider() override; + + const dbus::ObjectPath& object_path() const { return object_path_; } + const std::string& uuid() const { return uuid_; } + + private: + // D-Bus object path of the fake GATT service. + dbus::ObjectPath object_path_; + + // 128-bit GATT service UUID. + std::string uuid_; + + // List of included GATT services. + std::vector<dbus::ObjectPath> includes_; + + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothGattServiceServiceProvider); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_GATT_SERVICE_SERVICE_PROVIDER_H_ diff --git a/chromeos/dbus/fake_bluetooth_input_client.cc b/chromeos/dbus/fake_bluetooth_input_client.cc new file mode 100644 index 0000000..ab2c66b --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_input_client.cc @@ -0,0 +1,123 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_input_client.h" + +#include <map> + +#include "base/logging.h" +#include "base/stl_util.h" +#include "chromeos/dbus/fake_bluetooth_device_client.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_manager.h" +#include "dbus/object_proxy.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +FakeBluetoothInputClient::Properties::Properties( + const PropertyChangedCallback& callback) + : BluetoothInputClient::Properties( + NULL, + bluetooth_input::kBluetoothInputInterface, + callback) {} + +FakeBluetoothInputClient::Properties::~Properties() {} + +void FakeBluetoothInputClient::Properties::Get( + dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) { + VLOG(1) << "Get " << property->name(); + callback.Run(false); +} + +void FakeBluetoothInputClient::Properties::GetAll() { + VLOG(1) << "GetAll"; +} + +void FakeBluetoothInputClient::Properties::Set( + dbus::PropertyBase* property, + dbus::PropertySet::SetCallback callback) { + VLOG(1) << "Set " << property->name(); + callback.Run(false); +} + +FakeBluetoothInputClient::FakeBluetoothInputClient() {} + +FakeBluetoothInputClient::~FakeBluetoothInputClient() { + // Clean up Properties structures + STLDeleteValues(&properties_map_); +} + +void FakeBluetoothInputClient::Init(dbus::Bus* bus) {} + +void FakeBluetoothInputClient::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void FakeBluetoothInputClient::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +FakeBluetoothInputClient::Properties* FakeBluetoothInputClient::GetProperties( + const dbus::ObjectPath& object_path) { + PropertiesMap::iterator iter = properties_map_.find(object_path); + if (iter != properties_map_.end()) + return iter->second; + return NULL; +} + +void FakeBluetoothInputClient::AddInputDevice( + const dbus::ObjectPath& object_path) { + if (properties_map_.find(object_path) != properties_map_.end()) + return; + + Properties* properties = + new Properties(base::Bind(&FakeBluetoothInputClient::OnPropertyChanged, + base::Unretained(this), object_path)); + + // The LegacyAutopair and DisplayPinCode devices represent a typical mouse + // and keyboard respectively, so mark them as ReconnectMode "any". The + // DisplayPasskey device represents a Bluetooth 2.1+ keyboard and the + // ConnectUnpairable device represents a pre-standardization mouse, so mark + // them as ReconnectMode "device". + if (object_path.value() == FakeBluetoothDeviceClient::kDisplayPasskeyPath || + object_path.value() == + FakeBluetoothDeviceClient::kConnectUnpairablePath) { + properties->reconnect_mode.ReplaceValue( + bluetooth_input::kDeviceReconnectModeProperty); + } else { + properties->reconnect_mode.ReplaceValue( + bluetooth_input::kAnyReconnectModeProperty); + } + + properties_map_[object_path] = properties; + + FOR_EACH_OBSERVER(BluetoothInputClient::Observer, observers_, + InputAdded(object_path)); +} + +void FakeBluetoothInputClient::RemoveInputDevice( + const dbus::ObjectPath& object_path) { + PropertiesMap::iterator it = properties_map_.find(object_path); + + if (it == properties_map_.end()) + return; + + FOR_EACH_OBSERVER(BluetoothInputClient::Observer, observers_, + InputRemoved(object_path)); + + delete it->second; + properties_map_.erase(it); +} + +void FakeBluetoothInputClient::OnPropertyChanged( + const dbus::ObjectPath& object_path, + const std::string& property_name) { + FOR_EACH_OBSERVER(BluetoothInputClient::Observer, observers_, + InputPropertyChanged(object_path, property_name)); +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_input_client.h b/chromeos/dbus/fake_bluetooth_input_client.h new file mode 100644 index 0000000..22369eb --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_input_client.h @@ -0,0 +1,64 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_INPUT_CLIENT_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_INPUT_CLIENT_H_ + +#include "base/callback.h" +#include "base/observer_list.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_input_client.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +// FakeBluetoothInputClient simulates the behavior of the Bluetooth Daemon +// input device objects and is used both in test cases in place of a mock and on +// the Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothInputClient : public BluetoothInputClient { + public: + struct Properties : public BluetoothInputClient::Properties { + explicit Properties(const PropertyChangedCallback& callback); + ~Properties() override; + + // dbus::PropertySet override + void Get(dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) override; + void GetAll() override; + void Set(dbus::PropertyBase* property, + dbus::PropertySet::SetCallback callback) override; + }; + + FakeBluetoothInputClient(); + ~FakeBluetoothInputClient() override; + + // BluetoothInputClient overrides + void Init(dbus::Bus* bus) override; + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + Properties* GetProperties(const dbus::ObjectPath& object_path) override; + + // Simulate device addition/removal + void AddInputDevice(const dbus::ObjectPath& object_path); + void RemoveInputDevice(const dbus::ObjectPath& object_path); + + private: + // Property callback passed when we create Properties* structures. + void OnPropertyChanged(const dbus::ObjectPath& object_path, + const std::string& property_name); + + // Static properties we return. + typedef std::map<const dbus::ObjectPath, Properties*> PropertiesMap; + PropertiesMap properties_map_; + + // List of observers interested in event notifications from us. + base::ObserverList<Observer> observers_; + + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothInputClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_INPUT_CLIENT_H_ diff --git a/chromeos/dbus/fake_bluetooth_le_advertisement_service_provider.cc b/chromeos/dbus/fake_bluetooth_le_advertisement_service_provider.cc new file mode 100644 index 0000000..ce46e3d --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_le_advertisement_service_provider.cc @@ -0,0 +1,46 @@ +// Copyright 2015 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/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_le_advertisement_service_provider.h" +#include "chromeos/dbus/fake_bluetooth_le_advertising_manager_client.h" + +namespace chromeos { + +FakeBluetoothLEAdvertisementServiceProvider:: + FakeBluetoothLEAdvertisementServiceProvider( + const dbus::ObjectPath& object_path, + Delegate* delegate) + : delegate_(delegate) { + object_path_ = object_path; + VLOG(1) << "Creating Bluetooth Advertisement: " << object_path_.value(); + + FakeBluetoothLEAdvertisingManagerClient* + fake_bluetooth_profile_manager_client = + static_cast<FakeBluetoothLEAdvertisingManagerClient*>( + DBusThreadManager::Get() + ->GetBluetoothLEAdvertisingManagerClient()); + fake_bluetooth_profile_manager_client->RegisterAdvertisementServiceProvider( + this); +} + +FakeBluetoothLEAdvertisementServiceProvider:: + ~FakeBluetoothLEAdvertisementServiceProvider() { + VLOG(1) << "Cleaning up Bluetooth Advertisement: " << object_path_.value(); + + FakeBluetoothLEAdvertisingManagerClient* + fake_bluetooth_profile_manager_client = + static_cast<FakeBluetoothLEAdvertisingManagerClient*>( + DBusThreadManager::Get() + ->GetBluetoothLEAdvertisingManagerClient()); + fake_bluetooth_profile_manager_client->UnregisterAdvertisementServiceProvider( + this); +} + +void FakeBluetoothLEAdvertisementServiceProvider::Release() { + VLOG(1) << object_path_.value() << ": Release"; + delegate_->Released(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_le_advertisement_service_provider.h b/chromeos/dbus/fake_bluetooth_le_advertisement_service_provider.h new file mode 100644 index 0000000..5a19aee --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_le_advertisement_service_provider.h @@ -0,0 +1,49 @@ +// Copyright 2015 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 CHROMEOS_DBUS_FAKE_BLUETOOTH_LE_ADVERTISEMENT_SERVICE_PROVIDER_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_LE_ADVERTISEMENT_SERVICE_PROVIDER_H_ + +#include "base/bind.h" +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_le_advertisement_service_provider.h" +#include "dbus/file_descriptor.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// FakeBluetoothAdvertisementServiceProvider simulates the behavior of a local +// Bluetooth agent object and is used both in test cases in place of a +// mock and on the Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothLEAdvertisementServiceProvider + : public BluetoothLEAdvertisementServiceProvider { + public: + FakeBluetoothLEAdvertisementServiceProvider( + const dbus::ObjectPath& object_path, + Delegate* delegate); + ~FakeBluetoothLEAdvertisementServiceProvider() override; + + // Each of these calls the equivalent + // BluetoothAdvertisementServiceProvider::Delegate method on the object passed + // on construction. + void Release(); + + const dbus::ObjectPath& object_path() { return object_path_; } + + private: + friend class FakeBluetoothLEAdvertisingManagerClient; + + // All incoming method calls are passed on to the Delegate and a callback + // passed to generate the reply. |delegate_| is generally the object that + // owns this one, and must outlive it. + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothLEAdvertisementServiceProvider); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_LE_ADVERTISEMENT_SERVICE_PROVIDER_H_ diff --git a/chromeos/dbus/fake_bluetooth_le_advertising_manager_client.cc b/chromeos/dbus/fake_bluetooth_le_advertising_manager_client.cc new file mode 100644 index 0000000..201fc42 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_le_advertising_manager_client.cc @@ -0,0 +1,101 @@ +// Copyright 2015 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/location.h" +#include "base/logging.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_proxy.h" +#include "fake_bluetooth_le_advertisement_service_provider.h" +#include "fake_bluetooth_le_advertising_manager_client.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +const char FakeBluetoothLEAdvertisingManagerClient::kAdvertisingManagerPath[] = + "/fake/hci0"; + +FakeBluetoothLEAdvertisingManagerClient:: + FakeBluetoothLEAdvertisingManagerClient() {} + +FakeBluetoothLEAdvertisingManagerClient:: + ~FakeBluetoothLEAdvertisingManagerClient() {} + +void FakeBluetoothLEAdvertisingManagerClient::Init(dbus::Bus* bus) {} + +void FakeBluetoothLEAdvertisingManagerClient::AddObserver(Observer* observer) {} + +void FakeBluetoothLEAdvertisingManagerClient::RemoveObserver( + Observer* observer) {} + +void FakeBluetoothLEAdvertisingManagerClient::RegisterAdvertisement( + const dbus::ObjectPath& manager_object_path, + const dbus::ObjectPath& advertisement_object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "RegisterAdvertisment: " << advertisement_object_path.value(); + + if (manager_object_path != dbus::ObjectPath(kAdvertisingManagerPath)) { + error_callback.Run(kNoResponseError, "Invalid Advertising Manager path."); + return; + } + + ServiceProviderMap::iterator iter = + service_provider_map_.find(advertisement_object_path); + if (iter == service_provider_map_.end()) { + error_callback.Run(bluetooth_advertising_manager::kErrorInvalidArguments, + "Advertisement object not registered"); + } else if (!currently_registered_.value().empty()) { + error_callback.Run(bluetooth_advertising_manager::kErrorFailed, + "Maximum advertisements reached"); + } else { + currently_registered_ = advertisement_object_path; + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); + } +} + +void FakeBluetoothLEAdvertisingManagerClient::UnregisterAdvertisement( + const dbus::ObjectPath& manager_object_path, + const dbus::ObjectPath& advertisement_object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "UnregisterAdvertisment: " << advertisement_object_path.value(); + + if (manager_object_path != dbus::ObjectPath(kAdvertisingManagerPath)) { + error_callback.Run(kNoResponseError, "Invalid Advertising Manager path."); + return; + } + + ServiceProviderMap::iterator iter = + service_provider_map_.find(advertisement_object_path); + if (iter == service_provider_map_.end()) { + error_callback.Run(bluetooth_advertising_manager::kErrorDoesNotExist, + "Advertisement not registered"); + } else if (advertisement_object_path != currently_registered_) { + error_callback.Run(bluetooth_advertising_manager::kErrorDoesNotExist, + "Does not exist"); + } else { + currently_registered_ = dbus::ObjectPath(""); + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); + } +} + +void FakeBluetoothLEAdvertisingManagerClient:: + RegisterAdvertisementServiceProvider( + FakeBluetoothLEAdvertisementServiceProvider* service_provider) { + service_provider_map_[service_provider->object_path_] = service_provider; +} + +void FakeBluetoothLEAdvertisingManagerClient:: + UnregisterAdvertisementServiceProvider( + FakeBluetoothLEAdvertisementServiceProvider* service_provider) { + ServiceProviderMap::iterator iter = + service_provider_map_.find(service_provider->object_path_); + if (iter != service_provider_map_.end() && iter->second == service_provider) + service_provider_map_.erase(iter); +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_le_advertising_manager_client.h b/chromeos/dbus/fake_bluetooth_le_advertising_manager_client.h new file mode 100644 index 0000000..5df129c --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_le_advertising_manager_client.h @@ -0,0 +1,79 @@ +// Copyright 2015 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 CHROMEOS_DBUS_FAKE_BLUETOOTH_LE_ADVERTISING_MANAGER_CLIENT_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_LE_ADVERTISING_MANAGER_CLIENT_H_ + +#include <map> +#include <string> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/observer_list.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_le_advertising_manager_client.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +class FakeBluetoothLEAdvertisementServiceProvider; + +// FakeBluetoothAdvertisementManagerClient simulates the behavior of the +// Bluetooth +// Daemon's profile manager object and is used both in test cases in place of a +// mock and on the Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothLEAdvertisingManagerClient + : public BluetoothLEAdvertisingManagerClient { + public: + FakeBluetoothLEAdvertisingManagerClient(); + ~FakeBluetoothLEAdvertisingManagerClient() override; + + // DBusClient overrides: + void Init(dbus::Bus* bus) override; + + // BluetoothAdvertisingManagerClient overrides: + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + void RegisterAdvertisement(const dbus::ObjectPath& manager_object_path, + const dbus::ObjectPath& advertisement_object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void UnregisterAdvertisement( + const dbus::ObjectPath& manager_object_path, + const dbus::ObjectPath& advertisement_object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + + // Register, unregister and retrieve pointers to profile server providers. + void RegisterAdvertisementServiceProvider( + FakeBluetoothLEAdvertisementServiceProvider* service_provider); + void UnregisterAdvertisementServiceProvider( + FakeBluetoothLEAdvertisementServiceProvider* service_provider); + FakeBluetoothLEAdvertisementServiceProvider* GetAdvertisementServiceProvider( + const std::string& uuid); + + // Advertising manager path that we simulate. + static const char kAdvertisingManagerPath[]; + + private: + // Map of a D-Bus object path to the FakeBluetoothAdvertisementServiceProvider + // registered for it; maintained by RegisterAdvertisementServiceProvider() and + // UnregisterProfileServiceProvicer() called by the constructor and + // destructor of FakeBluetoothAdvertisementServiceProvider. + typedef std::map<dbus::ObjectPath, + FakeBluetoothLEAdvertisementServiceProvider*> + ServiceProviderMap; + ServiceProviderMap service_provider_map_; + + // Holds the currently registered advertisement. If there is no advertisement + // registered, this path is empty. + dbus::ObjectPath currently_registered_; + + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothLEAdvertisingManagerClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_LE_ADVERTISING_MANAGER_CLIENT_H_ diff --git a/chromeos/dbus/fake_bluetooth_media_client.cc b/chromeos/dbus/fake_bluetooth_media_client.cc new file mode 100644 index 0000000..f9cfb5a --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_media_client.cc @@ -0,0 +1,134 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_media_client.h" + +#include <string> + +#include "base/stl_util.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_adapter_client.h" +#include "chromeos/dbus/fake_bluetooth_media_endpoint_service_provider.h" +#include "chromeos/dbus/fake_bluetooth_media_transport_client.h" + +using dbus::ObjectPath; + +namespace { + +// Except for |kFailedError|, the other error is defined in BlueZ D-Bus Media +// API. +const char kFailedError[] = "org.chromium.Error.Failed"; +const char kInvalidArgumentsError[] = "org.chromium.Error.InvalidArguments"; + +} // namespace + +namespace chromeos { + +// static +const uint8_t FakeBluetoothMediaClient::kDefaultCodec = 0x00; + +FakeBluetoothMediaClient::FakeBluetoothMediaClient() + : visible_(true), + object_path_(ObjectPath(FakeBluetoothAdapterClient::kAdapterPath)) {} + +FakeBluetoothMediaClient::~FakeBluetoothMediaClient() {} + +void FakeBluetoothMediaClient::Init(dbus::Bus* bus) {} + +void FakeBluetoothMediaClient::AddObserver( + BluetoothMediaClient::Observer* observer) { + DCHECK(observer); + observers_.AddObserver(observer); +} + +void FakeBluetoothMediaClient::RemoveObserver( + BluetoothMediaClient::Observer* observer) { + DCHECK(observer); + observers_.RemoveObserver(observer); +} + +void FakeBluetoothMediaClient::RegisterEndpoint( + const ObjectPath& object_path, + const ObjectPath& endpoint_path, + const EndpointProperties& properties, + const base::Closure& callback, + const ErrorCallback& error_callback) { + if (!visible_) + return; + + VLOG(1) << "RegisterEndpoint: " << endpoint_path.value(); + + // The media client and adapter client should have the same object path. + if (object_path != object_path_ || + properties.uuid != BluetoothMediaClient::kBluetoothAudioSinkUUID || + properties.codec != kDefaultCodec || properties.capabilities.empty()) { + error_callback.Run(kInvalidArgumentsError, ""); + return; + } + + callback.Run(); +} + +void FakeBluetoothMediaClient::UnregisterEndpoint( + const ObjectPath& object_path, + const ObjectPath& endpoint_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + // TODO(mcchou): Come up with some corresponding actions. + VLOG(1) << "UnregisterEndpoint: " << endpoint_path.value(); + + if (!ContainsKey(endpoints_, endpoint_path)) { + error_callback.Run(kFailedError, "Unknown media endpoint"); + return; + } + + SetEndpointRegistered(endpoints_[endpoint_path], false); + callback.Run(); +} + +void FakeBluetoothMediaClient::SetVisible(bool visible) { + visible_ = visible; + + if (visible_) + return; + + // If the media object becomes invisible, an update chain will unregister all + // endpoints and set the associated transport objects to be invalid. + // SetEndpointRegistered will remove the endpoint entry from |endpoints_|. + while (endpoints_.begin() != endpoints_.end()) + SetEndpointRegistered(endpoints_.begin()->second, false); + + // Notifies observers about the change on |visible_|. + FOR_EACH_OBSERVER(BluetoothMediaClient::Observer, observers_, + MediaRemoved(object_path_)); +} + +void FakeBluetoothMediaClient::SetEndpointRegistered( + FakeBluetoothMediaEndpointServiceProvider* endpoint, + bool registered) { + if (registered) { + endpoints_[endpoint->object_path()] = endpoint; + return; + } + + if (!IsRegistered(endpoint->object_path())) + return; + + // Once a media endpoint object becomes invalid, invalidate the associated + // transport. + FakeBluetoothMediaTransportClient* transport = + static_cast<FakeBluetoothMediaTransportClient*>( + DBusThreadManager::Get()->GetBluetoothMediaTransportClient()); + transport->SetValid(endpoint, false); + + endpoints_.erase(endpoint->object_path()); + endpoint->Released(); +} + +bool FakeBluetoothMediaClient::IsRegistered( + const dbus::ObjectPath& endpoint_path) { + return ContainsKey(endpoints_, endpoint_path); +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_media_client.h b/chromeos/dbus/fake_bluetooth_media_client.h new file mode 100644 index 0000000..3b4f4a7 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_media_client.h @@ -0,0 +1,76 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_MEDIA_CLIENT_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_MEDIA_CLIENT_H_ + +#include <map> + +#include "base/callback.h" +#include "base/observer_list.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_media_client.h" +#include "dbus/object_path.h" + +namespace chromeos { + +class FakeBluetoothMediaEndpointServiceProvider; + +class CHROMEOS_EXPORT FakeBluetoothMediaClient : public BluetoothMediaClient { + public: + // The default codec is SBC(0x00). + static const uint8_t kDefaultCodec; + + FakeBluetoothMediaClient(); + ~FakeBluetoothMediaClient() override; + + // DBusClient override. + void Init(dbus::Bus* bus) override; + + // BluetoothMediaClient overrides. + void AddObserver(BluetoothMediaClient::Observer* observer) override; + void RemoveObserver(BluetoothMediaClient::Observer* observer) override; + void RegisterEndpoint(const dbus::ObjectPath& object_path, + const dbus::ObjectPath& endpoint_path, + const EndpointProperties& properties, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void UnregisterEndpoint(const dbus::ObjectPath& object_path, + const dbus::ObjectPath& endpoint_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + + // Makes the media object visible/invisible to emulate the addition/removal + // events. + void SetVisible(bool visible); + + // Sets the registration state for a given media endpoint. + void SetEndpointRegistered( + FakeBluetoothMediaEndpointServiceProvider* endpoint, + bool registered); + + // Indicates whether the given endpoint path is registered or not. + bool IsRegistered(const dbus::ObjectPath& endpoint_path); + + private: + // Indicates whether the media object is visible or not. + bool visible_; + + // The path of the media object. + dbus::ObjectPath object_path_; + + // Map of registered endpoints. Each pair is composed of an endpoint path as + // key and a pointer to the endpoint as value. + std::map<dbus::ObjectPath, FakeBluetoothMediaEndpointServiceProvider*> + endpoints_; + + // List of observers interested in event notifications from us. + base::ObserverList<BluetoothMediaClient::Observer> observers_; + + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothMediaClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_MEDIA_CLIENT_H_ diff --git a/chromeos/dbus/fake_bluetooth_media_endpoint_service_provider.cc b/chromeos/dbus/fake_bluetooth_media_endpoint_service_provider.cc new file mode 100644 index 0000000..a275374 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_media_endpoint_service_provider.cc @@ -0,0 +1,65 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_media_endpoint_service_provider.h" + +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_media_client.h" +#include "chromeos/dbus/fake_bluetooth_media_transport_client.h" + +using dbus::ObjectPath; + +namespace chromeos { + +FakeBluetoothMediaEndpointServiceProvider:: + FakeBluetoothMediaEndpointServiceProvider(const ObjectPath& object_path, + Delegate* delegate) + : visible_(false), object_path_(object_path), delegate_(delegate) { + VLOG(1) << "Create Bluetooth Media Endpoint: " << object_path_.value(); +} + +FakeBluetoothMediaEndpointServiceProvider:: + ~FakeBluetoothMediaEndpointServiceProvider() { + VLOG(1) << "Cleaning up Bluetooth Media Endpoint: " << object_path_.value(); +} + +void FakeBluetoothMediaEndpointServiceProvider::SetConfiguration( + const ObjectPath& transport_path, + const Delegate::TransportProperties& properties) { + VLOG(1) << object_path_.value() << ": SetConfiguration for " + << transport_path.value(); + + delegate_->SetConfiguration(transport_path, properties); +} + +void FakeBluetoothMediaEndpointServiceProvider::SelectConfiguration( + const std::vector<uint8_t>& capabilities, + const Delegate::SelectConfigurationCallback& callback) { + VLOG(1) << object_path_.value() << ": SelectConfiguration"; + + delegate_->SelectConfiguration(capabilities, callback); + + // Makes the transport object valid for the given endpoint path. + FakeBluetoothMediaTransportClient* transport = + static_cast<FakeBluetoothMediaTransportClient*>( + DBusThreadManager::Get()->GetBluetoothMediaTransportClient()); + DCHECK(transport); + transport->SetValid(this, true); +} + +void FakeBluetoothMediaEndpointServiceProvider::ClearConfiguration( + const ObjectPath& transport_path) { + VLOG(1) << object_path_.value() << ": ClearConfiguration on " + << transport_path.value(); + + delegate_->ClearConfiguration(transport_path); +} + +void FakeBluetoothMediaEndpointServiceProvider::Released() { + VLOG(1) << object_path_.value() << ": Released"; + + delegate_->Released(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_media_endpoint_service_provider.h b/chromeos/dbus/fake_bluetooth_media_endpoint_service_provider.h new file mode 100644 index 0000000..5efe14f --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_media_endpoint_service_provider.h @@ -0,0 +1,57 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_MEDIA_ENDPOINT_SERVICE_PROVIDER_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_MEDIA_ENDPOINT_SERVICE_PROVIDER_H_ + +#include <vector> + +#include "base/logging.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_media_endpoint_service_provider.h" +#include "dbus/object_path.h" +#include "testing/gtest/include/gtest/gtest_prod.h" + +namespace chromeos { + +// FakeBluetoothMediaEndpointServiceProvider simulates the behavior of a local +// Bluetooth Media Endpoint object. +class CHROMEOS_EXPORT FakeBluetoothMediaEndpointServiceProvider + : public BluetoothMediaEndpointServiceProvider { + public: + FakeBluetoothMediaEndpointServiceProvider(const dbus::ObjectPath& object_path, + Delegate* delegate); + ~FakeBluetoothMediaEndpointServiceProvider() override; + + // Each of these calls the equivalent BluetoothMediaEnpointServiceProvider:: + // Delegate method on the object passed on construction. + void SetConfiguration(const dbus::ObjectPath& transport_path, + const Delegate::TransportProperties& properties); + void SelectConfiguration( + const std::vector<uint8_t>& capabilities, + const Delegate::SelectConfigurationCallback& callback); + void ClearConfiguration(const dbus::ObjectPath& transport_path); + void Released(); + + // Gets the path of the media endpoint object. + const dbus::ObjectPath& object_path() const { return object_path_; } + + private: + // Indicates whether the endpoint object is visible or not. + bool visible_; + + // The path of the media endpoint object. + dbus::ObjectPath object_path_; + + // All incoming method calls are passed to |delegate_|. |callback| passed to + // |delegate_| will generate the response for those methods which have + // non-void return. + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothMediaEndpointServiceProvider); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_MEDIA_ENDPOINT_SERVICE_PROVIDER_H_ diff --git a/chromeos/dbus/fake_bluetooth_media_transport_client.cc b/chromeos/dbus/fake_bluetooth_media_transport_client.cc new file mode 100644 index 0000000..ae8f89b --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_media_transport_client.cc @@ -0,0 +1,328 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_media_transport_client.h" + +#include <unistd.h> +#include <sys/socket.h> + +#include <sstream> + +#include "base/bind.h" +#include "base/stl_util.h" +#include "chromeos/dbus/bluetooth_media_client.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_adapter_client.h" +#include "chromeos/dbus/fake_bluetooth_media_client.h" +#include "chromeos/dbus/fake_bluetooth_media_endpoint_service_provider.h" +#include "dbus/file_descriptor.h" + +using dbus::ObjectPath; + +namespace { + +// TODO(mcchou): Remove this constants once it is in cros_system_api. +const char kBluetoothMediaTransportInterface[] = "org.bluez.MediaTransport1"; +const char kNotImplemented[] = "org.bluez.NotImplemented"; +const char kNotAuthorized[] = "org.bluez.NotAuthorized"; +const char kFailed[] = "org.bluez.Failed"; +const char kNotAvailable[] = "org.bluez.NotAvailable"; + +const int kInvalidFd = -1; + +ObjectPath GenerateTransportPath() { + static unsigned int sequence_number = 0; + ++sequence_number; + std::stringstream path; + path << chromeos::FakeBluetoothAdapterClient::kAdapterPath + << chromeos::FakeBluetoothMediaTransportClient::kTransportDevicePath + << "/fd" << sequence_number; + return ObjectPath(path.str()); +} + +#define UINT8_VECTOR_FROM_ARRAY(array) \ + std::vector<uint8_t>(array, array + arraysize(array)) + +} // namespace + +namespace chromeos { + +// static +const char FakeBluetoothMediaTransportClient::kTransportDevicePath[] = + "/fake_audio_source"; +const uint8_t FakeBluetoothMediaTransportClient::kTransportCodec = 0x00; +const uint8_t FakeBluetoothMediaTransportClient::kTransportConfiguration[] = { + 0x21, 0x15, 0x33, 0x2C}; +const uint8_t FakeBluetoothMediaTransportClient::kTransportConfigurationLength = + arraysize(FakeBluetoothMediaTransportClient::kTransportConfiguration); +const uint16_t FakeBluetoothMediaTransportClient::kTransportDelay = 5; +const uint16_t FakeBluetoothMediaTransportClient::kTransportVolume = 50; +const uint16_t FakeBluetoothMediaTransportClient::kDefaultReadMtu = 20; +const uint16_t FakeBluetoothMediaTransportClient::kDefaultWriteMtu = 25; + +FakeBluetoothMediaTransportClient::Properties::Properties( + const PropertyChangedCallback& callback) + : BluetoothMediaTransportClient::Properties( + nullptr, + kBluetoothMediaTransportInterface, + callback) {} + +FakeBluetoothMediaTransportClient::Properties::~Properties() {} + +void FakeBluetoothMediaTransportClient::Properties::Get( + dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) { + VLOG(1) << "Get " << property->name(); + callback.Run(false); +} + +void FakeBluetoothMediaTransportClient::Properties::GetAll() { + VLOG(1) << "GetAll called."; +} + +void FakeBluetoothMediaTransportClient::Properties::Set( + dbus::PropertyBase* property, + dbus::PropertySet::SetCallback callback) { + VLOG(1) << "Set " << property->name(); + callback.Run(false); +} + +FakeBluetoothMediaTransportClient::Transport::Transport( + const ObjectPath& transport_path, + Properties* transport_properties) + : path(transport_path) { + properties.reset(transport_properties); +} + +FakeBluetoothMediaTransportClient::Transport::~Transport() {} + +FakeBluetoothMediaTransportClient::FakeBluetoothMediaTransportClient() {} + +FakeBluetoothMediaTransportClient::~FakeBluetoothMediaTransportClient() { + STLDeleteValues(&endpoint_to_transport_map_); +} + +// DBusClient override. +void FakeBluetoothMediaTransportClient::Init(dbus::Bus* bus) {} + +void FakeBluetoothMediaTransportClient::AddObserver( + BluetoothMediaTransportClient::Observer* observer) { + observers_.AddObserver(observer); +} + +void FakeBluetoothMediaTransportClient::RemoveObserver( + BluetoothMediaTransportClient::Observer* observer) { + observers_.RemoveObserver(observer); +} + +FakeBluetoothMediaTransportClient::Properties* +FakeBluetoothMediaTransportClient::GetProperties( + const ObjectPath& object_path) { + const ObjectPath& endpoint_path = GetEndpointPath(object_path); + Transport* transport = GetTransport(endpoint_path); + if (!transport) + return nullptr; + return transport->properties.get(); +} + +void FakeBluetoothMediaTransportClient::Acquire( + const ObjectPath& object_path, + const AcquireCallback& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "Acquire - transport path: " << object_path.value(); + AcquireInternal(false, object_path, callback, error_callback); +} + +void FakeBluetoothMediaTransportClient::TryAcquire( + const ObjectPath& object_path, + const AcquireCallback& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "TryAcquire - transport path: " << object_path.value(); + AcquireInternal(true, object_path, callback, error_callback); +} + +void FakeBluetoothMediaTransportClient::Release( + const ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + error_callback.Run(kNotImplemented, ""); +} + +void FakeBluetoothMediaTransportClient::SetValid( + FakeBluetoothMediaEndpointServiceProvider* endpoint, + bool valid) { + FakeBluetoothMediaClient* media = static_cast<FakeBluetoothMediaClient*>( + DBusThreadManager::Get()->GetBluetoothMediaClient()); + DCHECK(media); + + ObjectPath endpoint_path(endpoint->object_path()); + if (!media->IsRegistered(endpoint_path)) + return; + + if (valid) { + ObjectPath transport_path = GenerateTransportPath(); + VLOG(1) << "New transport, " << transport_path.value() + << " is created for endpoint " << endpoint_path.value(); + + // Sets the fake property set with default values. + scoped_ptr<Properties> properties(new Properties( + base::Bind(&FakeBluetoothMediaTransportClient::OnPropertyChanged, + base::Unretained(this)))); + properties->device.ReplaceValue(ObjectPath(kTransportDevicePath)); + properties->uuid.ReplaceValue( + BluetoothMediaClient::kBluetoothAudioSinkUUID); + properties->codec.ReplaceValue(kTransportCodec); + properties->configuration.ReplaceValue( + UINT8_VECTOR_FROM_ARRAY(kTransportConfiguration)); + properties->state.ReplaceValue(BluetoothMediaTransportClient::kStateIdle); + properties->delay.ReplaceValue(kTransportDelay); + properties->volume.ReplaceValue(kTransportVolume); + + endpoint_to_transport_map_[endpoint_path] = + new Transport(transport_path, properties.release()); + transport_to_endpoint_map_[transport_path] = endpoint_path; + return; + } + + Transport* transport = GetTransport(endpoint_path); + if (!transport) + return; + ObjectPath transport_path = transport->path; + + // Notifies observers about the state change of the transport. + FOR_EACH_OBSERVER(BluetoothMediaTransportClient::Observer, observers_, + MediaTransportRemoved(transport_path)); + + endpoint->ClearConfiguration(transport_path); + delete transport; + endpoint_to_transport_map_.erase(endpoint_path); + transport_to_endpoint_map_.erase(transport_path); +} + +void FakeBluetoothMediaTransportClient::SetState( + const ObjectPath& endpoint_path, + const std::string& state) { + VLOG(1) << "SetState - state: " << state; + + Transport* transport = GetTransport(endpoint_path); + if (!transport) + return; + + transport->properties->state.ReplaceValue(state); + FOR_EACH_OBSERVER( + BluetoothMediaTransportClient::Observer, observers_, + MediaTransportPropertyChanged( + transport->path, BluetoothMediaTransportClient::kStateProperty)); +} + +void FakeBluetoothMediaTransportClient::SetVolume( + const ObjectPath& endpoint_path, + const uint16_t& volume) { + Transport* transport = GetTransport(endpoint_path); + if (!transport) + return; + + transport->properties->volume.ReplaceValue(volume); + FOR_EACH_OBSERVER( + BluetoothMediaTransportClient::Observer, observers_, + MediaTransportPropertyChanged( + transport->path, BluetoothMediaTransportClient::kVolumeProperty)); +} + +void FakeBluetoothMediaTransportClient::WriteData( + const ObjectPath& endpoint_path, + const std::vector<char>& bytes) { + VLOG(1) << "WriteData - write " << bytes.size() << " bytes"; + + Transport* transport = GetTransport(endpoint_path); + + if (!transport || transport->properties->state.value() != "active") { + VLOG(1) << "WriteData - write operation rejected, since the state isn't " + "active for endpoint: " + << endpoint_path.value(); + return; + } + + if (!transport->input_fd.get()) { + VLOG(1) << "WriteData - invalid input file descriptor"; + return; + } + + ssize_t written_len = + write(transport->input_fd->GetPlatformFile(), bytes.data(), bytes.size()); + if (written_len < 0) { + VLOG(1) << "WriteData - failed to write to the socket"; + return; + } + + VLOG(1) << "WriteData - wrote " << written_len << " bytes to the socket"; +} + +ObjectPath FakeBluetoothMediaTransportClient::GetTransportPath( + const ObjectPath& endpoint_path) { + Transport* transport = GetTransport(endpoint_path); + return transport ? transport->path : ObjectPath(""); +} + +void FakeBluetoothMediaTransportClient::OnPropertyChanged( + const std::string& property_name) { + VLOG(1) << "Property " << property_name << " changed"; +} + +ObjectPath FakeBluetoothMediaTransportClient::GetEndpointPath( + const ObjectPath& transport_path) { + const auto& it = transport_to_endpoint_map_.find(transport_path); + return it != transport_to_endpoint_map_.end() ? it->second : ObjectPath(""); +} + +FakeBluetoothMediaTransportClient::Transport* +FakeBluetoothMediaTransportClient::GetTransport( + const ObjectPath& endpoint_path) { + const auto& it = endpoint_to_transport_map_.find(endpoint_path); + return (it != endpoint_to_transport_map_.end()) ? it->second : nullptr; +} + +FakeBluetoothMediaTransportClient::Transport* +FakeBluetoothMediaTransportClient::GetTransportByPath( + const dbus::ObjectPath& transport_path) { + return GetTransport(GetEndpointPath(transport_path)); +} + +void FakeBluetoothMediaTransportClient::AcquireInternal( + bool try_flag, + const ObjectPath& object_path, + const AcquireCallback& callback, + const ErrorCallback& error_callback) { + const ObjectPath& endpoint_path = GetEndpointPath(object_path); + Transport* transport = GetTransport(endpoint_path); + if (!transport) { + error_callback.Run(kFailed, ""); + return; + } + + std::string state = transport->properties->state.value(); + if (state == "active") { + error_callback.Run(kNotAuthorized, ""); + return; + } + if (state != "pending") { + error_callback.Run(try_flag ? kNotAvailable : kFailed, ""); + return; + } + + int fds[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + transport->input_fd.reset(); + error_callback.Run(kFailed, ""); + return; + } + DCHECK((fds[0] > kInvalidFd) && (fds[1] > kInvalidFd)); + transport->input_fd.reset(new base::File(fds[0])); + + dbus::FileDescriptor out_fd(fds[1]); + callback.Run(&out_fd, kDefaultReadMtu, kDefaultWriteMtu); + SetState(endpoint_path, "active"); +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_media_transport_client.h b/chromeos/dbus/fake_bluetooth_media_transport_client.h new file mode 100644 index 0000000..bbf30a5 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_media_transport_client.h @@ -0,0 +1,148 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_MEDIA_TRANSPORT_CLIENT_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_MEDIA_TRANSPORT_CLIENT_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/files/file.h" +#include "base/memory/scoped_ptr.h" +#include "base/observer_list.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_media_transport_client.h" +#include "dbus/object_path.h" + +namespace chromeos { + +class FakeBluetoothMediaEndpointServiceProvider; + +class CHROMEOS_EXPORT FakeBluetoothMediaTransportClient + : public BluetoothMediaTransportClient { + public: + struct Properties : public BluetoothMediaTransportClient::Properties { + explicit Properties(const PropertyChangedCallback& callback); + ~Properties() override; + + void Get(dbus::PropertyBase* property, + dbus::PropertySet::GetCallback callback) override; + void GetAll() override; + void Set(dbus::PropertyBase* property, + dbus::PropertySet::SetCallback callback) override; + }; + + // The default path of the transport object. + static const char kTransportPath[]; + + // The default properties including device, codec, configuration, state, delay + // and volume, owned by a fake media transport object we emulate. + static const char kTransportDevicePath[]; + static const uint8_t kTransportCodec; + static const uint8_t kTransportConfiguration[]; + static const uint8_t kTransportConfigurationLength; + static const uint16_t kTransportDelay; + static const uint16_t kTransportVolume; + + // The default MTUs for read and write. + static const uint16_t kDefaultReadMtu; + static const uint16_t kDefaultWriteMtu; + + FakeBluetoothMediaTransportClient(); + ~FakeBluetoothMediaTransportClient() override; + + // DBusClient override. + void Init(dbus::Bus* bus) override; + + // BluetoothMediaTransportClient override. + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + Properties* GetProperties(const dbus::ObjectPath& object_path) override; + void Acquire(const dbus::ObjectPath& object_path, + const AcquireCallback& callback, + const ErrorCallback& error_callback) override; + void TryAcquire(const dbus::ObjectPath& object_path, + const AcquireCallback& callback, + const ErrorCallback& error_callback) override; + void Release(const dbus::ObjectPath& object_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + + // Makes the transport valid/invalid for a given media endpoint. The transport + // object is assigned to the given endpoint if valid is true, false + // otherwise. + void SetValid(FakeBluetoothMediaEndpointServiceProvider* endpoint, + bool valid); + + // Set state/volume property to a certain value. + void SetState(const dbus::ObjectPath& endpoint_path, + const std::string& state); + void SetVolume(const dbus::ObjectPath& endpoint_path, const uint16_t& volume); + + // Writes bytes to the input file descriptor, |input_fd|, associated with a + // transport object which is bound to |endpoint_path|. + void WriteData(const dbus::ObjectPath& endpoint_path, + const std::vector<char>& bytes); + + // Retrieves the transport object path bound to |endpoint_path|. + dbus::ObjectPath GetTransportPath(const dbus::ObjectPath& endpoint_path); + + private: + // This class is used for simulating the scenario where each media endpoint + // has a corresponding transport path and properties. Once an endpoint is + // assigned with a transport path, an object of Transport is created. + struct Transport { + Transport(const dbus::ObjectPath& transport_path, + Properties* transport_properties); + ~Transport(); + + // An unique transport path. + dbus::ObjectPath path; + + // The property set bound with |path|. + scoped_ptr<Properties> properties; + + // This is the internal end of socketpair created for simulation purposes. + // |input_fd| will be initialized when Acquire/TryAcquire is called. + scoped_ptr<base::File> input_fd; + }; + + // Property callback passed while a Properties structure is created. + void OnPropertyChanged(const std::string& property_name); + + // Gets the endpoint path associated with the given transport_path. + dbus::ObjectPath GetEndpointPath(const dbus::ObjectPath& transport_path); + + // Retrieves the transport structure bound to |endpoint_path|. + Transport* GetTransport(const dbus::ObjectPath& endpoint_path); + + // Retrieves the transport structure with |transport_path|. + Transport* GetTransportByPath(const dbus::ObjectPath& transport_path); + + // Helper function used by Acquire and TryAcquire to set up the sockpair and + // invoke callback/error_callback. + void AcquireInternal(bool try_flag, + const dbus::ObjectPath& object_path, + const AcquireCallback& callback, + const ErrorCallback& error_callback); + + // Map of endpoints with valid transport. Each pair is composed of an endpoint + // path and a Transport structure containing a transport path and its + // properties. + std::map<dbus::ObjectPath, Transport*> endpoint_to_transport_map_; + + // Map of valid transports. Each pair is composed of a transport path as the + // key and an endpoint path as the value. This map is used to get the + // corresponding endpoint path when GetProperties() is called. + std::map<dbus::ObjectPath, dbus::ObjectPath> transport_to_endpoint_map_; + + base::ObserverList<BluetoothMediaTransportClient::Observer> observers_; + + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothMediaTransportClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_MEDIA_TRANSPORT_CLIENT_H_ diff --git a/chromeos/dbus/fake_bluetooth_profile_manager_client.cc b/chromeos/dbus/fake_bluetooth_profile_manager_client.cc new file mode 100644 index 0000000..1a06d41 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_profile_manager_client.cc @@ -0,0 +1,111 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_profile_manager_client.h" + +#include "base/location.h" +#include "base/logging.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "chromeos/dbus/fake_bluetooth_profile_service_provider.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_proxy.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +const char FakeBluetoothProfileManagerClient::kL2capUuid[] = + "4d995052-33cc-4fdf-b446-75f32942a076"; +const char FakeBluetoothProfileManagerClient::kRfcommUuid[] = + "3f6d6dbf-a6ad-45fc-9653-47dc912ef70e"; +const char FakeBluetoothProfileManagerClient::kUnregisterableUuid[] = + "00000000-0000-0000-0000-000000000000"; + +FakeBluetoothProfileManagerClient::FakeBluetoothProfileManagerClient() {} + +FakeBluetoothProfileManagerClient::~FakeBluetoothProfileManagerClient() {} + +void FakeBluetoothProfileManagerClient::Init(dbus::Bus* bus) {} + +void FakeBluetoothProfileManagerClient::RegisterProfile( + const dbus::ObjectPath& profile_path, + const std::string& uuid, + const Options& options, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "RegisterProfile: " << profile_path.value() << ": " << uuid; + + if (uuid == kUnregisterableUuid) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(error_callback, + bluetooth_profile_manager::kErrorInvalidArguments, + "Can't register this UUID")); + return; + } + + // TODO(jamuraa): check options for channel & psm + + ServiceProviderMap::iterator iter = service_provider_map_.find(profile_path); + if (iter == service_provider_map_.end()) { + error_callback.Run(bluetooth_profile_manager::kErrorInvalidArguments, + "No profile created"); + } else { + ProfileMap::iterator piter = profile_map_.find(uuid); + if (piter != profile_map_.end()) { + error_callback.Run(bluetooth_profile_manager::kErrorAlreadyExists, + "Profile already registered"); + } else { + profile_map_[uuid] = profile_path; + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); + } + } +} + +void FakeBluetoothProfileManagerClient::UnregisterProfile( + const dbus::ObjectPath& profile_path, + const base::Closure& callback, + const ErrorCallback& error_callback) { + VLOG(1) << "UnregisterProfile: " << profile_path.value(); + + ServiceProviderMap::iterator iter = service_provider_map_.find(profile_path); + if (iter == service_provider_map_.end()) { + error_callback.Run(bluetooth_profile_manager::kErrorInvalidArguments, + "Profile not registered"); + } else { + for (ProfileMap::iterator piter = profile_map_.begin(); + piter != profile_map_.end(); ++piter) { + if (piter->second == profile_path) { + profile_map_.erase(piter); + break; + } + } + + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); + } +} + +void FakeBluetoothProfileManagerClient::RegisterProfileServiceProvider( + FakeBluetoothProfileServiceProvider* service_provider) { + service_provider_map_[service_provider->object_path_] = service_provider; +} + +void FakeBluetoothProfileManagerClient::UnregisterProfileServiceProvider( + FakeBluetoothProfileServiceProvider* service_provider) { + ServiceProviderMap::iterator iter = + service_provider_map_.find(service_provider->object_path_); + if (iter != service_provider_map_.end() && iter->second == service_provider) + service_provider_map_.erase(iter); +} + +FakeBluetoothProfileServiceProvider* +FakeBluetoothProfileManagerClient::GetProfileServiceProvider( + const std::string& uuid) { + ProfileMap::iterator iter = profile_map_.find(uuid); + if (iter == profile_map_.end()) + return nullptr; + return service_provider_map_[iter->second]; +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_profile_manager_client.h b/chromeos/dbus/fake_bluetooth_profile_manager_client.h new file mode 100644 index 0000000..c142606 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_profile_manager_client.h @@ -0,0 +1,74 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_PROFILE_MANAGER_CLIENT_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_PROFILE_MANAGER_CLIENT_H_ + +#include <map> +#include <string> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/observer_list.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_profile_manager_client.h" +#include "dbus/object_path.h" +#include "dbus/property.h" + +namespace chromeos { + +class FakeBluetoothProfileServiceProvider; + +// FakeBluetoothProfileManagerClient simulates the behavior of the Bluetooth +// Daemon's profile manager object and is used both in test cases in place of a +// mock and on the Linux desktop. +class CHROMEOS_EXPORT FakeBluetoothProfileManagerClient + : public BluetoothProfileManagerClient { + public: + FakeBluetoothProfileManagerClient(); + ~FakeBluetoothProfileManagerClient() override; + + // BluetoothProfileManagerClient overrides + void Init(dbus::Bus* bus) override; + void RegisterProfile(const dbus::ObjectPath& profile_path, + const std::string& uuid, + const Options& options, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + void UnregisterProfile(const dbus::ObjectPath& profile_path, + const base::Closure& callback, + const ErrorCallback& error_callback) override; + + // Register, unregister and retrieve pointers to profile server providers. + void RegisterProfileServiceProvider( + FakeBluetoothProfileServiceProvider* service_provider); + void UnregisterProfileServiceProvider( + FakeBluetoothProfileServiceProvider* service_provider); + FakeBluetoothProfileServiceProvider* GetProfileServiceProvider( + const std::string& uuid); + + // UUIDs recognised for testing. + static const char kL2capUuid[]; + static const char kRfcommUuid[]; + static const char kUnregisterableUuid[]; + + private: + // Map of a D-Bus object path to the FakeBluetoothProfileServiceProvider + // registered for it; maintained by RegisterProfileServiceProvider() and + // UnregisterProfileServiceProvicer() called by the constructor and + // destructor of FakeBluetoothProfileServiceProvider. + typedef std::map<dbus::ObjectPath, FakeBluetoothProfileServiceProvider*> + ServiceProviderMap; + ServiceProviderMap service_provider_map_; + + // Map of Profile UUID to the D-Bus object path of the service provider + // in |service_provider_map_|. Maintained by RegisterProfile() and + // UnregisterProfile() in response to BluetoothProfile methods. + typedef std::map<std::string, dbus::ObjectPath> ProfileMap; + ProfileMap profile_map_; +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_PROFILE_MANAGER_CLIENT_H_ diff --git a/chromeos/dbus/fake_bluetooth_profile_service_provider.cc b/chromeos/dbus/fake_bluetooth_profile_service_provider.cc new file mode 100644 index 0000000..6f9c345 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_profile_service_provider.cc @@ -0,0 +1,61 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/dbus/fake_bluetooth_profile_service_provider.h" + +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_profile_manager_client.h" + +namespace chromeos { + +FakeBluetoothProfileServiceProvider::FakeBluetoothProfileServiceProvider( + const dbus::ObjectPath& object_path, + Delegate* delegate) + : object_path_(object_path), delegate_(delegate) { + VLOG(1) << "Creating Bluetooth Profile: " << object_path_.value(); + + FakeBluetoothProfileManagerClient* fake_bluetooth_profile_manager_client = + static_cast<FakeBluetoothProfileManagerClient*>( + DBusThreadManager::Get()->GetBluetoothProfileManagerClient()); + fake_bluetooth_profile_manager_client->RegisterProfileServiceProvider(this); +} + +FakeBluetoothProfileServiceProvider::~FakeBluetoothProfileServiceProvider() { + VLOG(1) << "Cleaning up Bluetooth Profile: " << object_path_.value(); + + FakeBluetoothProfileManagerClient* fake_bluetooth_profile_manager_client = + static_cast<FakeBluetoothProfileManagerClient*>( + DBusThreadManager::Get()->GetBluetoothProfileManagerClient()); + fake_bluetooth_profile_manager_client->UnregisterProfileServiceProvider(this); +} + +void FakeBluetoothProfileServiceProvider::Released() { + VLOG(1) << object_path_.value() << ": Released"; + delegate_->Released(); +} + +void FakeBluetoothProfileServiceProvider::NewConnection( + const dbus::ObjectPath& device_path, + scoped_ptr<dbus::FileDescriptor> fd, + const Delegate::Options& options, + const Delegate::ConfirmationCallback& callback) { + VLOG(1) << object_path_.value() << ": NewConnection for " + << device_path.value(); + delegate_->NewConnection(device_path, fd.Pass(), options, callback); +} + +void FakeBluetoothProfileServiceProvider::RequestDisconnection( + const dbus::ObjectPath& device_path, + const Delegate::ConfirmationCallback& callback) { + VLOG(1) << object_path_.value() << ": RequestDisconnection for " + << device_path.value(); + delegate_->RequestDisconnection(device_path, callback); +} + +void FakeBluetoothProfileServiceProvider::Cancel() { + VLOG(1) << object_path_.value() << ": Cancel"; + delegate_->Cancel(); +} + +} // namespace chromeos diff --git a/chromeos/dbus/fake_bluetooth_profile_service_provider.h b/chromeos/dbus/fake_bluetooth_profile_service_provider.h new file mode 100644 index 0000000..2c98048 --- /dev/null +++ b/chromeos/dbus/fake_bluetooth_profile_service_provider.h @@ -0,0 +1,60 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_DBUS_FAKE_BLUETOOTH_PROFILE_SERVICE_PROVIDER_H_ +#define CHROMEOS_DBUS_FAKE_BLUETOOTH_PROFILE_SERVICE_PROVIDER_H_ + +#include "base/bind.h" +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/dbus/bluetooth_profile_service_provider.h" +#include "dbus/file_descriptor.h" +#include "dbus/object_path.h" + +namespace chromeos { + +// FakeBluetoothProfileServiceProvider simulates the behavior of a local +// Bluetooth agent object and is used both in test cases in place of a +// mock and on the Linux desktop. +// +// This class is only called from the dbus origin thread and is not thread-safe. +class CHROMEOS_EXPORT FakeBluetoothProfileServiceProvider + : public BluetoothProfileServiceProvider { + public: + FakeBluetoothProfileServiceProvider(const dbus::ObjectPath& object_path, + Delegate* delegate); + ~FakeBluetoothProfileServiceProvider() override; + + // Each of these calls the equivalent + // BluetoothProfileServiceProvider::Delegate method on the object passed on + // construction. + void Released(); + void NewConnection(const dbus::ObjectPath& device_path, + scoped_ptr<dbus::FileDescriptor> fd, + const Delegate::Options& options, + const Delegate::ConfirmationCallback& callback); + void RequestDisconnection(const dbus::ObjectPath& device_path, + const Delegate::ConfirmationCallback& callback); + void Cancel(); + + const dbus::ObjectPath& object_path() const { return object_path_; } + + private: + friend class FakeBluetoothProfileManagerClient; + + // D-Bus object path we are faking. + dbus::ObjectPath object_path_; + + // All incoming method calls are passed on to the Delegate and a callback + // passed to generate the reply. |delegate_| is generally the object that + // owns this one, and must outlive it. + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(FakeBluetoothProfileServiceProvider); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_FAKE_BLUETOOTH_PROFILE_SERVICE_PROVIDER_H_ |