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_device_chromeos.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/metrics/histogram.h"
      9 #include "base/strings/string_number_conversions.h"
     10 #include "base/strings/string_util.h"
     11 #include "chromeos/dbus/bluetooth_adapter_client.h"
     12 #include "chromeos/dbus/bluetooth_agent_manager_client.h"
     13 #include "chromeos/dbus/bluetooth_agent_service_provider.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 "dbus/bus.h"
     18 #include "device/bluetooth/bluetooth_adapter_chromeos.h"
     19 #include "device/bluetooth/bluetooth_profile_chromeos.h"
     20 #include "device/bluetooth/bluetooth_socket.h"
     21 #include "third_party/cros_system_api/dbus/service_constants.h"
     22 
     23 using device::BluetoothDevice;
     24 
     25 namespace {
     26 
     27 // The agent path is relatively meaningless since BlueZ only supports one
     28 // at time and will fail in an attempt to register another with "Already Exists"
     29 // (which we fail in OnRegisterAgentError with ERROR_INPROGRESS).
     30 const char kAgentPath[] = "/org/chromium/bluetooth_agent";
     31 
     32 // Histogram enumerations for pairing methods.
     33 enum UMAPairingMethod {
     34   UMA_PAIRING_METHOD_NONE,
     35   UMA_PAIRING_METHOD_REQUEST_PINCODE,
     36   UMA_PAIRING_METHOD_REQUEST_PASSKEY,
     37   UMA_PAIRING_METHOD_DISPLAY_PINCODE,
     38   UMA_PAIRING_METHOD_DISPLAY_PASSKEY,
     39   UMA_PAIRING_METHOD_CONFIRM_PASSKEY,
     40   // NOTE: Add new pairing methods immediately above this line. Make sure to
     41   // update the enum list in tools/histogram/histograms.xml accordinly.
     42   UMA_PAIRING_METHOD_COUNT
     43 };
     44 
     45 // Histogram enumerations for pairing results.
     46 enum UMAPairingResult {
     47   UMA_PAIRING_RESULT_SUCCESS,
     48   UMA_PAIRING_RESULT_INPROGRESS,
     49   UMA_PAIRING_RESULT_FAILED,
     50   UMA_PAIRING_RESULT_AUTH_FAILED,
     51   UMA_PAIRING_RESULT_AUTH_CANCELED,
     52   UMA_PAIRING_RESULT_AUTH_REJECTED,
     53   UMA_PAIRING_RESULT_AUTH_TIMEOUT,
     54   UMA_PAIRING_RESULT_UNSUPPORTED_DEVICE,
     55   UMA_PAIRING_RESULT_UNKNOWN_ERROR,
     56   // NOTE: Add new pairing results immediately above this line. Make sure to
     57   // update the enum list in tools/histogram/histograms.xml accordinly.
     58   UMA_PAIRING_RESULT_COUNT
     59 };
     60 
     61 void ParseModalias(const dbus::ObjectPath& object_path,
     62                    uint16 *vendor_id,
     63                    uint16 *product_id,
     64                    uint16 *device_id) {
     65   chromeos::BluetoothDeviceClient::Properties* properties =
     66       chromeos::DBusThreadManager::Get()->GetBluetoothDeviceClient()->
     67           GetProperties(object_path);
     68   DCHECK(properties);
     69 
     70   std::string modalias = properties->modalias.value();
     71   if (StartsWithASCII(modalias, "usb:", false) && modalias.length() == 19) {
     72     // usb:vXXXXpXXXXdXXXX
     73     if (modalias[4] == 'v' && vendor_id != NULL) {
     74       uint64 component = 0;
     75       base::HexStringToUInt64(modalias.substr(5, 4), &component);
     76       *vendor_id = component;
     77     }
     78 
     79     if (modalias[9] == 'p' && product_id != NULL) {
     80       uint64 component = 0;
     81       base::HexStringToUInt64(modalias.substr(10, 4), &component);
     82       *product_id = component;
     83     }
     84 
     85     if (modalias[14] == 'd' && device_id != NULL) {
     86       uint64 component = 0;
     87       base::HexStringToUInt64(modalias.substr(15, 4), &component);
     88       *device_id = component;
     89     }
     90   }
     91 }
     92 
     93 void RecordPairingResult(BluetoothDevice::ConnectErrorCode error_code) {
     94   UMAPairingResult pairing_result;
     95   switch (error_code) {
     96     case BluetoothDevice::ERROR_INPROGRESS:
     97       pairing_result = UMA_PAIRING_RESULT_INPROGRESS;
     98       break;
     99     case BluetoothDevice::ERROR_FAILED:
    100       pairing_result = UMA_PAIRING_RESULT_FAILED;
    101       break;
    102     case BluetoothDevice::ERROR_AUTH_FAILED:
    103       pairing_result = UMA_PAIRING_RESULT_AUTH_FAILED;
    104       break;
    105     case BluetoothDevice::ERROR_AUTH_CANCELED:
    106       pairing_result = UMA_PAIRING_RESULT_AUTH_CANCELED;
    107       break;
    108     case BluetoothDevice::ERROR_AUTH_REJECTED:
    109       pairing_result = UMA_PAIRING_RESULT_AUTH_REJECTED;
    110       break;
    111     case BluetoothDevice::ERROR_AUTH_TIMEOUT:
    112       pairing_result = UMA_PAIRING_RESULT_AUTH_TIMEOUT;
    113       break;
    114     case BluetoothDevice::ERROR_UNSUPPORTED_DEVICE:
    115       pairing_result = UMA_PAIRING_RESULT_UNSUPPORTED_DEVICE;
    116       break;
    117     default:
    118       pairing_result = UMA_PAIRING_RESULT_UNKNOWN_ERROR;
    119   }
    120 
    121   UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingResult",
    122                             pairing_result,
    123                             UMA_PAIRING_RESULT_COUNT);
    124 }
    125 
    126 }  // namespace
    127 
    128 namespace chromeos {
    129 
    130 BluetoothDeviceChromeOS::BluetoothDeviceChromeOS(
    131     BluetoothAdapterChromeOS* adapter,
    132     const dbus::ObjectPath& object_path)
    133     : adapter_(adapter),
    134       object_path_(object_path),
    135       num_connecting_calls_(0),
    136       pairing_delegate_(NULL),
    137       pairing_delegate_used_(false),
    138       weak_ptr_factory_(this) {
    139 }
    140 
    141 BluetoothDeviceChromeOS::~BluetoothDeviceChromeOS() {
    142 }
    143 
    144 uint32 BluetoothDeviceChromeOS::GetBluetoothClass() const {
    145   BluetoothDeviceClient::Properties* properties =
    146       DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    147           GetProperties(object_path_);
    148   DCHECK(properties);
    149 
    150   return properties->bluetooth_class.value();
    151 }
    152 
    153 std::string BluetoothDeviceChromeOS::GetDeviceName() const {
    154   BluetoothDeviceClient::Properties* properties =
    155       DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    156           GetProperties(object_path_);
    157   DCHECK(properties);
    158 
    159   return properties->alias.value();
    160 }
    161 
    162 std::string BluetoothDeviceChromeOS::GetAddress() const {
    163   BluetoothDeviceClient::Properties* properties =
    164       DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    165           GetProperties(object_path_);
    166   DCHECK(properties);
    167 
    168   return properties->address.value();
    169 }
    170 
    171 uint16 BluetoothDeviceChromeOS::GetVendorID() const {
    172   uint16 vendor_id  = 0;
    173   ParseModalias(object_path_, &vendor_id, NULL, NULL);
    174   return vendor_id;
    175 }
    176 
    177 uint16 BluetoothDeviceChromeOS::GetProductID() const {
    178   uint16 product_id  = 0;
    179   ParseModalias(object_path_, NULL, &product_id, NULL);
    180   return product_id;
    181 }
    182 
    183 uint16 BluetoothDeviceChromeOS::GetDeviceID() const {
    184   uint16 device_id  = 0;
    185   ParseModalias(object_path_, NULL, NULL, &device_id);
    186   return device_id;
    187 }
    188 
    189 bool BluetoothDeviceChromeOS::IsPaired() const {
    190   BluetoothDeviceClient::Properties* properties =
    191       DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    192           GetProperties(object_path_);
    193   DCHECK(properties);
    194 
    195   // Trusted devices are devices that don't support pairing but that the
    196   // user has explicitly connected; it makes no sense for UI purposes to
    197   // treat them differently from each other.
    198   return properties->paired.value() || properties->trusted.value();
    199 }
    200 
    201 bool BluetoothDeviceChromeOS::IsConnected() const {
    202   BluetoothDeviceClient::Properties* properties =
    203       DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    204           GetProperties(object_path_);
    205   DCHECK(properties);
    206 
    207   return properties->connected.value();
    208 }
    209 
    210 bool BluetoothDeviceChromeOS::IsConnectable() const {
    211   BluetoothInputClient::Properties* input_properties =
    212       DBusThreadManager::Get()->GetBluetoothInputClient()->
    213           GetProperties(object_path_);
    214   // GetProperties returns NULL when the device does not implement the given
    215   // interface. Non HID devices are normally connectable.
    216   if (!input_properties)
    217     return true;
    218 
    219   return input_properties->reconnect_mode.value() != "device";
    220 }
    221 
    222 bool BluetoothDeviceChromeOS::IsConnecting() const {
    223   return num_connecting_calls_ > 0;
    224 }
    225 
    226 BluetoothDeviceChromeOS::ServiceList BluetoothDeviceChromeOS::GetServices()
    227     const {
    228   BluetoothDeviceClient::Properties* properties =
    229       DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    230           GetProperties(object_path_);
    231   DCHECK(properties);
    232 
    233   return properties->uuids.value();
    234 }
    235 
    236 void BluetoothDeviceChromeOS::GetServiceRecords(
    237     const ServiceRecordsCallback& callback,
    238     const ErrorCallback& error_callback) {
    239   // TODO(keybuk): not implemented; remove
    240   error_callback.Run();
    241 }
    242 
    243 void BluetoothDeviceChromeOS::ProvidesServiceWithName(
    244     const std::string& name,
    245     const ProvidesServiceCallback& callback) {
    246   // TODO(keybuk): not implemented; remove
    247   callback.Run(false);
    248 }
    249 
    250 bool BluetoothDeviceChromeOS::ExpectingPinCode() const {
    251   return !pincode_callback_.is_null();
    252 }
    253 
    254 bool BluetoothDeviceChromeOS::ExpectingPasskey() const {
    255   return !passkey_callback_.is_null();
    256 }
    257 
    258 bool BluetoothDeviceChromeOS::ExpectingConfirmation() const {
    259   return !confirmation_callback_.is_null();
    260 }
    261 
    262 void BluetoothDeviceChromeOS::Connect(
    263     BluetoothDevice::PairingDelegate* pairing_delegate,
    264     const base::Closure& callback,
    265     const ConnectErrorCallback& error_callback) {
    266   if (num_connecting_calls_++ == 0)
    267     adapter_->NotifyDeviceChanged(this);
    268 
    269   VLOG(1) << object_path_.value() << ": Connecting, " << num_connecting_calls_
    270           << " in progress";
    271 
    272   if (IsPaired() || !pairing_delegate || !IsPairable()) {
    273     // No need to pair, or unable to, skip straight to connection.
    274     ConnectInternal(false, callback, error_callback);
    275   } else {
    276     // Initiate high-security connection with pairing.
    277     DCHECK(!pairing_delegate_);
    278     DCHECK(agent_.get() == NULL);
    279 
    280     pairing_delegate_ = pairing_delegate;
    281     pairing_delegate_used_ = false;
    282 
    283     // The agent path is relatively meaningless since BlueZ only supports
    284     // one per application at a time.
    285     dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
    286     agent_.reset(BluetoothAgentServiceProvider::Create(
    287         system_bus, dbus::ObjectPath(kAgentPath), this));
    288     DCHECK(agent_.get());
    289 
    290     VLOG(1) << object_path_.value() << ": Registering agent for pairing";
    291     DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
    292         RegisterAgent(
    293             dbus::ObjectPath(kAgentPath),
    294             bluetooth_agent_manager::kKeyboardDisplayCapability,
    295             base::Bind(&BluetoothDeviceChromeOS::OnRegisterAgent,
    296                        weak_ptr_factory_.GetWeakPtr(),
    297                        callback,
    298                        error_callback),
    299             base::Bind(&BluetoothDeviceChromeOS::OnRegisterAgentError,
    300                        weak_ptr_factory_.GetWeakPtr(),
    301                        error_callback));
    302   }
    303 }
    304 
    305 void BluetoothDeviceChromeOS::SetPinCode(const std::string& pincode) {
    306   if (!agent_.get() || pincode_callback_.is_null())
    307     return;
    308 
    309   pincode_callback_.Run(SUCCESS, pincode);
    310   pincode_callback_.Reset();
    311 }
    312 
    313 void BluetoothDeviceChromeOS::SetPasskey(uint32 passkey) {
    314   if (!agent_.get() || passkey_callback_.is_null())
    315     return;
    316 
    317   passkey_callback_.Run(SUCCESS, passkey);
    318   passkey_callback_.Reset();
    319 }
    320 
    321 void BluetoothDeviceChromeOS::ConfirmPairing() {
    322   if (!agent_.get() || confirmation_callback_.is_null())
    323     return;
    324 
    325   confirmation_callback_.Run(SUCCESS);
    326   confirmation_callback_.Reset();
    327 }
    328 
    329 void BluetoothDeviceChromeOS::RejectPairing() {
    330   RunPairingCallbacks(REJECTED);
    331 }
    332 
    333 void BluetoothDeviceChromeOS::CancelPairing() {
    334   // If there wasn't a callback in progress that we can reply to then we
    335   // have to send a CancelPairing() to the device instead.
    336   if (!RunPairingCallbacks(CANCELLED)) {
    337     DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    338         CancelPairing(
    339             object_path_,
    340             base::Bind(&base::DoNothing),
    341             base::Bind(&BluetoothDeviceChromeOS::OnCancelPairingError,
    342                        weak_ptr_factory_.GetWeakPtr()));
    343 
    344     // Since there's no calback to this method, it's possible that the pairing
    345     // delegate is going to be freed before things complete.
    346     UnregisterAgent();
    347   }
    348 }
    349 
    350 void BluetoothDeviceChromeOS::Disconnect(const base::Closure& callback,
    351                                          const ErrorCallback& error_callback) {
    352   VLOG(1) << object_path_.value() << ": Disconnecting";
    353   DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    354       Disconnect(
    355           object_path_,
    356           base::Bind(&BluetoothDeviceChromeOS::OnDisconnect,
    357                      weak_ptr_factory_.GetWeakPtr(),
    358                      callback),
    359           base::Bind(&BluetoothDeviceChromeOS::OnDisconnectError,
    360                      weak_ptr_factory_.GetWeakPtr(),
    361                      error_callback));
    362 }
    363 
    364 void BluetoothDeviceChromeOS::Forget(const ErrorCallback& error_callback) {
    365   VLOG(1) << object_path_.value() << ": Removing device";
    366   DBusThreadManager::Get()->GetBluetoothAdapterClient()->
    367       RemoveDevice(
    368           adapter_->object_path_,
    369           object_path_,
    370           base::Bind(&base::DoNothing),
    371           base::Bind(&BluetoothDeviceChromeOS::OnForgetError,
    372                      weak_ptr_factory_.GetWeakPtr(),
    373                      error_callback));
    374 }
    375 
    376 void BluetoothDeviceChromeOS::ConnectToService(
    377     const std::string& service_uuid,
    378     const SocketCallback& callback) {
    379   // TODO(keybuk): implement
    380   callback.Run(scoped_refptr<device::BluetoothSocket>());
    381 }
    382 
    383 void BluetoothDeviceChromeOS::ConnectToProfile(
    384     device::BluetoothProfile* profile,
    385     const base::Closure& callback,
    386     const ErrorCallback& error_callback) {
    387   BluetoothProfileChromeOS* profile_chromeos =
    388       static_cast<BluetoothProfileChromeOS*>(profile);
    389   VLOG(1) << object_path_.value() << ": Connecting profile: "
    390           << profile_chromeos->uuid();
    391   DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    392       ConnectProfile(
    393           object_path_,
    394           profile_chromeos->uuid(),
    395           base::Bind(
    396               &BluetoothDeviceChromeOS::OnConnectProfile,
    397               weak_ptr_factory_.GetWeakPtr(),
    398               profile,
    399               callback),
    400           base::Bind(
    401               &BluetoothDeviceChromeOS::OnConnectProfileError,
    402               weak_ptr_factory_.GetWeakPtr(),
    403               profile,
    404               error_callback));
    405 }
    406 
    407 void BluetoothDeviceChromeOS::SetOutOfBandPairingData(
    408     const device::BluetoothOutOfBandPairingData& data,
    409     const base::Closure& callback,
    410     const ErrorCallback& error_callback) {
    411   // TODO(keybuk): implement
    412   error_callback.Run();
    413 }
    414 
    415 void BluetoothDeviceChromeOS::ClearOutOfBandPairingData(
    416     const base::Closure& callback,
    417     const ErrorCallback& error_callback) {
    418   // TODO(keybuk): implement
    419   error_callback.Run();
    420 }
    421 
    422 
    423 void BluetoothDeviceChromeOS::Release() {
    424   DCHECK(agent_.get());
    425   DCHECK(pairing_delegate_);
    426   VLOG(1) << object_path_.value() << ": Release";
    427 
    428   pincode_callback_.Reset();
    429   passkey_callback_.Reset();
    430   confirmation_callback_.Reset();
    431 
    432   UnregisterAgent();
    433 }
    434 
    435 void BluetoothDeviceChromeOS::RequestPinCode(
    436     const dbus::ObjectPath& device_path,
    437     const PinCodeCallback& callback) {
    438   DCHECK(agent_.get());
    439   DCHECK(device_path == object_path_);
    440   VLOG(1) << object_path_.value() << ": RequestPinCode";
    441 
    442   UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
    443                             UMA_PAIRING_METHOD_REQUEST_PINCODE,
    444                             UMA_PAIRING_METHOD_COUNT);
    445 
    446   DCHECK(pairing_delegate_);
    447   DCHECK(pincode_callback_.is_null());
    448   pincode_callback_ = callback;
    449   pairing_delegate_->RequestPinCode(this);
    450   pairing_delegate_used_ = true;
    451 }
    452 
    453 void BluetoothDeviceChromeOS::DisplayPinCode(
    454     const dbus::ObjectPath& device_path,
    455     const std::string& pincode) {
    456   DCHECK(agent_.get());
    457   DCHECK(device_path == object_path_);
    458   VLOG(1) << object_path_.value() << ": DisplayPinCode: " << pincode;
    459 
    460   UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
    461                             UMA_PAIRING_METHOD_DISPLAY_PINCODE,
    462                             UMA_PAIRING_METHOD_COUNT);
    463 
    464   DCHECK(pairing_delegate_);
    465   pairing_delegate_->DisplayPinCode(this, pincode);
    466   pairing_delegate_used_ = true;
    467 }
    468 
    469 void BluetoothDeviceChromeOS::RequestPasskey(
    470     const dbus::ObjectPath& device_path,
    471     const PasskeyCallback& callback) {
    472   DCHECK(agent_.get());
    473   DCHECK(device_path == object_path_);
    474   VLOG(1) << object_path_.value() << ": RequestPasskey";
    475 
    476   UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
    477                             UMA_PAIRING_METHOD_REQUEST_PASSKEY,
    478                             UMA_PAIRING_METHOD_COUNT);
    479 
    480   DCHECK(pairing_delegate_);
    481   DCHECK(passkey_callback_.is_null());
    482   passkey_callback_ = callback;
    483   pairing_delegate_->RequestPasskey(this);
    484   pairing_delegate_used_ = true;
    485 }
    486 
    487 void BluetoothDeviceChromeOS::DisplayPasskey(
    488     const dbus::ObjectPath& device_path,
    489     uint32 passkey,
    490     uint16 entered) {
    491   DCHECK(agent_.get());
    492   DCHECK(device_path == object_path_);
    493   VLOG(1) << object_path_.value() << ": DisplayPasskey: " << passkey
    494           << " (" << entered << " entered)";
    495 
    496   if (entered == 0)
    497     UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
    498                               UMA_PAIRING_METHOD_DISPLAY_PASSKEY,
    499                               UMA_PAIRING_METHOD_COUNT);
    500 
    501   DCHECK(pairing_delegate_);
    502   if (entered == 0)
    503     pairing_delegate_->DisplayPasskey(this, passkey);
    504   pairing_delegate_->KeysEntered(this, entered);
    505   pairing_delegate_used_ = true;
    506 }
    507 
    508 void BluetoothDeviceChromeOS::RequestConfirmation(
    509     const dbus::ObjectPath& device_path,
    510     uint32 passkey,
    511     const ConfirmationCallback& callback) {
    512   DCHECK(agent_.get());
    513   DCHECK(device_path == object_path_);
    514   VLOG(1) << object_path_.value() << ": RequestConfirmation: " << passkey;
    515 
    516   UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
    517                             UMA_PAIRING_METHOD_CONFIRM_PASSKEY,
    518                             UMA_PAIRING_METHOD_COUNT);
    519 
    520   DCHECK(pairing_delegate_);
    521   DCHECK(confirmation_callback_.is_null());
    522   confirmation_callback_ = callback;
    523   pairing_delegate_->ConfirmPasskey(this, passkey);
    524   pairing_delegate_used_ = true;
    525 }
    526 
    527 void BluetoothDeviceChromeOS::RequestAuthorization(
    528     const dbus::ObjectPath& device_path,
    529     const ConfirmationCallback& callback) {
    530   // TODO(keybuk): implement
    531   callback.Run(CANCELLED);
    532 }
    533 
    534 void BluetoothDeviceChromeOS::AuthorizeService(
    535     const dbus::ObjectPath& device_path,
    536     const std::string& uuid,
    537     const ConfirmationCallback& callback) {
    538   // TODO(keybuk): implement
    539   callback.Run(CANCELLED);
    540 }
    541 
    542 void BluetoothDeviceChromeOS::Cancel() {
    543   DCHECK(agent_.get());
    544   VLOG(1) << object_path_.value() << ": Cancel";
    545 
    546   DCHECK(pairing_delegate_);
    547   pairing_delegate_->DismissDisplayOrConfirm();
    548 }
    549 
    550 void BluetoothDeviceChromeOS::ConnectInternal(
    551     bool after_pairing,
    552     const base::Closure& callback,
    553     const ConnectErrorCallback& error_callback) {
    554   VLOG(1) << object_path_.value() << ": Connecting";
    555   DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    556       Connect(
    557           object_path_,
    558           base::Bind(&BluetoothDeviceChromeOS::OnConnect,
    559                      weak_ptr_factory_.GetWeakPtr(),
    560                      after_pairing,
    561                      callback),
    562           base::Bind(&BluetoothDeviceChromeOS::OnConnectError,
    563                      weak_ptr_factory_.GetWeakPtr(),
    564                      after_pairing,
    565                      error_callback));
    566 }
    567 
    568 void BluetoothDeviceChromeOS::OnConnect(bool after_pairing,
    569                                         const base::Closure& callback) {
    570   if (--num_connecting_calls_ == 0)
    571     adapter_->NotifyDeviceChanged(this);
    572 
    573   DCHECK(num_connecting_calls_ >= 0);
    574   VLOG(1) << object_path_.value() << ": Connected, " << num_connecting_calls_
    575         << " still in progress";
    576 
    577   SetTrusted();
    578 
    579   if (after_pairing)
    580     UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingResult",
    581                               UMA_PAIRING_RESULT_SUCCESS,
    582                               UMA_PAIRING_RESULT_COUNT);
    583 
    584   callback.Run();
    585 }
    586 
    587 void BluetoothDeviceChromeOS::OnConnectError(
    588     bool after_pairing,
    589     const ConnectErrorCallback& error_callback,
    590     const std::string& error_name,
    591     const std::string& error_message) {
    592   if (--num_connecting_calls_ == 0)
    593     adapter_->NotifyDeviceChanged(this);
    594 
    595   DCHECK(num_connecting_calls_ >= 0);
    596   LOG(WARNING) << object_path_.value() << ": Failed to connect device: "
    597                << error_name << ": " << error_message;
    598   VLOG(1) << object_path_.value() << ": " << num_connecting_calls_
    599           << " still in progress";
    600 
    601   // Determine the error code from error_name.
    602   ConnectErrorCode error_code = ERROR_UNKNOWN;
    603   if (error_name == bluetooth_device::kErrorFailed) {
    604     error_code = ERROR_FAILED;
    605   } else if (error_name == bluetooth_device::kErrorInProgress) {
    606     error_code = ERROR_INPROGRESS;
    607   } else if (error_name == bluetooth_device::kErrorNotSupported) {
    608     error_code = ERROR_UNSUPPORTED_DEVICE;
    609   }
    610 
    611   if (after_pairing)
    612     RecordPairingResult(error_code);
    613   error_callback.Run(error_code);
    614 }
    615 
    616 void BluetoothDeviceChromeOS::OnRegisterAgent(
    617     const base::Closure& callback,
    618     const ConnectErrorCallback& error_callback) {
    619   VLOG(1) << object_path_.value() << ": Agent registered, now pairing";
    620 
    621   DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    622       Pair(object_path_,
    623            base::Bind(&BluetoothDeviceChromeOS::OnPair,
    624                       weak_ptr_factory_.GetWeakPtr(),
    625                       callback, error_callback),
    626            base::Bind(&BluetoothDeviceChromeOS::OnPairError,
    627                       weak_ptr_factory_.GetWeakPtr(),
    628                       error_callback));
    629 }
    630 
    631 void BluetoothDeviceChromeOS::OnRegisterAgentError(
    632     const ConnectErrorCallback& error_callback,
    633     const std::string& error_name,
    634     const std::string& error_message) {
    635   if (--num_connecting_calls_ == 0)
    636     adapter_->NotifyDeviceChanged(this);
    637 
    638   DCHECK(num_connecting_calls_ >= 0);
    639   LOG(WARNING) << object_path_.value() << ": Failed to register agent: "
    640                << error_name << ": " << error_message;
    641   VLOG(1) << object_path_.value() << ": " << num_connecting_calls_
    642           << " still in progress";
    643 
    644   UnregisterAgent();
    645 
    646   // Determine the error code from error_name.
    647   ConnectErrorCode error_code = ERROR_UNKNOWN;
    648   if (error_name == bluetooth_agent_manager::kErrorAlreadyExists)
    649     error_code = ERROR_INPROGRESS;
    650 
    651   RecordPairingResult(error_code);
    652   error_callback.Run(error_code);
    653 }
    654 
    655 void BluetoothDeviceChromeOS::OnPair(
    656     const base::Closure& callback,
    657     const ConnectErrorCallback& error_callback) {
    658   VLOG(1) << object_path_.value() << ": Paired";
    659 
    660   if (!pairing_delegate_used_)
    661     UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingMethod",
    662                               UMA_PAIRING_METHOD_NONE,
    663                               UMA_PAIRING_METHOD_COUNT);
    664   UnregisterAgent();
    665   SetTrusted();
    666   ConnectInternal(true, callback, error_callback);
    667 }
    668 
    669 void BluetoothDeviceChromeOS::OnPairError(
    670     const ConnectErrorCallback& error_callback,
    671     const std::string& error_name,
    672     const std::string& error_message) {
    673   if (--num_connecting_calls_ == 0)
    674     adapter_->NotifyDeviceChanged(this);
    675 
    676   DCHECK(num_connecting_calls_ >= 0);
    677   LOG(WARNING) << object_path_.value() << ": Failed to pair device: "
    678                << error_name << ": " << error_message;
    679   VLOG(1) << object_path_.value() << ": " << num_connecting_calls_
    680           << " still in progress";
    681 
    682   UnregisterAgent();
    683 
    684   // Determine the error code from error_name.
    685   ConnectErrorCode error_code = ERROR_UNKNOWN;
    686   if (error_name == bluetooth_device::kErrorConnectionAttemptFailed) {
    687     error_code = ERROR_FAILED;
    688   } else if (error_name == bluetooth_device::kErrorFailed) {
    689     error_code = ERROR_FAILED;
    690   } else if (error_name == bluetooth_device::kErrorAuthenticationFailed) {
    691     error_code = ERROR_AUTH_FAILED;
    692   } else if (error_name == bluetooth_device::kErrorAuthenticationCanceled) {
    693     error_code = ERROR_AUTH_CANCELED;
    694   } else if (error_name == bluetooth_device::kErrorAuthenticationRejected) {
    695     error_code = ERROR_AUTH_REJECTED;
    696   } else if (error_name == bluetooth_device::kErrorAuthenticationTimeout) {
    697     error_code = ERROR_AUTH_TIMEOUT;
    698   }
    699 
    700   RecordPairingResult(error_code);
    701   error_callback.Run(error_code);
    702 }
    703 
    704 void BluetoothDeviceChromeOS::OnCancelPairingError(
    705     const std::string& error_name,
    706     const std::string& error_message) {
    707   LOG(WARNING) << object_path_.value() << ": Failed to cancel pairing: "
    708                << error_name << ": " << error_message;
    709 }
    710 
    711 void BluetoothDeviceChromeOS::SetTrusted() {
    712   // Unconditionally send the property change, rather than checking the value
    713   // first; there's no harm in doing this and it solves any race conditions
    714   // with the property becoming true or false and this call happening before
    715   // we get the D-Bus signal about the earlier change.
    716   DBusThreadManager::Get()->GetBluetoothDeviceClient()->
    717       GetProperties(object_path_)->trusted.Set(
    718           true,
    719           base::Bind(&BluetoothDeviceChromeOS::OnSetTrusted,
    720                      weak_ptr_factory_.GetWeakPtr()));
    721 }
    722 
    723 void BluetoothDeviceChromeOS::OnSetTrusted(bool success) {
    724   LOG_IF(WARNING, !success) << object_path_.value()
    725                             << ": Failed to set device as trusted";
    726 }
    727 
    728 void BluetoothDeviceChromeOS::UnregisterAgent() {
    729   if (!agent_.get())
    730     return;
    731 
    732   DCHECK(pairing_delegate_);
    733 
    734   DCHECK(pincode_callback_.is_null());
    735   DCHECK(passkey_callback_.is_null());
    736   DCHECK(confirmation_callback_.is_null());
    737 
    738   pairing_delegate_->DismissDisplayOrConfirm();
    739   pairing_delegate_ = NULL;
    740 
    741   agent_.reset();
    742 
    743   // Clean up after ourselves.
    744   VLOG(1) << object_path_.value() << ": Unregistering pairing agent";
    745   DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
    746       UnregisterAgent(
    747           dbus::ObjectPath(kAgentPath),
    748           base::Bind(&base::DoNothing),
    749           base::Bind(&BluetoothDeviceChromeOS::OnUnregisterAgentError,
    750                      weak_ptr_factory_.GetWeakPtr()));
    751 }
    752 
    753 void BluetoothDeviceChromeOS::OnUnregisterAgentError(
    754     const std::string& error_name,
    755     const std::string& error_message) {
    756   LOG(WARNING) << object_path_.value() << ": Failed to unregister agent: "
    757                << error_name << ": " << error_message;
    758 }
    759 
    760 void BluetoothDeviceChromeOS::OnDisconnect(const base::Closure& callback) {
    761   VLOG(1) << object_path_.value() << ": Disconnected";
    762   callback.Run();
    763 }
    764 
    765 void BluetoothDeviceChromeOS::OnDisconnectError(
    766     const ErrorCallback& error_callback,
    767     const std::string& error_name,
    768     const std::string& error_message) {
    769   LOG(WARNING) << object_path_.value() << ": Failed to disconnect device: "
    770                << error_name << ": " << error_message;
    771   error_callback.Run();
    772 }
    773 
    774 void BluetoothDeviceChromeOS::OnForgetError(
    775     const ErrorCallback& error_callback,
    776     const std::string& error_name,
    777     const std::string& error_message) {
    778   LOG(WARNING) << object_path_.value() << ": Failed to remove device: "
    779                << error_name << ": " << error_message;
    780   error_callback.Run();
    781 }
    782 
    783 bool BluetoothDeviceChromeOS::RunPairingCallbacks(Status status) {
    784   if (!agent_.get())
    785     return false;
    786 
    787   bool callback_run = false;
    788   if (!pincode_callback_.is_null()) {
    789     pincode_callback_.Run(status, "");
    790     pincode_callback_.Reset();
    791     callback_run = true;
    792   }
    793 
    794   if (!passkey_callback_.is_null()) {
    795     passkey_callback_.Run(status, 0);
    796     passkey_callback_.Reset();
    797     callback_run = true;
    798   }
    799 
    800   if (!confirmation_callback_.is_null()) {
    801     confirmation_callback_.Run(status);
    802     confirmation_callback_.Reset();
    803     callback_run = true;
    804   }
    805 
    806   return callback_run;
    807 }
    808 
    809 void BluetoothDeviceChromeOS::OnConnectProfile(
    810     device::BluetoothProfile* profile,
    811     const base::Closure& callback) {
    812   BluetoothProfileChromeOS* profile_chromeos =
    813       static_cast<BluetoothProfileChromeOS*>(profile);
    814   VLOG(1) << object_path_.value() << ": Profile connected: "
    815           << profile_chromeos->uuid();
    816   callback.Run();
    817 }
    818 
    819 void BluetoothDeviceChromeOS::OnConnectProfileError(
    820     device::BluetoothProfile* profile,
    821     const ErrorCallback& error_callback,
    822     const std::string& error_name,
    823     const std::string& error_message) {
    824   BluetoothProfileChromeOS* profile_chromeos =
    825       static_cast<BluetoothProfileChromeOS*>(profile);
    826   VLOG(1) << object_path_.value() << ": Profile connection failed: "
    827           << profile_chromeos->uuid() << ": "
    828           << error_name << ": " << error_message;
    829   error_callback.Run();
    830 }
    831 
    832 }  // namespace chromeos
    833