summaryrefslogtreecommitdiffstats
path: root/device/bluetooth/bluetooth_adapter_chromeos.cc
diff options
context:
space:
mode:
Diffstat (limited to 'device/bluetooth/bluetooth_adapter_chromeos.cc')
-rw-r--r--device/bluetooth/bluetooth_adapter_chromeos.cc1515
1 files changed, 1515 insertions, 0 deletions
diff --git a/device/bluetooth/bluetooth_adapter_chromeos.cc b/device/bluetooth/bluetooth_adapter_chromeos.cc
new file mode 100644
index 0000000..15f30bf
--- /dev/null
+++ b/device/bluetooth/bluetooth_adapter_chromeos.cc
@@ -0,0 +1,1515 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "device/bluetooth/bluetooth_adapter_chromeos.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/sequenced_task_runner.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "chromeos/system/devicetype.h"
+#include "device/bluetooth/bluetooth_adapter_profile_chromeos.h"
+#include "device/bluetooth/bluetooth_advertisement_chromeos.h"
+#include "device/bluetooth/bluetooth_audio_sink_chromeos.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_device_chromeos.h"
+#include "device/bluetooth/bluetooth_discovery_session_outcome.h"
+#include "device/bluetooth/bluetooth_pairing_chromeos.h"
+#include "device/bluetooth/bluetooth_remote_gatt_characteristic_chromeos.h"
+#include "device/bluetooth/bluetooth_remote_gatt_descriptor_chromeos.h"
+#include "device/bluetooth/bluetooth_remote_gatt_service_chromeos.h"
+#include "device/bluetooth/bluetooth_socket_chromeos.h"
+#include "device/bluetooth/bluetooth_socket_thread.h"
+#include "device/bluetooth/bluetooth_uuid.h"
+#include "device/bluetooth/dbus/bluetooth_adapter_client.h"
+#include "device/bluetooth/dbus/bluetooth_agent_manager_client.h"
+#include "device/bluetooth/dbus/bluetooth_agent_service_provider.h"
+#include "device/bluetooth/dbus/bluetooth_device_client.h"
+#include "device/bluetooth/dbus/bluetooth_input_client.h"
+#include "device/bluetooth/dbus/bluez_dbus_manager.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+using device::BluetoothAdapter;
+using device::BluetoothAudioSink;
+using device::BluetoothDevice;
+using device::BluetoothDiscoveryFilter;
+using device::BluetoothSocket;
+using device::BluetoothUUID;
+using device::UMABluetoothDiscoverySessionOutcome;
+
+namespace {
+
+// The agent path is relatively meaningless since BlueZ only permits one to
+// exist per D-Bus connection, it just has to be unique within Chromium.
+const char kAgentPath[] = "/org/chromium/bluetooth_agent";
+
+void OnUnregisterAgentError(const std::string& error_name,
+ const std::string& error_message) {
+ // It's okay if the agent didn't exist, it means we never saw an adapter.
+ if (error_name == bluetooth_agent_manager::kErrorDoesNotExist)
+ return;
+
+ LOG(WARNING) << "Failed to unregister pairing agent: "
+ << error_name << ": " << error_message;
+}
+
+UMABluetoothDiscoverySessionOutcome TranslateDiscoveryErrorToUMA(
+ const std::string& error_name) {
+ if (error_name == bluez::BluetoothAdapterClient::kUnknownAdapterError) {
+ return UMABluetoothDiscoverySessionOutcome::CHROMEOS_DBUS_UNKNOWN_ADAPTER;
+ } else if (error_name == bluez::BluetoothAdapterClient::kNoResponseError) {
+ return UMABluetoothDiscoverySessionOutcome::CHROMEOS_DBUS_NO_RESPONSE;
+ } else if (error_name == bluetooth_device::kErrorInProgress) {
+ return UMABluetoothDiscoverySessionOutcome::CHROMEOS_DBUS_IN_PROGRESS;
+ } else if (error_name == bluetooth_device::kErrorNotReady) {
+ return UMABluetoothDiscoverySessionOutcome::CHROMEOS_DBUS_NOT_READY;
+ } else if (error_name == bluetooth_device::kErrorFailed) {
+ return UMABluetoothDiscoverySessionOutcome::FAILED;
+ } else {
+ LOG(WARNING) << "Can't histogram DBus error " << error_name;
+ return UMABluetoothDiscoverySessionOutcome::UNKNOWN;
+ }
+}
+
+} // namespace
+
+namespace device {
+
+// static
+base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
+ const InitCallback& init_callback) {
+ return chromeos::BluetoothAdapterChromeOS::CreateAdapter();
+}
+
+} // namespace device
+
+namespace chromeos {
+
+// static
+base::WeakPtr<BluetoothAdapter> BluetoothAdapterChromeOS::CreateAdapter() {
+ BluetoothAdapterChromeOS* adapter = new BluetoothAdapterChromeOS();
+ return adapter->weak_ptr_factory_.GetWeakPtr();
+}
+
+void BluetoothAdapterChromeOS::Shutdown() {
+ if (dbus_is_shutdown_)
+ return;
+ DCHECK(bluez::BluezDBusManager::IsInitialized())
+ << "Call BluetoothAdapterFactory::Shutdown() before "
+ "BluezDBusManager::Shutdown().";
+
+ if (IsPresent())
+ RemoveAdapter(); // Also deletes devices_.
+ DCHECK(devices_.empty());
+ // profiles_ is empty because all BluetoothSockets have been notified
+ // that this adapter is disappearing.
+ DCHECK(profiles_.empty());
+
+ for (auto& it : profile_queues_)
+ delete it.second;
+ profile_queues_.clear();
+
+ bluez::BluezDBusManager::Get()->GetBluetoothAdapterClient()->RemoveObserver(
+ this);
+ bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->RemoveObserver(
+ this);
+ bluez::BluezDBusManager::Get()->GetBluetoothInputClient()->RemoveObserver(
+ this);
+
+ VLOG(1) << "Unregistering pairing agent";
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAgentManagerClient()
+ ->UnregisterAgent(dbus::ObjectPath(kAgentPath),
+ base::Bind(&base::DoNothing),
+ base::Bind(&OnUnregisterAgentError));
+
+ agent_.reset();
+ dbus_is_shutdown_ = true;
+}
+
+BluetoothAdapterChromeOS::BluetoothAdapterChromeOS()
+ : dbus_is_shutdown_(false),
+ num_discovery_sessions_(0),
+ discovery_request_pending_(false),
+ weak_ptr_factory_(this) {
+ ui_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+ socket_thread_ = device::BluetoothSocketThread::Get();
+
+ bluez::BluezDBusManager::Get()->GetBluetoothAdapterClient()->AddObserver(
+ this);
+ bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->AddObserver(this);
+ bluez::BluezDBusManager::Get()->GetBluetoothInputClient()->AddObserver(this);
+
+ // Register the pairing agent.
+ dbus::Bus* system_bus = bluez::BluezDBusManager::Get()->GetSystemBus();
+ agent_.reset(bluez::BluetoothAgentServiceProvider::Create(
+ system_bus, dbus::ObjectPath(kAgentPath), this));
+ DCHECK(agent_.get());
+
+ std::vector<dbus::ObjectPath> object_paths = bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->GetAdapters();
+
+ if (!object_paths.empty()) {
+ VLOG(1) << object_paths.size() << " Bluetooth adapter(s) available.";
+ SetAdapter(object_paths[0]);
+ }
+}
+
+BluetoothAdapterChromeOS::~BluetoothAdapterChromeOS() {
+ Shutdown();
+}
+
+std::string BluetoothAdapterChromeOS::GetAddress() const {
+ if (!IsPresent())
+ return std::string();
+
+ bluez::BluetoothAdapterClient::Properties* properties =
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->GetProperties(object_path_);
+ DCHECK(properties);
+
+ return BluetoothDevice::CanonicalizeAddress(properties->address.value());
+}
+
+std::string BluetoothAdapterChromeOS::GetName() const {
+ if (!IsPresent())
+ return std::string();
+
+ bluez::BluetoothAdapterClient::Properties* properties =
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->GetProperties(object_path_);
+ DCHECK(properties);
+
+ return properties->alias.value();
+}
+
+void BluetoothAdapterChromeOS::SetName(const std::string& name,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ if (!IsPresent()) {
+ error_callback.Run();
+ return;
+ }
+
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->GetProperties(object_path_)
+ ->alias.Set(
+ name,
+ base::Bind(&BluetoothAdapterChromeOS::OnPropertyChangeCompleted,
+ weak_ptr_factory_.GetWeakPtr(), callback, error_callback));
+}
+
+bool BluetoothAdapterChromeOS::IsInitialized() const {
+ return true;
+}
+
+bool BluetoothAdapterChromeOS::IsPresent() const {
+ return !dbus_is_shutdown_ && !object_path_.value().empty();
+}
+
+bool BluetoothAdapterChromeOS::IsPowered() const {
+ if (!IsPresent())
+ return false;
+
+ bluez::BluetoothAdapterClient::Properties* properties =
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->GetProperties(object_path_);
+
+ return properties->powered.value();
+}
+
+void BluetoothAdapterChromeOS::SetPowered(
+ bool powered,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ if (!IsPresent()) {
+ error_callback.Run();
+ return;
+ }
+
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->GetProperties(object_path_)
+ ->powered.Set(
+ powered,
+ base::Bind(&BluetoothAdapterChromeOS::OnPropertyChangeCompleted,
+ weak_ptr_factory_.GetWeakPtr(), callback, error_callback));
+}
+
+bool BluetoothAdapterChromeOS::IsDiscoverable() const {
+ if (!IsPresent())
+ return false;
+
+ bluez::BluetoothAdapterClient::Properties* properties =
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->GetProperties(object_path_);
+
+ return properties->discoverable.value();
+}
+
+void BluetoothAdapterChromeOS::SetDiscoverable(
+ bool discoverable,
+ const base::Closure& callback,
+ const ErrorCallback& error_callback) {
+ if (!IsPresent()) {
+ error_callback.Run();
+ return;
+ }
+
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->GetProperties(object_path_)
+ ->discoverable.Set(
+ discoverable,
+ base::Bind(&BluetoothAdapterChromeOS::OnSetDiscoverable,
+ weak_ptr_factory_.GetWeakPtr(), callback, error_callback));
+}
+
+bool BluetoothAdapterChromeOS::IsDiscovering() const {
+ if (!IsPresent())
+ return false;
+
+ bluez::BluetoothAdapterClient::Properties* properties =
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->GetProperties(object_path_);
+
+ return properties->discovering.value();
+}
+
+void BluetoothAdapterChromeOS::CreateRfcommService(
+ const BluetoothUUID& uuid,
+ const ServiceOptions& options,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) {
+ DCHECK(!dbus_is_shutdown_);
+ VLOG(1) << object_path_.value() << ": Creating RFCOMM service: "
+ << uuid.canonical_value();
+ scoped_refptr<BluetoothSocketChromeOS> socket =
+ BluetoothSocketChromeOS::CreateBluetoothSocket(
+ ui_task_runner_, socket_thread_);
+ socket->Listen(this,
+ BluetoothSocketChromeOS::kRfcomm,
+ uuid,
+ options,
+ base::Bind(callback, socket),
+ error_callback);
+}
+
+void BluetoothAdapterChromeOS::CreateL2capService(
+ const BluetoothUUID& uuid,
+ const ServiceOptions& options,
+ const CreateServiceCallback& callback,
+ const CreateServiceErrorCallback& error_callback) {
+ DCHECK(!dbus_is_shutdown_);
+ VLOG(1) << object_path_.value() << ": Creating L2CAP service: "
+ << uuid.canonical_value();
+ scoped_refptr<BluetoothSocketChromeOS> socket =
+ BluetoothSocketChromeOS::CreateBluetoothSocket(
+ ui_task_runner_, socket_thread_);
+ socket->Listen(this,
+ BluetoothSocketChromeOS::kL2cap,
+ uuid,
+ options,
+ base::Bind(callback, socket),
+ error_callback);
+}
+
+void BluetoothAdapterChromeOS::RegisterAudioSink(
+ const BluetoothAudioSink::Options& options,
+ const device::BluetoothAdapter::AcquiredCallback& callback,
+ const BluetoothAudioSink::ErrorCallback& error_callback) {
+ VLOG(1) << "Registering audio sink";
+ if (!this->IsPresent()) {
+ error_callback.Run(BluetoothAudioSink::ERROR_INVALID_ADAPTER);
+ return;
+ }
+ scoped_refptr<BluetoothAudioSinkChromeOS> audio_sink(
+ new BluetoothAudioSinkChromeOS(this));
+ audio_sink->Register(
+ options, base::Bind(&BluetoothAdapterChromeOS::OnRegisterAudioSink,
+ weak_ptr_factory_.GetWeakPtr(), callback,
+ error_callback, audio_sink),
+ error_callback);
+}
+
+void BluetoothAdapterChromeOS::RegisterAdvertisement(
+ scoped_ptr<device::BluetoothAdvertisement::Data> advertisement_data,
+ const CreateAdvertisementCallback& callback,
+ const CreateAdvertisementErrorCallback& error_callback) {
+ scoped_refptr<BluetoothAdvertisementChromeOS> advertisement(
+ new BluetoothAdvertisementChromeOS(advertisement_data.Pass(), this));
+ advertisement->Register(base::Bind(callback, advertisement), error_callback);
+}
+
+void BluetoothAdapterChromeOS::RemovePairingDelegateInternal(
+ BluetoothDevice::PairingDelegate* pairing_delegate) {
+ // Check if any device is using the pairing delegate.
+ // If so, clear the pairing context which will make any responses no-ops.
+ for (DevicesMap::iterator iter = devices_.begin();
+ iter != devices_.end(); ++iter) {
+ BluetoothDeviceChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceChromeOS*>(iter->second);
+
+ BluetoothPairingChromeOS* pairing = device_chromeos->GetPairing();
+ if (pairing && pairing->GetPairingDelegate() == pairing_delegate)
+ device_chromeos->EndPairing();
+ }
+}
+
+void BluetoothAdapterChromeOS::AdapterAdded(
+ const dbus::ObjectPath& object_path) {
+ // Set the adapter to the newly added adapter only if no adapter is present.
+ if (!IsPresent())
+ SetAdapter(object_path);
+}
+
+void BluetoothAdapterChromeOS::AdapterRemoved(
+ const dbus::ObjectPath& object_path) {
+ if (object_path == object_path_)
+ RemoveAdapter();
+}
+
+void BluetoothAdapterChromeOS::AdapterPropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) {
+ if (object_path != object_path_)
+ return;
+ DCHECK(IsPresent());
+
+ bluez::BluetoothAdapterClient::Properties* properties =
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->GetProperties(object_path_);
+
+ if (property_name == properties->powered.name()) {
+ PoweredChanged(properties->powered.value());
+ } else if (property_name == properties->discoverable.name()) {
+ DiscoverableChanged(properties->discoverable.value());
+ } else if (property_name == properties->discovering.name()) {
+ DiscoveringChanged(properties->discovering.value());
+ }
+}
+
+void BluetoothAdapterChromeOS::DeviceAdded(
+ const dbus::ObjectPath& object_path) {
+ DCHECK(bluez::BluezDBusManager::Get());
+ bluez::BluetoothDeviceClient::Properties* properties =
+ bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
+ object_path);
+ if (!properties || properties->adapter.value() != object_path_)
+ return;
+ DCHECK(IsPresent());
+
+ BluetoothDeviceChromeOS* device_chromeos =
+ new BluetoothDeviceChromeOS(this,
+ object_path,
+ ui_task_runner_,
+ socket_thread_);
+ DCHECK(devices_.find(device_chromeos->GetAddress()) == devices_.end());
+
+ devices_[device_chromeos->GetAddress()] = device_chromeos;
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ DeviceAdded(this, device_chromeos));
+}
+
+void BluetoothAdapterChromeOS::DeviceRemoved(
+ const dbus::ObjectPath& object_path) {
+ for (DevicesMap::iterator iter = devices_.begin();
+ iter != devices_.end(); ++iter) {
+ BluetoothDeviceChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceChromeOS*>(iter->second);
+ if (device_chromeos->object_path() == object_path) {
+ devices_.erase(iter);
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ DeviceRemoved(this, device_chromeos));
+ delete device_chromeos;
+ return;
+ }
+ }
+}
+
+void BluetoothAdapterChromeOS::DevicePropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) {
+ BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path);
+ if (!device_chromeos)
+ return;
+
+ bluez::BluetoothDeviceClient::Properties* properties =
+ bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->GetProperties(
+ object_path);
+
+ if (property_name == properties->address.name()) {
+ for (auto iter = devices_.begin(); iter != devices_.end(); ++iter) {
+ if (iter->second->GetAddress() == device_chromeos->GetAddress()) {
+ std::string old_address = iter->first;
+ VLOG(1) << "Device changed address, old: " << old_address
+ << " new: " << device_chromeos->GetAddress();
+ devices_.erase(iter);
+
+ DCHECK(devices_.find(device_chromeos->GetAddress()) == devices_.end());
+ devices_[device_chromeos->GetAddress()] = device_chromeos;
+ NotifyDeviceAddressChanged(device_chromeos, old_address);
+ break;
+ }
+ }
+ }
+
+ if (property_name == properties->bluetooth_class.name() ||
+ property_name == properties->address.name() ||
+ property_name == properties->alias.name() ||
+ property_name == properties->paired.name() ||
+ property_name == properties->trusted.name() ||
+ property_name == properties->connected.name() ||
+ property_name == properties->uuids.name() ||
+ property_name == properties->rssi.name() ||
+ property_name == properties->tx_power.name()) {
+ NotifyDeviceChanged(device_chromeos);
+ }
+
+ // When a device becomes paired, mark it as trusted so that the user does
+ // not need to approve every incoming connection
+ if (property_name == properties->paired.name() &&
+ properties->paired.value() && !properties->trusted.value()) {
+ device_chromeos->SetTrusted();
+ }
+
+ // UMA connection counting
+ if (property_name == properties->connected.name()) {
+ // PlayStation joystick tries to reconnect after disconnection from USB.
+ // If it is still not trusted, set it, so it becomes available on the
+ // list of known devices.
+ if (properties->connected.value() && device_chromeos->IsTrustable() &&
+ !properties->trusted.value())
+ device_chromeos->SetTrusted();
+
+ int count = 0;
+
+ for (DevicesMap::iterator iter = devices_.begin();
+ iter != devices_.end(); ++iter) {
+ if (iter->second->IsPaired() && iter->second->IsConnected())
+ ++count;
+ }
+
+ UMA_HISTOGRAM_COUNTS_100("Bluetooth.ConnectedDeviceCount", count);
+ }
+}
+
+void BluetoothAdapterChromeOS::InputPropertyChanged(
+ const dbus::ObjectPath& object_path,
+ const std::string& property_name) {
+ BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path);
+ if (!device_chromeos)
+ return;
+
+ bluez::BluetoothInputClient::Properties* properties =
+ bluez::BluezDBusManager::Get()->GetBluetoothInputClient()->GetProperties(
+ object_path);
+
+ // Properties structure can be removed, which triggers a change in the
+ // BluetoothDevice::IsConnectable() property, as does a change in the
+ // actual reconnect_mode property.
+ if (!properties || property_name == properties->reconnect_mode.name()) {
+ NotifyDeviceChanged(device_chromeos);
+ }
+}
+
+void BluetoothAdapterChromeOS::Released() {
+ VLOG(1) << "Release";
+ if (!IsPresent())
+ return;
+ DCHECK(agent_.get());
+
+ // Called after we unregister the pairing agent, e.g. when changing I/O
+ // capabilities. Nothing much to be done right now.
+}
+
+void BluetoothAdapterChromeOS::RequestPinCode(
+ const dbus::ObjectPath& device_path,
+ const PinCodeCallback& callback) {
+ DCHECK(IsPresent());
+ DCHECK(agent_.get());
+ VLOG(1) << device_path.value() << ": RequestPinCode";
+
+ BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+ if (!pairing) {
+ callback.Run(REJECTED, "");
+ return;
+ }
+
+ pairing->RequestPinCode(callback);
+}
+
+void BluetoothAdapterChromeOS::DisplayPinCode(
+ const dbus::ObjectPath& device_path,
+ const std::string& pincode) {
+ DCHECK(IsPresent());
+ DCHECK(agent_.get());
+ VLOG(1) << device_path.value() << ": DisplayPinCode: " << pincode;
+
+ BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+ if (!pairing)
+ return;
+
+ pairing->DisplayPinCode(pincode);
+}
+
+void BluetoothAdapterChromeOS::RequestPasskey(
+ const dbus::ObjectPath& device_path,
+ const PasskeyCallback& callback) {
+ DCHECK(IsPresent());
+ DCHECK(agent_.get());
+ VLOG(1) << device_path.value() << ": RequestPasskey";
+
+ BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+ if (!pairing) {
+ callback.Run(REJECTED, 0);
+ return;
+ }
+
+ pairing->RequestPasskey(callback);
+}
+
+void BluetoothAdapterChromeOS::DisplayPasskey(
+ const dbus::ObjectPath& device_path,
+ uint32 passkey,
+ uint16 entered) {
+ DCHECK(IsPresent());
+ DCHECK(agent_.get());
+ VLOG(1) << device_path.value() << ": DisplayPasskey: " << passkey
+ << " (" << entered << " entered)";
+
+ BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+ if (!pairing)
+ return;
+
+ if (entered == 0)
+ pairing->DisplayPasskey(passkey);
+
+ pairing->KeysEntered(entered);
+}
+
+void BluetoothAdapterChromeOS::RequestConfirmation(
+ const dbus::ObjectPath& device_path,
+ uint32 passkey,
+ const ConfirmationCallback& callback) {
+ DCHECK(IsPresent());
+ DCHECK(agent_.get());
+ VLOG(1) << device_path.value() << ": RequestConfirmation: " << passkey;
+
+ BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+ if (!pairing) {
+ callback.Run(REJECTED);
+ return;
+ }
+
+ pairing->RequestConfirmation(passkey, callback);
+}
+
+void BluetoothAdapterChromeOS::RequestAuthorization(
+ const dbus::ObjectPath& device_path,
+ const ConfirmationCallback& callback) {
+ DCHECK(IsPresent());
+ DCHECK(agent_.get());
+ VLOG(1) << device_path.value() << ": RequestAuthorization";
+
+ BluetoothPairingChromeOS* pairing = GetPairing(device_path);
+ if (!pairing) {
+ callback.Run(REJECTED);
+ return;
+ }
+
+ pairing->RequestAuthorization(callback);
+}
+
+void BluetoothAdapterChromeOS::AuthorizeService(
+ const dbus::ObjectPath& device_path,
+ const std::string& uuid,
+ const ConfirmationCallback& callback) {
+ DCHECK(IsPresent());
+ DCHECK(agent_.get());
+ VLOG(1) << device_path.value() << ": AuthorizeService: " << uuid;
+
+ BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(device_path);
+ if (!device_chromeos) {
+ callback.Run(CANCELLED);
+ return;
+ }
+
+ // We always set paired devices to Trusted, so the only reason that this
+ // method call would ever be called is in the case of a race condition where
+ // our "Set('Trusted', true)" method call is still pending in the Bluetooth
+ // daemon because it's busy handling the incoming connection.
+ if (device_chromeos->IsPaired()) {
+ callback.Run(SUCCESS);
+ return;
+ }
+
+ // TODO(keybuk): reject service authorizations when not paired, determine
+ // whether this is acceptable long-term.
+ LOG(WARNING) << "Rejecting service connection from unpaired device "
+ << device_chromeos->GetAddress() << " for UUID " << uuid;
+ callback.Run(REJECTED);
+}
+
+void BluetoothAdapterChromeOS::Cancel() {
+ DCHECK(IsPresent());
+ DCHECK(agent_.get());
+ VLOG(1) << "Cancel";
+}
+
+void BluetoothAdapterChromeOS::OnRegisterAgent() {
+ VLOG(1) << "Pairing agent registered, requesting to be made default";
+
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAgentManagerClient()
+ ->RequestDefaultAgent(
+ dbus::ObjectPath(kAgentPath),
+ base::Bind(&BluetoothAdapterChromeOS::OnRequestDefaultAgent,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&BluetoothAdapterChromeOS::OnRequestDefaultAgentError,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void BluetoothAdapterChromeOS::OnRegisterAgentError(
+ const std::string& error_name,
+ const std::string& error_message) {
+ // Our agent being already registered isn't an error.
+ if (error_name == bluetooth_agent_manager::kErrorAlreadyExists)
+ return;
+
+ LOG(WARNING) << ": Failed to register pairing agent: "
+ << error_name << ": " << error_message;
+}
+
+void BluetoothAdapterChromeOS::OnRequestDefaultAgent() {
+ VLOG(1) << "Pairing agent now default";
+}
+
+void BluetoothAdapterChromeOS::OnRequestDefaultAgentError(
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(WARNING) << ": Failed to make pairing agent default: "
+ << error_name << ": " << error_message;
+}
+
+void BluetoothAdapterChromeOS::OnRegisterAudioSink(
+ const device::BluetoothAdapter::AcquiredCallback& callback,
+ const device::BluetoothAudioSink::ErrorCallback& error_callback,
+ scoped_refptr<BluetoothAudioSink> audio_sink) {
+ if (!IsPresent()) {
+ VLOG(1) << "Failed to register audio sink, adapter not present";
+ error_callback.Run(BluetoothAudioSink::ERROR_INVALID_ADAPTER);
+ return;
+ }
+ DCHECK(audio_sink.get());
+ callback.Run(audio_sink);
+}
+
+BluetoothDeviceChromeOS*
+BluetoothAdapterChromeOS::GetDeviceWithPath(
+ const dbus::ObjectPath& object_path) {
+ if (!IsPresent())
+ return nullptr;
+
+ for (DevicesMap::iterator iter = devices_.begin(); iter != devices_.end();
+ ++iter) {
+ BluetoothDeviceChromeOS* device_chromeos =
+ static_cast<BluetoothDeviceChromeOS*>(iter->second);
+ if (device_chromeos->object_path() == object_path)
+ return device_chromeos;
+ }
+
+ return nullptr;
+}
+
+BluetoothPairingChromeOS* BluetoothAdapterChromeOS::GetPairing(
+ const dbus::ObjectPath& object_path) {
+ DCHECK(IsPresent());
+ BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path);
+ if (!device_chromeos) {
+ LOG(WARNING) << "Pairing Agent request for unknown device: "
+ << object_path.value();
+ return nullptr;
+ }
+
+ BluetoothPairingChromeOS* pairing = device_chromeos->GetPairing();
+ if (pairing)
+ return pairing;
+
+ // The device doesn't have its own pairing context, so this is an incoming
+ // pairing request that should use our best default delegate (if we have one).
+ BluetoothDevice::PairingDelegate* pairing_delegate = DefaultPairingDelegate();
+ if (!pairing_delegate)
+ return nullptr;
+
+ return device_chromeos->BeginPairing(pairing_delegate);
+}
+
+void BluetoothAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) {
+ DCHECK(!IsPresent());
+ DCHECK(!dbus_is_shutdown_);
+ object_path_ = object_path;
+
+ VLOG(1) << object_path_.value() << ": using adapter.";
+
+ VLOG(1) << "Registering pairing agent";
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAgentManagerClient()
+ ->RegisterAgent(
+ dbus::ObjectPath(kAgentPath),
+ bluetooth_agent_manager::kKeyboardDisplayCapability,
+ base::Bind(&BluetoothAdapterChromeOS::OnRegisterAgent,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::Bind(&BluetoothAdapterChromeOS::OnRegisterAgentError,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ SetDefaultAdapterName();
+
+ bluez::BluetoothAdapterClient::Properties* properties =
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->GetProperties(object_path_);
+
+ PresentChanged(true);
+
+ if (properties->powered.value())
+ PoweredChanged(true);
+ if (properties->discoverable.value())
+ DiscoverableChanged(true);
+ if (properties->discovering.value())
+ DiscoveringChanged(true);
+
+ std::vector<dbus::ObjectPath> device_paths =
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothDeviceClient()
+ ->GetDevicesForAdapter(object_path_);
+
+ for (std::vector<dbus::ObjectPath>::iterator iter = device_paths.begin();
+ iter != device_paths.end(); ++iter) {
+ DeviceAdded(*iter);
+ }
+}
+
+void BluetoothAdapterChromeOS::SetDefaultAdapterName() {
+ DCHECK(IsPresent());
+
+ std::string alias;
+ switch (chromeos::GetDeviceType()) {
+ case DeviceType::kChromebase:
+ alias = "Chromebase";
+ break;
+ case DeviceType::kChromebit:
+ alias = "Chromebit";
+ break;
+ case DeviceType::kChromebook:
+ alias = "Chromebook";
+ break;
+ case DeviceType::kChromebox:
+ alias = "Chromebox";
+ break;
+ case DeviceType::kUnknown:
+ alias = "Chromebook";
+ break;
+ }
+
+ SetName(alias, base::Bind(&base::DoNothing), base::Bind(&base::DoNothing));
+}
+
+void BluetoothAdapterChromeOS::RemoveAdapter() {
+ DCHECK(IsPresent());
+ VLOG(1) << object_path_.value() << ": adapter removed.";
+
+ bluez::BluetoothAdapterClient::Properties* properties =
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->GetProperties(object_path_);
+
+ object_path_ = dbus::ObjectPath("");
+
+ if (properties->powered.value())
+ PoweredChanged(false);
+ if (properties->discoverable.value())
+ DiscoverableChanged(false);
+ if (properties->discovering.value())
+ DiscoveringChanged(false);
+
+ // Copy the devices list here and clear the original so that when we
+ // send DeviceRemoved(), GetDevices() returns no devices.
+ DevicesMap devices = devices_;
+ devices_.clear();
+
+ for (DevicesMap::iterator iter = devices.begin();
+ iter != devices.end(); ++iter) {
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ DeviceRemoved(this, iter->second));
+ delete iter->second;
+ }
+
+ PresentChanged(false);
+}
+
+void BluetoothAdapterChromeOS::PoweredChanged(bool powered) {
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ AdapterPoweredChanged(this, powered));
+}
+
+void BluetoothAdapterChromeOS::DiscoverableChanged(bool discoverable) {
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ AdapterDiscoverableChanged(this, discoverable));
+}
+
+void BluetoothAdapterChromeOS::DiscoveringChanged(
+ bool discovering) {
+ // If the adapter stopped discovery due to a reason other than a request by
+ // us, reset the count to 0.
+ VLOG(1) << "Discovering changed: " << discovering;
+ if (!discovering && !discovery_request_pending_ &&
+ num_discovery_sessions_ > 0) {
+ VLOG(1) << "Marking sessions as inactive.";
+ num_discovery_sessions_ = 0;
+ MarkDiscoverySessionsAsInactive();
+ }
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ AdapterDiscoveringChanged(this, discovering));
+}
+
+void BluetoothAdapterChromeOS::PresentChanged(bool present) {
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ AdapterPresentChanged(this, present));
+}
+
+void BluetoothAdapterChromeOS::NotifyDeviceChanged(
+ BluetoothDeviceChromeOS* device) {
+ DCHECK(device);
+ DCHECK(device->adapter_ == this);
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ DeviceChanged(this, device));
+}
+
+void BluetoothAdapterChromeOS::NotifyDeviceAddressChanged(
+ BluetoothDeviceChromeOS* device,
+ const std::string& old_address) {
+ DCHECK(device->adapter_ == this);
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
+ DeviceAddressChanged(this, device, old_address));
+}
+
+void BluetoothAdapterChromeOS::NotifyGattServiceAdded(
+ BluetoothRemoteGattServiceChromeOS* service) {
+ DCHECK_EQ(service->GetAdapter(), this);
+ DCHECK_EQ(
+ static_cast<BluetoothDeviceChromeOS*>(service->GetDevice())->adapter_,
+ this);
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ GattServiceAdded(this, service->GetDevice(), service));
+}
+
+void BluetoothAdapterChromeOS::NotifyGattServiceRemoved(
+ BluetoothRemoteGattServiceChromeOS* service) {
+ DCHECK_EQ(service->GetAdapter(), this);
+ DCHECK_EQ(
+ static_cast<BluetoothDeviceChromeOS*>(service->GetDevice())->adapter_,
+ this);
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ GattServiceRemoved(this, service->GetDevice(), service));
+}
+
+void BluetoothAdapterChromeOS::NotifyGattServiceChanged(
+ BluetoothRemoteGattServiceChromeOS* service) {
+ DCHECK_EQ(service->GetAdapter(), this);
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ GattServiceChanged(this, service));
+}
+
+void BluetoothAdapterChromeOS::NotifyGattDiscoveryComplete(
+ BluetoothRemoteGattServiceChromeOS* service) {
+ DCHECK_EQ(service->GetAdapter(), this);
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ GattDiscoveryCompleteForService(this, service));
+}
+
+void BluetoothAdapterChromeOS::NotifyGattCharacteristicAdded(
+ BluetoothRemoteGattCharacteristicChromeOS* characteristic) {
+ DCHECK_EQ(static_cast<BluetoothRemoteGattServiceChromeOS*>(
+ characteristic->GetService())->GetAdapter(),
+ this);
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ GattCharacteristicAdded(this, characteristic));
+}
+
+void BluetoothAdapterChromeOS::NotifyGattCharacteristicRemoved(
+ BluetoothRemoteGattCharacteristicChromeOS* characteristic) {
+ DCHECK_EQ(static_cast<BluetoothRemoteGattServiceChromeOS*>(
+ characteristic->GetService())->GetAdapter(),
+ this);
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ GattCharacteristicRemoved(this, characteristic));
+}
+
+void BluetoothAdapterChromeOS::NotifyGattDescriptorAdded(
+ BluetoothRemoteGattDescriptorChromeOS* descriptor) {
+ DCHECK_EQ(static_cast<BluetoothRemoteGattServiceChromeOS*>(
+ descriptor->GetCharacteristic()->GetService())->GetAdapter(),
+ this);
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ GattDescriptorAdded(this, descriptor));
+}
+
+void BluetoothAdapterChromeOS::NotifyGattDescriptorRemoved(
+ BluetoothRemoteGattDescriptorChromeOS* descriptor) {
+ DCHECK_EQ(static_cast<BluetoothRemoteGattServiceChromeOS*>(
+ descriptor->GetCharacteristic()->GetService())->GetAdapter(),
+ this);
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ GattDescriptorRemoved(this, descriptor));
+}
+
+void BluetoothAdapterChromeOS::NotifyGattCharacteristicValueChanged(
+ BluetoothRemoteGattCharacteristicChromeOS* characteristic,
+ const std::vector<uint8>& value) {
+ DCHECK_EQ(static_cast<BluetoothRemoteGattServiceChromeOS*>(
+ characteristic->GetService())->GetAdapter(),
+ this);
+
+ FOR_EACH_OBSERVER(
+ BluetoothAdapter::Observer,
+ observers_,
+ GattCharacteristicValueChanged(this, characteristic, value));
+}
+
+void BluetoothAdapterChromeOS::NotifyGattDescriptorValueChanged(
+ BluetoothRemoteGattDescriptorChromeOS* descriptor,
+ const std::vector<uint8>& value) {
+ DCHECK_EQ(static_cast<BluetoothRemoteGattServiceChromeOS*>(
+ descriptor->GetCharacteristic()->GetService())->GetAdapter(),
+ this);
+
+ FOR_EACH_OBSERVER(BluetoothAdapter::Observer,
+ observers_,
+ GattDescriptorValueChanged(this, descriptor, value));
+}
+
+void BluetoothAdapterChromeOS::UseProfile(
+ const BluetoothUUID& uuid,
+ const dbus::ObjectPath& device_path,
+ const bluez::BluetoothProfileManagerClient::Options& options,
+ bluez::BluetoothProfileServiceProvider::Delegate* delegate,
+ const ProfileRegisteredCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ DCHECK(delegate);
+
+ if (!IsPresent()) {
+ VLOG(2) << "Adapter not present, erroring out";
+ error_callback.Run("Adapter not present");
+ return;
+ }
+
+ if (profiles_.find(uuid) != profiles_.end()) {
+ // TODO(jamuraa) check that the options are the same and error when they are
+ // not.
+ SetProfileDelegate(uuid, device_path, delegate, success_callback,
+ error_callback);
+ return;
+ }
+
+ if (profile_queues_.find(uuid) == profile_queues_.end()) {
+ BluetoothAdapterProfileChromeOS::Register(
+ uuid, options,
+ base::Bind(&BluetoothAdapterChromeOS::OnRegisterProfile, this, uuid),
+ base::Bind(&BluetoothAdapterChromeOS::OnRegisterProfileError, this,
+ uuid));
+
+ profile_queues_[uuid] = new std::vector<RegisterProfileCompletionPair>();
+ }
+
+ profile_queues_[uuid]->push_back(std::make_pair(
+ base::Bind(&BluetoothAdapterChromeOS::SetProfileDelegate, this, uuid,
+ device_path, delegate, success_callback, error_callback),
+ error_callback));
+}
+
+void BluetoothAdapterChromeOS::ReleaseProfile(
+ const dbus::ObjectPath& device_path,
+ BluetoothAdapterProfileChromeOS* profile) {
+ VLOG(2) << "Releasing Profile: " << profile->uuid().canonical_value()
+ << " from " << device_path.value();
+ profile->RemoveDelegate(
+ device_path, base::Bind(&BluetoothAdapterChromeOS::RemoveProfile,
+ weak_ptr_factory_.GetWeakPtr(), profile->uuid()));
+}
+
+void BluetoothAdapterChromeOS::RemoveProfile(const BluetoothUUID& uuid) {
+ VLOG(2) << "Remove Profile: " << uuid.canonical_value();
+
+ if (profiles_.find(uuid) != profiles_.end()) {
+ delete profiles_[uuid];
+ profiles_.erase(uuid);
+ }
+}
+
+void BluetoothAdapterChromeOS::OnRegisterProfile(
+ const BluetoothUUID& uuid,
+ scoped_ptr<BluetoothAdapterProfileChromeOS> profile) {
+ profiles_[uuid] = profile.release();
+
+ if (profile_queues_.find(uuid) == profile_queues_.end())
+ return;
+
+ for (auto& it : *profile_queues_[uuid])
+ it.first.Run();
+ delete profile_queues_[uuid];
+ profile_queues_.erase(uuid);
+}
+
+void BluetoothAdapterChromeOS::SetProfileDelegate(
+ const BluetoothUUID& uuid,
+ const dbus::ObjectPath& device_path,
+ bluez::BluetoothProfileServiceProvider::Delegate* delegate,
+ const ProfileRegisteredCallback& success_callback,
+ const ErrorCompletionCallback& error_callback) {
+ if (profiles_.find(uuid) == profiles_.end()) {
+ error_callback.Run("Cannot find profile!");
+ return;
+ }
+
+ if (profiles_[uuid]->SetDelegate(device_path, delegate)) {
+ success_callback.Run(profiles_[uuid]);
+ return;
+ }
+ // Already set
+ error_callback.Run(bluetooth_agent_manager::kErrorAlreadyExists);
+}
+
+void BluetoothAdapterChromeOS::OnRegisterProfileError(
+ const BluetoothUUID& uuid,
+ const std::string& error_name,
+ const std::string& error_message) {
+ VLOG(2) << object_path_.value() << ": Failed to register profile: "
+ << error_name << ": " << error_message;
+ if (profile_queues_.find(uuid) == profile_queues_.end())
+ return;
+
+ for (auto& it : *profile_queues_[uuid])
+ it.second.Run(error_message);
+
+ delete profile_queues_[uuid];
+ profile_queues_.erase(uuid);
+}
+
+void BluetoothAdapterChromeOS::OnSetDiscoverable(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback,
+ bool success) {
+ if (!IsPresent()) {
+ error_callback.Run();
+ return;
+ }
+
+ // Set the discoverable_timeout property to zero so the adapter remains
+ // discoverable forever.
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->GetProperties(object_path_)
+ ->discoverable_timeout.Set(
+ 0,
+ base::Bind(&BluetoothAdapterChromeOS::OnPropertyChangeCompleted,
+ weak_ptr_factory_.GetWeakPtr(), callback, error_callback));
+}
+
+void BluetoothAdapterChromeOS::OnPropertyChangeCompleted(
+ const base::Closure& callback,
+ const ErrorCallback& error_callback,
+ bool success) {
+ if (IsPresent() && success) {
+ callback.Run();
+ } else {
+ error_callback.Run();
+ }
+}
+
+void BluetoothAdapterChromeOS::AddDiscoverySession(
+ BluetoothDiscoveryFilter* discovery_filter,
+ const base::Closure& callback,
+ const DiscoverySessionErrorCallback& error_callback) {
+ if (!IsPresent()) {
+ error_callback.Run(
+ UMABluetoothDiscoverySessionOutcome::ADAPTER_NOT_PRESENT);
+ return;
+ }
+ VLOG(1) << __func__;
+ if (discovery_request_pending_) {
+ // The pending request is either to stop a previous session or to start a
+ // new one. Either way, queue this one.
+ DCHECK(num_discovery_sessions_ == 1 || num_discovery_sessions_ == 0);
+ VLOG(1) << "Pending request to start/stop device discovery. Queueing "
+ << "request to start a new discovery session.";
+ discovery_request_queue_.push(
+ std::make_tuple(discovery_filter, callback, error_callback));
+ return;
+ }
+
+ // The adapter is already discovering.
+ if (num_discovery_sessions_ > 0) {
+ DCHECK(IsDiscovering());
+ DCHECK(!discovery_request_pending_);
+ num_discovery_sessions_++;
+ SetDiscoveryFilter(BluetoothDiscoveryFilter::Merge(
+ GetMergedDiscoveryFilter().get(), discovery_filter),
+ callback, error_callback);
+ return;
+ }
+
+ // There are no active discovery sessions.
+ DCHECK_EQ(num_discovery_sessions_, 0);
+
+ if (discovery_filter) {
+ discovery_request_pending_ = true;
+
+ scoped_ptr<BluetoothDiscoveryFilter> df(new BluetoothDiscoveryFilter(
+ BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL));
+ df->CopyFrom(*discovery_filter);
+ SetDiscoveryFilter(
+ df.Pass(),
+ base::Bind(&BluetoothAdapterChromeOS::OnPreSetDiscoveryFilter,
+ weak_ptr_factory_.GetWeakPtr(), callback, error_callback),
+ base::Bind(&BluetoothAdapterChromeOS::OnPreSetDiscoveryFilterError,
+ weak_ptr_factory_.GetWeakPtr(), callback, error_callback));
+ return;
+ } else {
+ current_filter_.reset();
+ }
+
+ // This is the first request to start device discovery.
+ discovery_request_pending_ = true;
+ bluez::BluezDBusManager::Get()->GetBluetoothAdapterClient()->StartDiscovery(
+ object_path_,
+ base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery,
+ weak_ptr_factory_.GetWeakPtr(), callback, error_callback),
+ base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError,
+ weak_ptr_factory_.GetWeakPtr(), callback, error_callback));
+}
+
+void BluetoothAdapterChromeOS::RemoveDiscoverySession(
+ BluetoothDiscoveryFilter* discovery_filter,
+ const base::Closure& callback,
+ const DiscoverySessionErrorCallback& error_callback) {
+ if (!IsPresent()) {
+ error_callback.Run(
+ UMABluetoothDiscoverySessionOutcome::ADAPTER_NOT_PRESENT);
+ return;
+ }
+
+ VLOG(1) << __func__;
+ // There are active sessions other than the one currently being removed.
+ if (num_discovery_sessions_ > 1) {
+ DCHECK(IsDiscovering());
+ DCHECK(!discovery_request_pending_);
+ num_discovery_sessions_--;
+
+ SetDiscoveryFilter(GetMergedDiscoveryFilterMasked(discovery_filter),
+ callback, error_callback);
+ return;
+ }
+
+ // If there is a pending request to BlueZ, then queue this request.
+ if (discovery_request_pending_) {
+ VLOG(1) << "Pending request to start/stop device discovery. Queueing "
+ << "request to stop discovery session.";
+ error_callback.Run(
+ UMABluetoothDiscoverySessionOutcome::REMOVE_WITH_PENDING_REQUEST);
+ return;
+ }
+
+ // There are no active sessions. Return error.
+ if (num_discovery_sessions_ == 0) {
+ // TODO(armansito): This should never happen once we have the
+ // DiscoverySession API. Replace this case with an assert once it's
+ // the deprecated methods have been removed. (See crbug.com/3445008).
+ VLOG(1) << "No active discovery sessions. Returning error.";
+ error_callback.Run(
+ UMABluetoothDiscoverySessionOutcome::ACTIVE_SESSION_NOT_IN_ADAPTER);
+ return;
+ }
+
+ // There is exactly one active discovery session. Request BlueZ to stop
+ // discovery.
+ DCHECK_EQ(num_discovery_sessions_, 1);
+ discovery_request_pending_ = true;
+ bluez::BluezDBusManager::Get()->GetBluetoothAdapterClient()->StopDiscovery(
+ object_path_, base::Bind(&BluetoothAdapterChromeOS::OnStopDiscovery,
+ weak_ptr_factory_.GetWeakPtr(), callback),
+ base::Bind(&BluetoothAdapterChromeOS::OnStopDiscoveryError,
+ weak_ptr_factory_.GetWeakPtr(), error_callback));
+}
+
+void BluetoothAdapterChromeOS::SetDiscoveryFilter(
+ scoped_ptr<BluetoothDiscoveryFilter> discovery_filter,
+ const base::Closure& callback,
+ const DiscoverySessionErrorCallback& error_callback) {
+ if (!IsPresent()) {
+ error_callback.Run(UMABluetoothDiscoverySessionOutcome::ADAPTER_REMOVED);
+ return;
+ }
+
+ // If old and new filter are equal (null) then don't make request, just call
+ // succes callback
+ if (!current_filter_ && !discovery_filter.get()) {
+ callback.Run();
+ return;
+ }
+
+ // If old and new filter are not null and equal then don't make request, just
+ // call succes callback
+ if (current_filter_ && discovery_filter &&
+ current_filter_->Equals(*discovery_filter)) {
+ callback.Run();
+ return;
+ }
+
+ current_filter_.reset(discovery_filter.release());
+
+ bluez::BluetoothAdapterClient::DiscoveryFilter dbus_discovery_filter;
+
+ if (current_filter_.get()) {
+ uint16_t pathloss;
+ int16_t rssi;
+ uint8_t transport;
+ std::set<device::BluetoothUUID> uuids;
+
+ if (current_filter_->GetPathloss(&pathloss))
+ dbus_discovery_filter.pathloss.reset(new uint16_t(pathloss));
+
+ if (current_filter_->GetRSSI(&rssi))
+ dbus_discovery_filter.rssi.reset(new int16_t(rssi));
+
+ transport = current_filter_->GetTransport();
+ if (transport == BluetoothDiscoveryFilter::Transport::TRANSPORT_LE) {
+ dbus_discovery_filter.transport.reset(new std::string("le"));
+ } else if (transport ==
+ BluetoothDiscoveryFilter::Transport::TRANSPORT_CLASSIC) {
+ dbus_discovery_filter.transport.reset(new std::string("bredr"));
+ } else if (transport ==
+ BluetoothDiscoveryFilter::Transport::TRANSPORT_DUAL) {
+ dbus_discovery_filter.transport.reset(new std::string("auto"));
+ }
+
+ current_filter_->GetUUIDs(uuids);
+ if (uuids.size()) {
+ dbus_discovery_filter.uuids =
+ scoped_ptr<std::vector<std::string>>(new std::vector<std::string>);
+
+ for (const auto& it : uuids)
+ dbus_discovery_filter.uuids.get()->push_back(it.value());
+ }
+ }
+
+ bluez::BluezDBusManager::Get()
+ ->GetBluetoothAdapterClient()
+ ->SetDiscoveryFilter(
+ object_path_, dbus_discovery_filter,
+ base::Bind(&BluetoothAdapterChromeOS::OnSetDiscoveryFilter,
+ weak_ptr_factory_.GetWeakPtr(), callback, error_callback),
+ base::Bind(&BluetoothAdapterChromeOS::OnSetDiscoveryFilterError,
+ weak_ptr_factory_.GetWeakPtr(), callback, error_callback));
+}
+
+void BluetoothAdapterChromeOS::OnStartDiscovery(
+ const base::Closure& callback,
+ const DiscoverySessionErrorCallback& error_callback) {
+ // Report success on the original request and increment the count.
+ VLOG(1) << __func__;
+ DCHECK(discovery_request_pending_);
+ DCHECK_EQ(num_discovery_sessions_, 0);
+ discovery_request_pending_ = false;
+ num_discovery_sessions_++;
+ if (IsPresent()) {
+ callback.Run();
+ } else {
+ error_callback.Run(UMABluetoothDiscoverySessionOutcome::ADAPTER_REMOVED);
+ }
+
+ // Try to add a new discovery session for each queued request.
+ ProcessQueuedDiscoveryRequests();
+}
+
+void BluetoothAdapterChromeOS::OnStartDiscoveryError(
+ const base::Closure& callback,
+ const DiscoverySessionErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(WARNING) << object_path_.value() << ": Failed to start discovery: "
+ << error_name << ": " << error_message;
+
+ // Failed to start discovery. This can only happen if the count is at 0.
+ DCHECK_EQ(num_discovery_sessions_, 0);
+ DCHECK(discovery_request_pending_);
+ discovery_request_pending_ = false;
+
+ // Discovery request may fail if discovery was previously initiated by Chrome,
+ // but the session were invalidated due to the discovery state unexpectedly
+ // changing to false and then back to true. In this case, report success.
+ if (IsPresent() && error_name == bluetooth_device::kErrorInProgress &&
+ IsDiscovering()) {
+ VLOG(1) << "Discovery previously initiated. Reporting success.";
+ num_discovery_sessions_++;
+ callback.Run();
+ } else {
+ error_callback.Run(TranslateDiscoveryErrorToUMA(error_name));
+ }
+
+ // Try to add a new discovery session for each queued request.
+ ProcessQueuedDiscoveryRequests();
+}
+
+void BluetoothAdapterChromeOS::OnStopDiscovery(const base::Closure& callback) {
+ // Report success on the original request and decrement the count.
+ VLOG(1) << __func__;
+ DCHECK(discovery_request_pending_);
+ DCHECK_EQ(num_discovery_sessions_, 1);
+ discovery_request_pending_ = false;
+ num_discovery_sessions_--;
+ callback.Run();
+
+ current_filter_.reset();
+
+ // Try to add a new discovery session for each queued request.
+ ProcessQueuedDiscoveryRequests();
+}
+
+void BluetoothAdapterChromeOS::OnStopDiscoveryError(
+ const DiscoverySessionErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(WARNING) << object_path_.value() << ": Failed to stop discovery: "
+ << error_name << ": " << error_message;
+
+ // Failed to stop discovery. This can only happen if the count is at 1.
+ DCHECK(discovery_request_pending_);
+ DCHECK_EQ(num_discovery_sessions_, 1);
+ discovery_request_pending_ = false;
+ error_callback.Run(TranslateDiscoveryErrorToUMA(error_name));
+
+ // Try to add a new discovery session for each queued request.
+ ProcessQueuedDiscoveryRequests();
+}
+
+void BluetoothAdapterChromeOS::OnPreSetDiscoveryFilter(
+ const base::Closure& callback,
+ const DiscoverySessionErrorCallback& error_callback) {
+ // This is the first request to start device discovery.
+ DCHECK(discovery_request_pending_);
+ DCHECK_EQ(num_discovery_sessions_, 0);
+
+ bluez::BluezDBusManager::Get()->GetBluetoothAdapterClient()->StartDiscovery(
+ object_path_,
+ base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery,
+ weak_ptr_factory_.GetWeakPtr(), callback, error_callback),
+ base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError,
+ weak_ptr_factory_.GetWeakPtr(), callback, error_callback));
+}
+
+void BluetoothAdapterChromeOS::OnPreSetDiscoveryFilterError(
+ const base::Closure& callback,
+ const DiscoverySessionErrorCallback& error_callback,
+ UMABluetoothDiscoverySessionOutcome outcome) {
+ LOG(WARNING) << object_path_.value()
+ << ": Failed to pre set discovery filter.";
+
+ // Failed to start discovery. This can only happen if the count is at 0.
+ DCHECK_EQ(num_discovery_sessions_, 0);
+ DCHECK(discovery_request_pending_);
+ discovery_request_pending_ = false;
+
+ error_callback.Run(outcome);
+
+ // Try to add a new discovery session for each queued request.
+ ProcessQueuedDiscoveryRequests();
+}
+
+void BluetoothAdapterChromeOS::OnSetDiscoveryFilter(
+ const base::Closure& callback,
+ const DiscoverySessionErrorCallback& error_callback) {
+ // Report success on the original request and increment the count.
+ VLOG(1) << __func__;
+ if (IsPresent()) {
+ callback.Run();
+ } else {
+ error_callback.Run(UMABluetoothDiscoverySessionOutcome::ADAPTER_REMOVED);
+ }
+}
+
+void BluetoothAdapterChromeOS::OnSetDiscoveryFilterError(
+ const base::Closure& callback,
+ const DiscoverySessionErrorCallback& error_callback,
+ const std::string& error_name,
+ const std::string& error_message) {
+ LOG(WARNING) << object_path_.value()
+ << ": Failed to set discovery filter: " << error_name << ": "
+ << error_message;
+
+ UMABluetoothDiscoverySessionOutcome outcome =
+ TranslateDiscoveryErrorToUMA(error_name);
+ if (outcome == UMABluetoothDiscoverySessionOutcome::FAILED) {
+ // bluez/doc/adapter-api.txt says "Failed" is returned from
+ // SetDiscoveryFilter when the controller doesn't support the requested
+ // transport.
+ outcome = UMABluetoothDiscoverySessionOutcome::
+ CHROMEOS_DBUS_FAILED_MAYBE_UNSUPPORTED_TRANSPORT;
+ }
+ error_callback.Run(outcome);
+
+ // Try to add a new discovery session for each queued request.
+ ProcessQueuedDiscoveryRequests();
+}
+
+void BluetoothAdapterChromeOS::ProcessQueuedDiscoveryRequests() {
+ while (!discovery_request_queue_.empty()) {
+ VLOG(1) << "Process queued discovery request.";
+ DiscoveryParamTuple params = discovery_request_queue_.front();
+ discovery_request_queue_.pop();
+ AddDiscoverySession(std::get<0>(params), std::get<1>(params),
+ std::get<2>(params));
+
+ // If the queued request resulted in a pending call, then let it
+ // asynchonously process the remaining queued requests once the pending
+ // call returns.
+ if (discovery_request_pending_)
+ return;
+ }
+}
+
+} // namespace chromeos