| // 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/logging.h" |
| #include "base/metrics/histogram.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/sys_info.h" |
| #include "base/thread_task_runner_handle.h" |
| #include "chromeos/dbus/bluetooth_adapter_client.h" |
| #include "chromeos/dbus/bluetooth_agent_manager_client.h" |
| #include "chromeos/dbus/bluetooth_agent_service_provider.h" |
| #include "chromeos/dbus/bluetooth_device_client.h" |
| #include "chromeos/dbus/bluetooth_input_client.h" |
| #include "chromeos/dbus/dbus_thread_manager.h" |
| #include "device/bluetooth/bluetooth_device.h" |
| #include "device/bluetooth/bluetooth_device_chromeos.h" |
| #include "device/bluetooth/bluetooth_pairing_chromeos.h" |
| #include "device/bluetooth/bluetooth_socket_thread.h" |
| #include "third_party/cros_system_api/dbus/service_constants.h" |
| |
| using device::BluetoothAdapter; |
| using device::BluetoothDevice; |
| |
| 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; |
| } |
| |
| } // namespace |
| |
| namespace device { |
| |
| // static |
| base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter( |
| const InitCallback& init_callback) { |
| return chromeos::BluetoothAdapterChromeOS::CreateAdapter(); |
| } |
| |
| } |
| |
| namespace chromeos { |
| |
| // static |
| base::WeakPtr<BluetoothAdapter> BluetoothAdapterChromeOS::CreateAdapter() { |
| BluetoothAdapterChromeOS* adapter = new BluetoothAdapterChromeOS(); |
| return adapter->weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| BluetoothAdapterChromeOS::BluetoothAdapterChromeOS() |
| : num_discovery_sessions_(0), |
| discovery_request_pending_(false), |
| weak_ptr_factory_(this) { |
| ui_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |
| socket_thread_ = device::BluetoothSocketThread::Get(); |
| |
| DBusThreadManager::Get()->GetBluetoothAdapterClient()->AddObserver(this); |
| DBusThreadManager::Get()->GetBluetoothDeviceClient()->AddObserver(this); |
| DBusThreadManager::Get()->GetBluetoothInputClient()->AddObserver(this); |
| |
| // Register the pairing agent. |
| dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus(); |
| agent_.reset(BluetoothAgentServiceProvider::Create( |
| system_bus, dbus::ObjectPath(kAgentPath), this)); |
| DCHECK(agent_.get()); |
| |
| std::vector<dbus::ObjectPath> object_paths = |
| DBusThreadManager::Get()->GetBluetoothAdapterClient()->GetAdapters(); |
| |
| if (!object_paths.empty()) { |
| VLOG(1) << object_paths.size() << " Bluetooth adapter(s) available."; |
| SetAdapter(object_paths[0]); |
| } |
| } |
| |
| BluetoothAdapterChromeOS::~BluetoothAdapterChromeOS() { |
| DBusThreadManager::Get()->GetBluetoothAdapterClient()->RemoveObserver(this); |
| DBusThreadManager::Get()->GetBluetoothDeviceClient()->RemoveObserver(this); |
| DBusThreadManager::Get()->GetBluetoothInputClient()->RemoveObserver(this); |
| |
| VLOG(1) << "Unregistering pairing agent"; |
| DBusThreadManager::Get()->GetBluetoothAgentManagerClient()-> |
| UnregisterAgent( |
| dbus::ObjectPath(kAgentPath), |
| base::Bind(&base::DoNothing), |
| base::Bind(&OnUnregisterAgentError)); |
| } |
| |
| void BluetoothAdapterChromeOS::AddObserver( |
| BluetoothAdapter::Observer* observer) { |
| DCHECK(observer); |
| observers_.AddObserver(observer); |
| } |
| |
| void BluetoothAdapterChromeOS::RemoveObserver( |
| BluetoothAdapter::Observer* observer) { |
| DCHECK(observer); |
| observers_.RemoveObserver(observer); |
| } |
| |
| std::string BluetoothAdapterChromeOS::GetAddress() const { |
| if (!IsPresent()) |
| return std::string(); |
| |
| BluetoothAdapterClient::Properties* properties = |
| DBusThreadManager::Get()->GetBluetoothAdapterClient()-> |
| GetProperties(object_path_); |
| DCHECK(properties); |
| |
| return properties->address.value(); |
| } |
| |
| std::string BluetoothAdapterChromeOS::GetName() const { |
| if (!IsPresent()) |
| return std::string(); |
| |
| BluetoothAdapterClient::Properties* properties = |
| DBusThreadManager::Get()->GetBluetoothAdapterClient()-> |
| GetProperties(object_path_); |
| DCHECK(properties); |
| |
| return properties->alias.value(); |
| } |
| |
| void BluetoothAdapterChromeOS::SetName(const std::string& name, |
| const base::Closure& callback, |
| const ErrorCallback& error_callback) { |
| if (!IsPresent()) |
| error_callback.Run(); |
| |
| DBusThreadManager::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 !object_path_.value().empty(); |
| } |
| |
| bool BluetoothAdapterChromeOS::IsPowered() const { |
| if (!IsPresent()) |
| return false; |
| |
| BluetoothAdapterClient::Properties* properties = |
| DBusThreadManager::Get()->GetBluetoothAdapterClient()-> |
| GetProperties(object_path_); |
| |
| return properties->powered.value(); |
| } |
| |
| void BluetoothAdapterChromeOS::SetPowered( |
| bool powered, |
| const base::Closure& callback, |
| const ErrorCallback& error_callback) { |
| if (!IsPresent()) |
| error_callback.Run(); |
| |
| DBusThreadManager::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; |
| |
| BluetoothAdapterClient::Properties* properties = |
| DBusThreadManager::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(); |
| |
| DBusThreadManager::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; |
| |
| BluetoothAdapterClient::Properties* properties = |
| DBusThreadManager::Get()->GetBluetoothAdapterClient()-> |
| GetProperties(object_path_); |
| |
| return properties->discovering.value(); |
| } |
| |
| void BluetoothAdapterChromeOS::ReadLocalOutOfBandPairingData( |
| const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback, |
| const ErrorCallback& error_callback) { |
| error_callback.Run(); |
| } |
| |
| void BluetoothAdapterChromeOS::RemovePairingDelegateInternal( |
| BluetoothDevice::PairingDelegate* pairing_delegate) { |
| // Before removing a pairing delegate make sure that there aren't any devices |
| // currently using it; if there are, clear the pairing context which will |
| // make any responses no-ops. |
| for (DevicesMap::iterator iter = devices_.begin(); |
| iter != devices_.end(); ++iter) { |
| BluetoothDeviceChromeOS* device_chromeos = |
| static_cast<BluetoothDeviceChromeOS*>(iter->second); |
| |
| BluetoothPairingChromeOS* pairing = device_chromeos->GetPairing(); |
| if (pairing && pairing->GetPairingDelegate() == pairing_delegate) |
| device_chromeos->EndPairing(); |
| } |
| } |
| |
| void BluetoothAdapterChromeOS::AdapterAdded( |
| const dbus::ObjectPath& object_path) { |
| // Set the adapter to the newly added adapter only if no adapter is present. |
| if (!IsPresent()) |
| SetAdapter(object_path); |
| } |
| |
| void BluetoothAdapterChromeOS::AdapterRemoved( |
| const dbus::ObjectPath& object_path) { |
| if (object_path == object_path_) |
| RemoveAdapter(); |
| } |
| |
| void BluetoothAdapterChromeOS::AdapterPropertyChanged( |
| const dbus::ObjectPath& object_path, |
| const std::string& property_name) { |
| if (object_path != object_path_) |
| return; |
| |
| BluetoothAdapterClient::Properties* properties = |
| DBusThreadManager::Get()->GetBluetoothAdapterClient()-> |
| GetProperties(object_path_); |
| |
| if (property_name == properties->powered.name()) |
| PoweredChanged(properties->powered.value()); |
| else if (property_name == properties->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) { |
| BluetoothDeviceClient::Properties* properties = |
| DBusThreadManager::Get()->GetBluetoothDeviceClient()-> |
| GetProperties(object_path); |
| if (properties->adapter.value() != object_path_) |
| return; |
| |
| 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; |
| |
| BluetoothDeviceClient::Properties* properties = |
| DBusThreadManager::Get()->GetBluetoothDeviceClient()-> |
| GetProperties(object_path); |
| |
| if (property_name == properties->bluetooth_class.name() || |
| property_name == properties->address.name() || |
| property_name == properties->alias.name() || |
| property_name == properties->paired.name() || |
| property_name == properties->trusted.name() || |
| property_name == properties->connected.name() || |
| property_name == properties->uuids.name()) |
| NotifyDeviceChanged(device_chromeos); |
| |
| // 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()) |
| device_chromeos->SetTrusted(); |
| |
| // UMA connection counting |
| if (property_name == properties->connected.name()) { |
| int count = 0; |
| |
| for (DevicesMap::iterator iter = devices_.begin(); |
| iter != devices_.end(); ++iter) { |
| if (iter->second->IsPaired() && iter->second->IsConnected()) |
| ++count; |
| } |
| |
| UMA_HISTOGRAM_COUNTS_100("Bluetooth.ConnectedDeviceCount", count); |
| } |
| } |
| |
| void BluetoothAdapterChromeOS::InputPropertyChanged( |
| const dbus::ObjectPath& object_path, |
| const std::string& property_name) { |
| BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path); |
| if (!device_chromeos) |
| return; |
| |
| BluetoothInputClient::Properties* properties = |
| DBusThreadManager::Get()->GetBluetoothInputClient()-> |
| GetProperties(object_path); |
| |
| // Properties structure can be removed, which triggers a change in the |
| // BluetoothDevice::IsConnectable() property, as does a change in the |
| // actual reconnect_mode property. |
| if (!properties || |
| property_name == properties->reconnect_mode.name()) |
| NotifyDeviceChanged(device_chromeos); |
| } |
| |
| void BluetoothAdapterChromeOS::Released() { |
| DCHECK(agent_.get()); |
| VLOG(1) << "Release"; |
| |
| // 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(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(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(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(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(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(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(agent_.get()); |
| VLOG(1) << device_path.value() << ": AuthorizeService: " << uuid; |
| |
| // TODO(keybuk): implement |
| callback.Run(CANCELLED); |
| } |
| |
| void BluetoothAdapterChromeOS::Cancel() { |
| DCHECK(agent_.get()); |
| VLOG(1) << "Cancel"; |
| } |
| |
| void BluetoothAdapterChromeOS::OnRegisterAgent() { |
| VLOG(1) << "Pairing agent registered, requesting to be made default"; |
| |
| DBusThreadManager::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; |
| } |
| |
| BluetoothDeviceChromeOS* |
| BluetoothAdapterChromeOS::GetDeviceWithPath( |
| 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) |
| return device_chromeos; |
| } |
| |
| return NULL; |
| } |
| |
| BluetoothPairingChromeOS* BluetoothAdapterChromeOS::GetPairing( |
| const dbus::ObjectPath& object_path) |
| { |
| BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path); |
| if (!device_chromeos) { |
| LOG(WARNING) << "Pairing Agent request for unknown device: " |
| << object_path.value(); |
| return NULL; |
| } |
| |
| BluetoothPairingChromeOS* pairing = device_chromeos->GetPairing(); |
| if (pairing) |
| return pairing; |
| |
| // The device doesn't have its own pairing context, so this is an incoming |
| // pairing request that should use our best default delegate (if we have one). |
| BluetoothDevice::PairingDelegate* pairing_delegate = DefaultPairingDelegate(); |
| if (!pairing_delegate) |
| return NULL; |
| |
| return device_chromeos->BeginPairing(pairing_delegate); |
| } |
| |
| void BluetoothAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) { |
| DCHECK(!IsPresent()); |
| object_path_ = object_path; |
| |
| VLOG(1) << object_path_.value() << ": using adapter."; |
| |
| VLOG(1) << "Registering pairing agent"; |
| DBusThreadManager::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(); |
| |
| BluetoothAdapterClient::Properties* properties = |
| DBusThreadManager::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 = |
| DBusThreadManager::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() { |
| std::string board = base::SysInfo::GetLsbReleaseBoard(); |
| std::string alias; |
| if (board.substr(0, 6) == "stumpy") { |
| alias = "Chromebox"; |
| } else if (board.substr(0, 4) == "link") { |
| alias = "Chromebook Pixel"; |
| } else { |
| alias = "Chromebook"; |
| } |
| |
| SetName(alias, base::Bind(&base::DoNothing), base::Bind(&base::DoNothing)); |
| } |
| |
| void BluetoothAdapterChromeOS::RemoveAdapter() { |
| DCHECK(IsPresent()); |
| VLOG(1) << object_path_.value() << ": adapter removed."; |
| |
| BluetoothAdapterClient::Properties* properties = |
| DBusThreadManager::Get()->GetBluetoothAdapterClient()-> |
| GetProperties(object_path_); |
| |
| object_path_ = dbus::ObjectPath(""); |
| |
| if (properties->powered.value()) |
| PoweredChanged(false); |
| if (properties->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->adapter_ == this); |
| |
| FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, |
| DeviceChanged(this, device)); |
| } |
| |
| void BluetoothAdapterChromeOS::OnSetDiscoverable( |
| const base::Closure& callback, |
| const ErrorCallback& error_callback, |
| bool success) { |
| // Set the discoverable_timeout property to zero so the adapter remains |
| // discoverable forever. |
| DBusThreadManager::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 (success) |
| callback.Run(); |
| else |
| error_callback.Run(); |
| } |
| |
| void BluetoothAdapterChromeOS::AddDiscoverySession( |
| const base::Closure& callback, |
| const ErrorCallback& error_callback) { |
| 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_pair(callback, error_callback)); |
| return; |
| } |
| |
| // The adapter is already discovering. |
| if (num_discovery_sessions_ > 0) { |
| DCHECK(IsDiscovering()); |
| DCHECK(!discovery_request_pending_); |
| num_discovery_sessions_++; |
| callback.Run(); |
| return; |
| } |
| |
| // There are no active discovery sessions. |
| DCHECK(num_discovery_sessions_ == 0); |
| |
| // This is the first request to start device discovery. |
| discovery_request_pending_ = true; |
| DBusThreadManager::Get()->GetBluetoothAdapterClient()-> |
| StartDiscovery( |
| object_path_, |
| base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery, |
| weak_ptr_factory_.GetWeakPtr(), |
| callback), |
| base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError, |
| weak_ptr_factory_.GetWeakPtr(), |
| callback, |
| error_callback)); |
| } |
| |
| void BluetoothAdapterChromeOS::RemoveDiscoverySession( |
| const base::Closure& callback, |
| const ErrorCallback& error_callback) { |
| 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_--; |
| callback.Run(); |
| 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(); |
| 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(); |
| return; |
| } |
| |
| // There is exactly one active discovery session. Request BlueZ to stop |
| // discovery. |
| DCHECK(num_discovery_sessions_ == 1); |
| discovery_request_pending_ = true; |
| DBusThreadManager::Get()->GetBluetoothAdapterClient()-> |
| StopDiscovery( |
| object_path_, |
| base::Bind(&BluetoothAdapterChromeOS::OnStopDiscovery, |
| weak_ptr_factory_.GetWeakPtr(), |
| callback), |
| base::Bind(&BluetoothAdapterChromeOS::OnStopDiscoveryError, |
| weak_ptr_factory_.GetWeakPtr(), |
| error_callback)); |
| } |
| |
| void BluetoothAdapterChromeOS::OnStartDiscovery(const base::Closure& callback) { |
| // Report success on the original request and increment the count. |
| VLOG(1) << __func__; |
| DCHECK(discovery_request_pending_); |
| DCHECK(num_discovery_sessions_ == 0); |
| discovery_request_pending_ = false; |
| num_discovery_sessions_++; |
| callback.Run(); |
| |
| // Try to add a new discovery session for each queued request. |
| ProcessQueuedDiscoveryRequests(); |
| } |
| |
| void BluetoothAdapterChromeOS::OnStartDiscoveryError( |
| const base::Closure& callback, |
| const ErrorCallback& error_callback, |
| const std::string& error_name, |
| const std::string& error_message) { |
| LOG(WARNING) << object_path_.value() << ": Failed to start discovery: " |
| << error_name << ": " << error_message; |
| |
| // Failed to start discovery. This can only happen if the count is at 0. |
| DCHECK(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 (error_name == bluetooth_device::kErrorInProgress && IsDiscovering()) { |
| VLOG(1) << "Discovery previously initiated. Reporting success."; |
| num_discovery_sessions_++; |
| callback.Run(); |
| } else { |
| error_callback.Run(); |
| } |
| |
| // 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(num_discovery_sessions_ == 1); |
| discovery_request_pending_ = false; |
| num_discovery_sessions_--; |
| callback.Run(); |
| |
| // Try to add a new discovery session for each queued request. |
| ProcessQueuedDiscoveryRequests(); |
| } |
| |
| void BluetoothAdapterChromeOS::OnStopDiscoveryError( |
| const ErrorCallback& error_callback, |
| const std::string& error_name, |
| const std::string& error_message) { |
| LOG(WARNING) << object_path_.value() << ": Failed to stop discovery: " |
| << error_name << ": " << error_message; |
| |
| // Failed to stop discovery. This can only happen if the count is at 1. |
| DCHECK(discovery_request_pending_); |
| DCHECK(num_discovery_sessions_ == 1); |
| discovery_request_pending_ = false; |
| error_callback.Run(); |
| |
| // 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."; |
| DiscoveryCallbackPair callbacks = discovery_request_queue_.front(); |
| discovery_request_queue_.pop(); |
| AddDiscoverySession(callbacks.first, callbacks.second); |
| |
| // 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 |