Home | History | Annotate | Download | only in bluetooth
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "device/bluetooth/bluetooth_adapter_chromeos.h"
      6 
      7 #include <string>
      8 
      9 #include "base/bind.h"
     10 #include "base/logging.h"
     11 #include "base/metrics/histogram.h"
     12 #include "base/sys_info.h"
     13 #include "chromeos/dbus/bluetooth_adapter_client.h"
     14 #include "chromeos/dbus/bluetooth_device_client.h"
     15 #include "chromeos/dbus/bluetooth_input_client.h"
     16 #include "chromeos/dbus/dbus_thread_manager.h"
     17 #include "device/bluetooth/bluetooth_device.h"
     18 #include "device/bluetooth/bluetooth_device_chromeos.h"
     19 
     20 using device::BluetoothAdapter;
     21 using device::BluetoothDevice;
     22 
     23 namespace chromeos {
     24 
     25 BluetoothAdapterChromeOS::BluetoothAdapterChromeOS()
     26     : weak_ptr_factory_(this) {
     27   DBusThreadManager::Get()->GetBluetoothAdapterClient()->AddObserver(this);
     28   DBusThreadManager::Get()->GetBluetoothDeviceClient()->AddObserver(this);
     29   DBusThreadManager::Get()->GetBluetoothInputClient()->AddObserver(this);
     30 
     31   std::vector<dbus::ObjectPath> object_paths =
     32       DBusThreadManager::Get()->GetBluetoothAdapterClient()->GetAdapters();
     33 
     34   if (!object_paths.empty()) {
     35     VLOG(1) << object_paths.size() << " Bluetooth adapter(s) available.";
     36     SetAdapter(object_paths[0]);
     37   }
     38 }
     39 
     40 BluetoothAdapterChromeOS::~BluetoothAdapterChromeOS() {
     41   DBusThreadManager::Get()->GetBluetoothAdapterClient()->RemoveObserver(this);
     42   DBusThreadManager::Get()->GetBluetoothDeviceClient()->RemoveObserver(this);
     43   DBusThreadManager::Get()->GetBluetoothInputClient()->RemoveObserver(this);
     44 }
     45 
     46 void BluetoothAdapterChromeOS::AddObserver(
     47     BluetoothAdapter::Observer* observer) {
     48   DCHECK(observer);
     49   observers_.AddObserver(observer);
     50 }
     51 
     52 void BluetoothAdapterChromeOS::RemoveObserver(
     53     BluetoothAdapter::Observer* observer) {
     54   DCHECK(observer);
     55   observers_.RemoveObserver(observer);
     56 }
     57 
     58 std::string BluetoothAdapterChromeOS::GetAddress() const {
     59   if (!IsPresent())
     60     return std::string();
     61 
     62   BluetoothAdapterClient::Properties* properties =
     63       DBusThreadManager::Get()->GetBluetoothAdapterClient()->
     64           GetProperties(object_path_);
     65   DCHECK(properties);
     66 
     67   return properties->address.value();
     68 }
     69 
     70 std::string BluetoothAdapterChromeOS::GetName() const {
     71   if (!IsPresent())
     72     return std::string();
     73 
     74   BluetoothAdapterClient::Properties* properties =
     75       DBusThreadManager::Get()->GetBluetoothAdapterClient()->
     76           GetProperties(object_path_);
     77   DCHECK(properties);
     78 
     79   return properties->alias.value();
     80 }
     81 
     82 bool BluetoothAdapterChromeOS::IsInitialized() const {
     83   return true;
     84 }
     85 
     86 bool BluetoothAdapterChromeOS::IsPresent() const {
     87   return !object_path_.value().empty();
     88 }
     89 
     90 bool BluetoothAdapterChromeOS::IsPowered() const {
     91   if (!IsPresent())
     92     return false;
     93 
     94   BluetoothAdapterClient::Properties* properties =
     95       DBusThreadManager::Get()->GetBluetoothAdapterClient()->
     96           GetProperties(object_path_);
     97 
     98   return properties->powered.value();
     99 }
    100 
    101 void BluetoothAdapterChromeOS::SetPowered(
    102     bool powered,
    103     const base::Closure& callback,
    104     const ErrorCallback& error_callback) {
    105   DBusThreadManager::Get()->GetBluetoothAdapterClient()->
    106       GetProperties(object_path_)->powered.Set(
    107           powered,
    108           base::Bind(&BluetoothAdapterChromeOS::OnSetPowered,
    109                      weak_ptr_factory_.GetWeakPtr(),
    110                      callback,
    111                      error_callback));
    112 }
    113 
    114 bool BluetoothAdapterChromeOS::IsDiscovering() const {
    115   if (!IsPresent())
    116     return false;
    117 
    118   BluetoothAdapterClient::Properties* properties =
    119       DBusThreadManager::Get()->GetBluetoothAdapterClient()->
    120           GetProperties(object_path_);
    121 
    122   return properties->discovering.value();
    123 }
    124 
    125 void BluetoothAdapterChromeOS::StartDiscovering(
    126     const base::Closure& callback,
    127     const ErrorCallback& error_callback) {
    128   // BlueZ counts discovery sessions, and permits multiple sessions for a
    129   // single connection, so issue a StartDiscovery() call for every use
    130   // within Chromium for the right behavior.
    131   DBusThreadManager::Get()->GetBluetoothAdapterClient()->
    132       StartDiscovery(
    133           object_path_,
    134           base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery,
    135                      weak_ptr_factory_.GetWeakPtr(),
    136                      callback),
    137           base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError,
    138                      weak_ptr_factory_.GetWeakPtr(),
    139                      error_callback));
    140 }
    141 
    142 void BluetoothAdapterChromeOS::StopDiscovering(
    143     const base::Closure& callback,
    144     const ErrorCallback& error_callback) {
    145   // Inform BlueZ to stop one of our open discovery sessions.
    146   DBusThreadManager::Get()->GetBluetoothAdapterClient()->
    147       StopDiscovery(
    148           object_path_,
    149           base::Bind(&BluetoothAdapterChromeOS::OnStopDiscovery,
    150                      weak_ptr_factory_.GetWeakPtr(),
    151                      callback),
    152           base::Bind(&BluetoothAdapterChromeOS::OnStopDiscoveryError,
    153                      weak_ptr_factory_.GetWeakPtr(),
    154                      error_callback));
    155 }
    156 
    157 void BluetoothAdapterChromeOS::ReadLocalOutOfBandPairingData(
    158     const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback,
    159     const ErrorCallback& error_callback) {
    160   error_callback.Run();
    161 }
    162 
    163 void BluetoothAdapterChromeOS::AdapterAdded(
    164     const dbus::ObjectPath& object_path) {
    165   // Set the adapter to the newly added adapter only if no adapter is present.
    166   if (!IsPresent())
    167     SetAdapter(object_path);
    168 }
    169 
    170 void BluetoothAdapterChromeOS::AdapterRemoved(
    171     const dbus::ObjectPath& object_path) {
    172   if (object_path == object_path_)
    173     RemoveAdapter();
    174 }
    175 
    176 void BluetoothAdapterChromeOS::AdapterPropertyChanged(
    177     const dbus::ObjectPath& object_path,
    178     const std::string& property_name) {
    179   if (object_path != object_path_)
    180     return;
    181 
    182   BluetoothAdapterClient::Properties* properties =
    183       DBusThreadManager::Get()->GetBluetoothAdapterClient()->
    184           GetProperties(object_path_);
    185 
    186   if (property_name == properties->powered.name())
    187     PoweredChanged(properties->powered.value());
    188   else if (property_name == properties->discovering.name())
    189     DiscoveringChanged(properties->discovering.value());
    190 }
    191 
    192 void BluetoothAdapterChromeOS::DeviceAdded(
    193   const dbus::ObjectPath& object_path) {
    194   BluetoothDeviceClient::Properties* properties =
    195       DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    196           GetProperties(object_path);
    197   if (properties->adapter.value() != object_path_)
    198     return;
    199 
    200   BluetoothDeviceChromeOS* device_chromeos =
    201       new BluetoothDeviceChromeOS(this, object_path);
    202   DCHECK(devices_.find(device_chromeos->GetAddress()) == devices_.end());
    203 
    204   devices_[device_chromeos->GetAddress()] = device_chromeos;
    205 
    206   FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
    207                     DeviceAdded(this, device_chromeos));
    208 }
    209 
    210 void BluetoothAdapterChromeOS::DeviceRemoved(
    211     const dbus::ObjectPath& object_path) {
    212   for (DevicesMap::iterator iter = devices_.begin();
    213        iter != devices_.end(); ++iter) {
    214     BluetoothDeviceChromeOS* device_chromeos =
    215         static_cast<BluetoothDeviceChromeOS*>(iter->second);
    216     if (device_chromeos->object_path() == object_path) {
    217       devices_.erase(iter);
    218 
    219       FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
    220                         DeviceRemoved(this, device_chromeos));
    221       delete device_chromeos;
    222       return;
    223     }
    224   }
    225 }
    226 
    227 void BluetoothAdapterChromeOS::DevicePropertyChanged(
    228     const dbus::ObjectPath& object_path,
    229     const std::string& property_name) {
    230   BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path);
    231   if (!device_chromeos)
    232     return;
    233 
    234   BluetoothDeviceClient::Properties* properties =
    235       DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    236           GetProperties(object_path);
    237 
    238   if (property_name == properties->bluetooth_class.name() ||
    239       property_name == properties->address.name() ||
    240       property_name == properties->alias.name() ||
    241       property_name == properties->paired.name() ||
    242       property_name == properties->trusted.name() ||
    243       property_name == properties->connected.name() ||
    244       property_name == properties->uuids.name())
    245     NotifyDeviceChanged(device_chromeos);
    246 
    247   // UMA connection counting
    248   if (property_name == properties->connected.name()) {
    249     int count = 0;
    250 
    251     for (DevicesMap::iterator iter = devices_.begin();
    252          iter != devices_.end(); ++iter) {
    253       if (iter->second->IsPaired() && iter->second->IsConnected())
    254         ++count;
    255     }
    256 
    257     UMA_HISTOGRAM_COUNTS_100("Bluetooth.ConnectedDeviceCount", count);
    258   }
    259 }
    260 
    261 void BluetoothAdapterChromeOS::InputPropertyChanged(
    262     const dbus::ObjectPath& object_path,
    263     const std::string& property_name) {
    264   BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path);
    265   if (!device_chromeos)
    266     return;
    267 
    268   BluetoothInputClient::Properties* properties =
    269       DBusThreadManager::Get()->GetBluetoothInputClient()->
    270           GetProperties(object_path);
    271 
    272   // Properties structure can be removed, which triggers a change in the
    273   // BluetoothDevice::IsConnectable() property, as does a change in the
    274   // actual reconnect_mode property.
    275   if (!properties ||
    276       property_name == properties->reconnect_mode.name())
    277     NotifyDeviceChanged(device_chromeos);
    278 }
    279 
    280 BluetoothDeviceChromeOS*
    281 BluetoothAdapterChromeOS::GetDeviceWithPath(
    282     const dbus::ObjectPath& object_path) {
    283   for (DevicesMap::iterator iter = devices_.begin();
    284        iter != devices_.end(); ++iter) {
    285     BluetoothDeviceChromeOS* device_chromeos =
    286         static_cast<BluetoothDeviceChromeOS*>(iter->second);
    287     if (device_chromeos->object_path() == object_path)
    288       return device_chromeos;
    289   }
    290 
    291   return NULL;
    292 }
    293 
    294 void BluetoothAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) {
    295   DCHECK(!IsPresent());
    296   object_path_ = object_path;
    297 
    298   VLOG(1) << object_path_.value() << ": using adapter.";
    299 
    300   SetAdapterName();
    301 
    302   BluetoothAdapterClient::Properties* properties =
    303       DBusThreadManager::Get()->GetBluetoothAdapterClient()->
    304           GetProperties(object_path_);
    305 
    306   PresentChanged(true);
    307 
    308   if (properties->powered.value())
    309     PoweredChanged(true);
    310   if (properties->discovering.value())
    311     DiscoveringChanged(true);
    312 
    313   std::vector<dbus::ObjectPath> device_paths =
    314       DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    315           GetDevicesForAdapter(object_path_);
    316 
    317   for (std::vector<dbus::ObjectPath>::iterator iter = device_paths.begin();
    318        iter != device_paths.end(); ++iter) {
    319     BluetoothDeviceChromeOS* device_chromeos =
    320         new BluetoothDeviceChromeOS(this, *iter);
    321 
    322     devices_[device_chromeos->GetAddress()] = device_chromeos;
    323 
    324     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
    325                       DeviceAdded(this, device_chromeos));
    326   }
    327 }
    328 
    329 void BluetoothAdapterChromeOS::SetAdapterName() {
    330   std::string board = base::SysInfo::GetLsbReleaseBoard();
    331   std::string alias;
    332   if (board.substr(0, 6) == "stumpy") {
    333     alias = "Chromebox";
    334   } else if (board.substr(0, 4) == "link") {
    335     alias = "Chromebook Pixel";
    336   } else {
    337     alias = "Chromebook";
    338   }
    339 
    340   DBusThreadManager::Get()->GetBluetoothAdapterClient()->
    341       GetProperties(object_path_)->alias.Set(
    342           alias,
    343           base::Bind(&BluetoothAdapterChromeOS::OnSetAlias,
    344                      weak_ptr_factory_.GetWeakPtr()));
    345 }
    346 
    347 void BluetoothAdapterChromeOS::OnSetAlias(bool success) {
    348   LOG_IF(WARNING, !success) << object_path_.value()
    349                             << ": Failed to set adapter alias";
    350 }
    351 
    352 void BluetoothAdapterChromeOS::RemoveAdapter() {
    353   DCHECK(IsPresent());
    354   VLOG(1) << object_path_.value() << ": adapter removed.";
    355 
    356   BluetoothAdapterClient::Properties* properties =
    357       DBusThreadManager::Get()->GetBluetoothAdapterClient()->
    358           GetProperties(object_path_);
    359 
    360   object_path_ = dbus::ObjectPath("");
    361 
    362   if (properties->powered.value())
    363     PoweredChanged(false);
    364   if (properties->discovering.value())
    365     DiscoveringChanged(false);
    366 
    367   // Copy the devices list here and clear the original so that when we
    368   // send DeviceRemoved(), GetDevices() returns no devices.
    369   DevicesMap devices = devices_;
    370   devices_.clear();
    371 
    372   for (DevicesMap::iterator iter = devices.begin();
    373        iter != devices.end(); ++iter) {
    374     FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
    375                       DeviceRemoved(this, iter->second));
    376     delete iter->second;
    377   }
    378 
    379   PresentChanged(false);
    380 }
    381 
    382 void BluetoothAdapterChromeOS::PoweredChanged(bool powered) {
    383   FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
    384                     AdapterPoweredChanged(this, powered));
    385 }
    386 
    387 void BluetoothAdapterChromeOS::DiscoveringChanged(
    388     bool discovering) {
    389   FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
    390                     AdapterDiscoveringChanged(this, discovering));
    391 }
    392 
    393 void BluetoothAdapterChromeOS::PresentChanged(bool present) {
    394   FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
    395                     AdapterPresentChanged(this, present));
    396 }
    397 
    398 void BluetoothAdapterChromeOS::NotifyDeviceChanged(
    399     BluetoothDeviceChromeOS* device) {
    400   DCHECK(device->adapter_ == this);
    401 
    402   FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
    403                     DeviceChanged(this, device));
    404 }
    405 
    406 void BluetoothAdapterChromeOS::OnSetPowered(const base::Closure& callback,
    407                                             const ErrorCallback& error_callback,
    408                                             bool success) {
    409   if (success)
    410     callback.Run();
    411   else
    412     error_callback.Run();
    413 }
    414 
    415 void BluetoothAdapterChromeOS::OnStartDiscovery(const base::Closure& callback) {
    416   callback.Run();
    417 }
    418 
    419 void BluetoothAdapterChromeOS::OnStartDiscoveryError(
    420     const ErrorCallback& error_callback,
    421     const std::string& error_name,
    422     const std::string& error_message) {
    423   LOG(WARNING) << object_path_.value() << ": Failed to start discovery: "
    424                << error_name << ": " << error_message;
    425   error_callback.Run();
    426 }
    427 
    428 void BluetoothAdapterChromeOS::OnStopDiscovery(const base::Closure& callback) {
    429   callback.Run();
    430 }
    431 
    432 void BluetoothAdapterChromeOS::OnStopDiscoveryError(
    433     const ErrorCallback& error_callback,
    434     const std::string& error_name,
    435     const std::string& error_message) {
    436   LOG(WARNING) << object_path_.value() << ": Failed to stop discovery: "
    437                << error_name << ": " << error_message;
    438   error_callback.Run();
    439 }
    440 
    441 }  // namespace chromeos
    442