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