bluetooth_device_chromeos.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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 <stdio.h>
8
9#include "base/bind.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/metrics/histogram.h"
12#include "base/strings/string_number_conversions.h"
13#include "base/strings/string_util.h"
14#include "chromeos/dbus/bluetooth_adapter_client.h"
15#include "chromeos/dbus/bluetooth_device_client.h"
16#include "chromeos/dbus/bluetooth_gatt_service_client.h"
17#include "chromeos/dbus/bluetooth_input_client.h"
18#include "chromeos/dbus/dbus_thread_manager.h"
19#include "dbus/bus.h"
20#include "device/bluetooth/bluetooth_adapter_chromeos.h"
21#include "device/bluetooth/bluetooth_pairing_chromeos.h"
22#include "device/bluetooth/bluetooth_remote_gatt_service_chromeos.h"
23#include "device/bluetooth/bluetooth_socket.h"
24#include "device/bluetooth/bluetooth_socket_chromeos.h"
25#include "device/bluetooth/bluetooth_socket_thread.h"
26#include "device/bluetooth/bluetooth_uuid.h"
27#include "third_party/cros_system_api/dbus/service_constants.h"
28
29using device::BluetoothDevice;
30using device::BluetoothSocket;
31using device::BluetoothUUID;
32
33namespace {
34
35// Histogram enumerations for pairing results.
36enum UMAPairingResult {
37  UMA_PAIRING_RESULT_SUCCESS,
38  UMA_PAIRING_RESULT_INPROGRESS,
39  UMA_PAIRING_RESULT_FAILED,
40  UMA_PAIRING_RESULT_AUTH_FAILED,
41  UMA_PAIRING_RESULT_AUTH_CANCELED,
42  UMA_PAIRING_RESULT_AUTH_REJECTED,
43  UMA_PAIRING_RESULT_AUTH_TIMEOUT,
44  UMA_PAIRING_RESULT_UNSUPPORTED_DEVICE,
45  UMA_PAIRING_RESULT_UNKNOWN_ERROR,
46  // NOTE: Add new pairing results immediately above this line. Make sure to
47  // update the enum list in tools/histogram/histograms.xml accordinly.
48  UMA_PAIRING_RESULT_COUNT
49};
50
51void ParseModalias(const dbus::ObjectPath& object_path,
52                   BluetoothDevice::VendorIDSource* vendor_id_source,
53                   uint16* vendor_id,
54                   uint16* product_id,
55                   uint16* device_id) {
56  chromeos::BluetoothDeviceClient::Properties* properties =
57      chromeos::DBusThreadManager::Get()->GetBluetoothDeviceClient()->
58          GetProperties(object_path);
59  DCHECK(properties);
60
61  std::string modalias = properties->modalias.value();
62  BluetoothDevice::VendorIDSource source_value;
63  int vendor_value, product_value, device_value;
64
65  if (sscanf(modalias.c_str(), "bluetooth:v%04xp%04xd%04x",
66             &vendor_value, &product_value, &device_value) == 3) {
67    source_value = BluetoothDevice::VENDOR_ID_BLUETOOTH;
68  } else if (sscanf(modalias.c_str(), "usb:v%04xp%04xd%04x",
69                    &vendor_value, &product_value, &device_value) == 3) {
70    source_value = BluetoothDevice::VENDOR_ID_USB;
71  } else {
72    return;
73  }
74
75  if (vendor_id_source != NULL)
76    *vendor_id_source = source_value;
77  if (vendor_id != NULL)
78    *vendor_id = vendor_value;
79  if (product_id != NULL)
80    *product_id = product_value;
81  if (device_id != NULL)
82    *device_id = device_value;
83}
84
85void RecordPairingResult(BluetoothDevice::ConnectErrorCode error_code) {
86  UMAPairingResult pairing_result;
87  switch (error_code) {
88    case BluetoothDevice::ERROR_INPROGRESS:
89      pairing_result = UMA_PAIRING_RESULT_INPROGRESS;
90      break;
91    case BluetoothDevice::ERROR_FAILED:
92      pairing_result = UMA_PAIRING_RESULT_FAILED;
93      break;
94    case BluetoothDevice::ERROR_AUTH_FAILED:
95      pairing_result = UMA_PAIRING_RESULT_AUTH_FAILED;
96      break;
97    case BluetoothDevice::ERROR_AUTH_CANCELED:
98      pairing_result = UMA_PAIRING_RESULT_AUTH_CANCELED;
99      break;
100    case BluetoothDevice::ERROR_AUTH_REJECTED:
101      pairing_result = UMA_PAIRING_RESULT_AUTH_REJECTED;
102      break;
103    case BluetoothDevice::ERROR_AUTH_TIMEOUT:
104      pairing_result = UMA_PAIRING_RESULT_AUTH_TIMEOUT;
105      break;
106    case BluetoothDevice::ERROR_UNSUPPORTED_DEVICE:
107      pairing_result = UMA_PAIRING_RESULT_UNSUPPORTED_DEVICE;
108      break;
109    default:
110      pairing_result = UMA_PAIRING_RESULT_UNKNOWN_ERROR;
111  }
112
113  UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingResult",
114                            pairing_result,
115                            UMA_PAIRING_RESULT_COUNT);
116}
117
118}  // namespace
119
120namespace chromeos {
121
122BluetoothDeviceChromeOS::BluetoothDeviceChromeOS(
123    BluetoothAdapterChromeOS* adapter,
124    const dbus::ObjectPath& object_path,
125    scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
126    scoped_refptr<device::BluetoothSocketThread> socket_thread)
127    : adapter_(adapter),
128      object_path_(object_path),
129      num_connecting_calls_(0),
130      connection_monitor_started_(false),
131      ui_task_runner_(ui_task_runner),
132      socket_thread_(socket_thread),
133      weak_ptr_factory_(this) {
134  DBusThreadManager::Get()->GetBluetoothGattServiceClient()->AddObserver(this);
135
136  // Add all known GATT services.
137  const std::vector<dbus::ObjectPath> gatt_services =
138      DBusThreadManager::Get()->GetBluetoothGattServiceClient()->GetServices();
139  for (std::vector<dbus::ObjectPath>::const_iterator it = gatt_services.begin();
140       it != gatt_services.end(); ++it) {
141    GattServiceAdded(*it);
142  }
143}
144
145BluetoothDeviceChromeOS::~BluetoothDeviceChromeOS() {
146  DBusThreadManager::Get()->GetBluetoothGattServiceClient()->
147      RemoveObserver(this);
148
149  // Copy the GATT services list here and clear the original so that when we
150  // send GattServiceRemoved(), GetGattServices() returns no services.
151  GattServiceMap gatt_services = gatt_services_;
152  gatt_services_.clear();
153  for (GattServiceMap::iterator iter = gatt_services.begin();
154       iter != gatt_services.end(); ++iter) {
155    FOR_EACH_OBSERVER(BluetoothDevice::Observer, observers_,
156                      GattServiceRemoved(this, iter->second));
157    delete iter->second;
158  }
159}
160
161void BluetoothDeviceChromeOS::AddObserver(
162    device::BluetoothDevice::Observer* observer) {
163  DCHECK(observer);
164  observers_.AddObserver(observer);
165}
166
167void BluetoothDeviceChromeOS::RemoveObserver(
168    device::BluetoothDevice::Observer* observer) {
169  DCHECK(observer);
170  observers_.RemoveObserver(observer);
171}
172
173uint32 BluetoothDeviceChromeOS::GetBluetoothClass() const {
174  BluetoothDeviceClient::Properties* properties =
175      DBusThreadManager::Get()->GetBluetoothDeviceClient()->
176          GetProperties(object_path_);
177  DCHECK(properties);
178
179  return properties->bluetooth_class.value();
180}
181
182std::string BluetoothDeviceChromeOS::GetDeviceName() const {
183  BluetoothDeviceClient::Properties* properties =
184      DBusThreadManager::Get()->GetBluetoothDeviceClient()->
185          GetProperties(object_path_);
186  DCHECK(properties);
187
188  return properties->alias.value();
189}
190
191std::string BluetoothDeviceChromeOS::GetAddress() const {
192  BluetoothDeviceClient::Properties* properties =
193      DBusThreadManager::Get()->GetBluetoothDeviceClient()->
194          GetProperties(object_path_);
195  DCHECK(properties);
196
197  return CanonicalizeAddress(properties->address.value());
198}
199
200BluetoothDevice::VendorIDSource
201BluetoothDeviceChromeOS::GetVendorIDSource() const {
202  VendorIDSource vendor_id_source = VENDOR_ID_UNKNOWN;
203  ParseModalias(object_path_, &vendor_id_source, NULL, NULL, NULL);
204  return vendor_id_source;
205}
206
207uint16 BluetoothDeviceChromeOS::GetVendorID() const {
208  uint16 vendor_id  = 0;
209  ParseModalias(object_path_, NULL, &vendor_id, NULL, NULL);
210  return vendor_id;
211}
212
213uint16 BluetoothDeviceChromeOS::GetProductID() const {
214  uint16 product_id  = 0;
215  ParseModalias(object_path_, NULL, NULL, &product_id, NULL);
216  return product_id;
217}
218
219uint16 BluetoothDeviceChromeOS::GetDeviceID() const {
220  uint16 device_id  = 0;
221  ParseModalias(object_path_, NULL, NULL, NULL, &device_id);
222  return device_id;
223}
224
225int BluetoothDeviceChromeOS::GetRSSI() const {
226  BluetoothDeviceClient::Properties* properties =
227      DBusThreadManager::Get()->GetBluetoothDeviceClient()->GetProperties(
228          object_path_);
229  DCHECK(properties);
230
231  if (!IsConnected()) {
232    NOTIMPLEMENTED();
233    return kUnknownPower;
234  }
235
236  return connection_monitor_started_ ? properties->connection_rssi.value()
237                                     : kUnknownPower;
238}
239
240int BluetoothDeviceChromeOS::GetCurrentHostTransmitPower() const {
241  BluetoothDeviceClient::Properties* properties =
242      DBusThreadManager::Get()->GetBluetoothDeviceClient()->GetProperties(
243          object_path_);
244  DCHECK(properties);
245
246  return IsConnected() && connection_monitor_started_
247             ? properties->connection_tx_power.value()
248             : kUnknownPower;
249}
250
251int BluetoothDeviceChromeOS::GetMaximumHostTransmitPower() const {
252  BluetoothDeviceClient::Properties* properties =
253      DBusThreadManager::Get()->GetBluetoothDeviceClient()->GetProperties(
254          object_path_);
255  DCHECK(properties);
256
257  return IsConnected() ? properties->connection_tx_power_max.value()
258                       : kUnknownPower;
259}
260
261bool BluetoothDeviceChromeOS::IsPaired() const {
262  BluetoothDeviceClient::Properties* properties =
263      DBusThreadManager::Get()->GetBluetoothDeviceClient()->
264          GetProperties(object_path_);
265  DCHECK(properties);
266
267  // Trusted devices are devices that don't support pairing but that the
268  // user has explicitly connected; it makes no sense for UI purposes to
269  // treat them differently from each other.
270  return properties->paired.value() || properties->trusted.value();
271}
272
273bool BluetoothDeviceChromeOS::IsConnected() const {
274  BluetoothDeviceClient::Properties* properties =
275      DBusThreadManager::Get()->GetBluetoothDeviceClient()->
276          GetProperties(object_path_);
277  DCHECK(properties);
278
279  return properties->connected.value();
280}
281
282bool BluetoothDeviceChromeOS::IsConnectable() const {
283  BluetoothInputClient::Properties* input_properties =
284      DBusThreadManager::Get()->GetBluetoothInputClient()->
285          GetProperties(object_path_);
286  // GetProperties returns NULL when the device does not implement the given
287  // interface. Non HID devices are normally connectable.
288  if (!input_properties)
289    return true;
290
291  return input_properties->reconnect_mode.value() != "device";
292}
293
294bool BluetoothDeviceChromeOS::IsConnecting() const {
295  return num_connecting_calls_ > 0;
296}
297
298BluetoothDeviceChromeOS::UUIDList BluetoothDeviceChromeOS::GetUUIDs() const {
299  BluetoothDeviceClient::Properties* properties =
300      DBusThreadManager::Get()->GetBluetoothDeviceClient()->
301          GetProperties(object_path_);
302  DCHECK(properties);
303
304  std::vector<device::BluetoothUUID> uuids;
305  const std::vector<std::string> &dbus_uuids = properties->uuids.value();
306  for (std::vector<std::string>::const_iterator iter = dbus_uuids.begin();
307       iter != dbus_uuids.end(); ++iter) {
308    device::BluetoothUUID uuid(*iter);
309    DCHECK(uuid.IsValid());
310    uuids.push_back(uuid);
311  }
312  return uuids;
313}
314
315bool BluetoothDeviceChromeOS::ExpectingPinCode() const {
316  return pairing_.get() && pairing_->ExpectingPinCode();
317}
318
319bool BluetoothDeviceChromeOS::ExpectingPasskey() const {
320  return pairing_.get() && pairing_->ExpectingPasskey();
321}
322
323bool BluetoothDeviceChromeOS::ExpectingConfirmation() const {
324  return pairing_.get() && pairing_->ExpectingConfirmation();
325}
326
327void BluetoothDeviceChromeOS::Connect(
328    BluetoothDevice::PairingDelegate* pairing_delegate,
329    const base::Closure& callback,
330    const ConnectErrorCallback& error_callback) {
331  if (num_connecting_calls_++ == 0)
332    adapter_->NotifyDeviceChanged(this);
333
334  VLOG(1) << object_path_.value() << ": Connecting, " << num_connecting_calls_
335          << " in progress";
336
337  if (IsPaired() || !pairing_delegate || !IsPairable()) {
338    // No need to pair, or unable to, skip straight to connection.
339    ConnectInternal(false, callback, error_callback);
340  } else {
341    // Initiate high-security connection with pairing.
342    BeginPairing(pairing_delegate);
343
344    DBusThreadManager::Get()->GetBluetoothDeviceClient()->
345        Pair(object_path_,
346             base::Bind(&BluetoothDeviceChromeOS::OnPair,
347                        weak_ptr_factory_.GetWeakPtr(),
348                        callback, error_callback),
349             base::Bind(&BluetoothDeviceChromeOS::OnPairError,
350                        weak_ptr_factory_.GetWeakPtr(),
351                        error_callback));
352  }
353}
354
355void BluetoothDeviceChromeOS::SetPinCode(const std::string& pincode) {
356  if (!pairing_.get())
357    return;
358
359  pairing_->SetPinCode(pincode);
360}
361
362void BluetoothDeviceChromeOS::SetPasskey(uint32 passkey) {
363  if (!pairing_.get())
364    return;
365
366  pairing_->SetPasskey(passkey);
367}
368
369void BluetoothDeviceChromeOS::ConfirmPairing() {
370  if (!pairing_.get())
371    return;
372
373  pairing_->ConfirmPairing();
374}
375
376void BluetoothDeviceChromeOS::RejectPairing() {
377  if (!pairing_.get())
378    return;
379
380  pairing_->RejectPairing();
381}
382
383void BluetoothDeviceChromeOS::CancelPairing() {
384  bool canceled = false;
385
386  // If there is a callback in progress that we can reply to then use that
387  // to cancel the current pairing request.
388  if (pairing_.get() && pairing_->CancelPairing())
389    canceled = true;
390
391  // If not we have to send an explicit CancelPairing() to the device instead.
392  if (!canceled) {
393    VLOG(1) << object_path_.value() << ": No pairing context or callback. "
394            << "Sending explicit cancel";
395    DBusThreadManager::Get()->GetBluetoothDeviceClient()->
396        CancelPairing(
397            object_path_,
398            base::Bind(&base::DoNothing),
399            base::Bind(&BluetoothDeviceChromeOS::OnCancelPairingError,
400                       weak_ptr_factory_.GetWeakPtr()));
401  }
402
403  // Since there is no callback to this method it's possible that the pairing
404  // delegate is going to be freed before things complete (indeed it's
405  // documented that this is the method you should call while freeing the
406  // pairing delegate), so clear our the context holding on to it.
407  EndPairing();
408}
409
410void BluetoothDeviceChromeOS::Disconnect(const base::Closure& callback,
411                                         const ErrorCallback& error_callback) {
412  VLOG(1) << object_path_.value() << ": Disconnecting";
413  DBusThreadManager::Get()->GetBluetoothDeviceClient()->
414      Disconnect(
415          object_path_,
416          base::Bind(&BluetoothDeviceChromeOS::OnDisconnect,
417                     weak_ptr_factory_.GetWeakPtr(),
418                     callback),
419          base::Bind(&BluetoothDeviceChromeOS::OnDisconnectError,
420                     weak_ptr_factory_.GetWeakPtr(),
421                     error_callback));
422}
423
424void BluetoothDeviceChromeOS::Forget(const ErrorCallback& error_callback) {
425  VLOG(1) << object_path_.value() << ": Removing device";
426  DBusThreadManager::Get()->GetBluetoothAdapterClient()->
427      RemoveDevice(
428          adapter_->object_path(),
429          object_path_,
430          base::Bind(&base::DoNothing),
431          base::Bind(&BluetoothDeviceChromeOS::OnForgetError,
432                     weak_ptr_factory_.GetWeakPtr(),
433                     error_callback));
434}
435
436void BluetoothDeviceChromeOS::ConnectToProfile(
437    device::BluetoothProfile* profile,
438    const base::Closure& callback,
439    const ConnectToProfileErrorCallback& error_callback) {
440  // TODO(keybuK): Remove.
441  error_callback.Run("Removed. Use chrome.bluetoothSocket.connect() instead.");
442}
443
444void BluetoothDeviceChromeOS::ConnectToService(
445    const BluetoothUUID& uuid,
446    const ConnectToServiceCallback& callback,
447    const ConnectToServiceErrorCallback& error_callback) {
448  VLOG(1) << object_path_.value() << ": Connecting to service: "
449          << uuid.canonical_value();
450  scoped_refptr<BluetoothSocketChromeOS> socket =
451      BluetoothSocketChromeOS::CreateBluetoothSocket(
452          ui_task_runner_,
453          socket_thread_,
454          NULL,
455          net::NetLog::Source());
456  socket->Connect(this, uuid,
457                  base::Bind(callback, socket),
458                  error_callback);
459}
460
461void BluetoothDeviceChromeOS::StartConnectionMonitor(
462    const base::Closure& callback,
463    const ErrorCallback& error_callback) {
464  DBusThreadManager::Get()->GetBluetoothDeviceClient()->StartConnectionMonitor(
465      object_path_,
466      base::Bind(&BluetoothDeviceChromeOS::OnStartConnectionMonitor,
467                 weak_ptr_factory_.GetWeakPtr(),
468                 callback),
469      base::Bind(&BluetoothDeviceChromeOS::OnStartConnectionMonitorError,
470                 weak_ptr_factory_.GetWeakPtr(),
471                 error_callback));
472}
473
474BluetoothPairingChromeOS* BluetoothDeviceChromeOS::BeginPairing(
475    BluetoothDevice::PairingDelegate* pairing_delegate) {
476  pairing_.reset(new BluetoothPairingChromeOS(this, pairing_delegate));
477  return pairing_.get();
478}
479
480void BluetoothDeviceChromeOS::EndPairing() {
481  pairing_.reset();
482}
483
484BluetoothPairingChromeOS* BluetoothDeviceChromeOS::GetPairing() const {
485  return pairing_.get();
486}
487
488void BluetoothDeviceChromeOS::GattServiceAdded(
489    const dbus::ObjectPath& object_path) {
490  if (GetGattService(object_path.value())) {
491    VLOG(1) << "Remote GATT service already exists: " << object_path.value();
492    return;
493  }
494
495  BluetoothGattServiceClient::Properties* properties =
496      DBusThreadManager::Get()->GetBluetoothGattServiceClient()->
497          GetProperties(object_path);
498  DCHECK(properties);
499  if (properties->device.value() != object_path_) {
500    VLOG(2) << "Remote GATT service does not belong to this device.";
501    return;
502  }
503
504  VLOG(1) << "Adding new remote GATT service for device: " << GetAddress();
505
506  BluetoothRemoteGattServiceChromeOS* service =
507      new BluetoothRemoteGattServiceChromeOS(this, object_path);
508  gatt_services_[service->GetIdentifier()] = service;
509  DCHECK(service->object_path() == object_path);
510  DCHECK(service->GetUUID().IsValid());
511
512  FOR_EACH_OBSERVER(device::BluetoothDevice::Observer, observers_,
513                    GattServiceAdded(this, service));
514}
515
516void BluetoothDeviceChromeOS::GattServiceRemoved(
517    const dbus::ObjectPath& object_path) {
518  GattServiceMap::iterator iter = gatt_services_.find(object_path.value());
519  if (iter == gatt_services_.end()) {
520    VLOG(2) << "Unknown GATT service removed: " << object_path.value();
521    return;
522  }
523
524  VLOG(1) << "Removing remote GATT service from device: " << GetAddress();
525
526  BluetoothRemoteGattServiceChromeOS* service =
527      static_cast<BluetoothRemoteGattServiceChromeOS*>(iter->second);
528  DCHECK(service->object_path() == object_path);
529  gatt_services_.erase(iter);
530  FOR_EACH_OBSERVER(device::BluetoothDevice::Observer, observers_,
531                    GattServiceRemoved(this, service));
532  delete service;
533}
534
535void BluetoothDeviceChromeOS::ConnectInternal(
536    bool after_pairing,
537    const base::Closure& callback,
538    const ConnectErrorCallback& error_callback) {
539  VLOG(1) << object_path_.value() << ": Connecting";
540  DBusThreadManager::Get()->GetBluetoothDeviceClient()->
541      Connect(
542          object_path_,
543          base::Bind(&BluetoothDeviceChromeOS::OnConnect,
544                     weak_ptr_factory_.GetWeakPtr(),
545                     after_pairing,
546                     callback),
547          base::Bind(&BluetoothDeviceChromeOS::OnConnectError,
548                     weak_ptr_factory_.GetWeakPtr(),
549                     after_pairing,
550                     error_callback));
551}
552
553void BluetoothDeviceChromeOS::OnConnect(bool after_pairing,
554                                        const base::Closure& callback) {
555  if (--num_connecting_calls_ == 0)
556    adapter_->NotifyDeviceChanged(this);
557
558  DCHECK(num_connecting_calls_ >= 0);
559  VLOG(1) << object_path_.value() << ": Connected, " << num_connecting_calls_
560        << " still in progress";
561
562  SetTrusted();
563
564  if (after_pairing)
565    UMA_HISTOGRAM_ENUMERATION("Bluetooth.PairingResult",
566                              UMA_PAIRING_RESULT_SUCCESS,
567                              UMA_PAIRING_RESULT_COUNT);
568
569  callback.Run();
570}
571
572void BluetoothDeviceChromeOS::OnConnectError(
573    bool after_pairing,
574    const ConnectErrorCallback& error_callback,
575    const std::string& error_name,
576    const std::string& error_message) {
577  if (--num_connecting_calls_ == 0)
578    adapter_->NotifyDeviceChanged(this);
579
580  DCHECK(num_connecting_calls_ >= 0);
581  LOG(WARNING) << object_path_.value() << ": Failed to connect device: "
582               << error_name << ": " << error_message;
583  VLOG(1) << object_path_.value() << ": " << num_connecting_calls_
584          << " still in progress";
585
586  // Determine the error code from error_name.
587  ConnectErrorCode error_code = ERROR_UNKNOWN;
588  if (error_name == bluetooth_device::kErrorFailed) {
589    error_code = ERROR_FAILED;
590  } else if (error_name == bluetooth_device::kErrorInProgress) {
591    error_code = ERROR_INPROGRESS;
592  } else if (error_name == bluetooth_device::kErrorNotSupported) {
593    error_code = ERROR_UNSUPPORTED_DEVICE;
594  }
595
596  if (after_pairing)
597    RecordPairingResult(error_code);
598  error_callback.Run(error_code);
599}
600
601void BluetoothDeviceChromeOS::OnPair(
602    const base::Closure& callback,
603    const ConnectErrorCallback& error_callback) {
604  VLOG(1) << object_path_.value() << ": Paired";
605
606  EndPairing();
607
608  ConnectInternal(true, callback, error_callback);
609}
610
611void BluetoothDeviceChromeOS::OnPairError(
612    const ConnectErrorCallback& error_callback,
613    const std::string& error_name,
614    const std::string& error_message) {
615  if (--num_connecting_calls_ == 0)
616    adapter_->NotifyDeviceChanged(this);
617
618  DCHECK(num_connecting_calls_ >= 0);
619  LOG(WARNING) << object_path_.value() << ": Failed to pair device: "
620               << error_name << ": " << error_message;
621  VLOG(1) << object_path_.value() << ": " << num_connecting_calls_
622          << " still in progress";
623
624  EndPairing();
625
626  // Determine the error code from error_name.
627  ConnectErrorCode error_code = ERROR_UNKNOWN;
628  if (error_name == bluetooth_device::kErrorConnectionAttemptFailed) {
629    error_code = ERROR_FAILED;
630  } else if (error_name == bluetooth_device::kErrorFailed) {
631    error_code = ERROR_FAILED;
632  } else if (error_name == bluetooth_device::kErrorAuthenticationFailed) {
633    error_code = ERROR_AUTH_FAILED;
634  } else if (error_name == bluetooth_device::kErrorAuthenticationCanceled) {
635    error_code = ERROR_AUTH_CANCELED;
636  } else if (error_name == bluetooth_device::kErrorAuthenticationRejected) {
637    error_code = ERROR_AUTH_REJECTED;
638  } else if (error_name == bluetooth_device::kErrorAuthenticationTimeout) {
639    error_code = ERROR_AUTH_TIMEOUT;
640  }
641
642  RecordPairingResult(error_code);
643  error_callback.Run(error_code);
644}
645
646void BluetoothDeviceChromeOS::OnCancelPairingError(
647    const std::string& error_name,
648    const std::string& error_message) {
649  LOG(WARNING) << object_path_.value() << ": Failed to cancel pairing: "
650               << error_name << ": " << error_message;
651}
652
653void BluetoothDeviceChromeOS::SetTrusted() {
654  // Unconditionally send the property change, rather than checking the value
655  // first; there's no harm in doing this and it solves any race conditions
656  // with the property becoming true or false and this call happening before
657  // we get the D-Bus signal about the earlier change.
658  DBusThreadManager::Get()->GetBluetoothDeviceClient()->
659      GetProperties(object_path_)->trusted.Set(
660          true,
661          base::Bind(&BluetoothDeviceChromeOS::OnSetTrusted,
662                     weak_ptr_factory_.GetWeakPtr()));
663}
664
665void BluetoothDeviceChromeOS::OnSetTrusted(bool success) {
666  LOG_IF(WARNING, !success) << object_path_.value()
667                            << ": Failed to set device as trusted";
668}
669
670void BluetoothDeviceChromeOS::OnStartConnectionMonitor(
671    const base::Closure& callback) {
672  connection_monitor_started_ = true;
673  callback.Run();
674}
675
676void BluetoothDeviceChromeOS::OnStartConnectionMonitorError(
677    const ErrorCallback& error_callback,
678    const std::string& error_name,
679    const std::string& error_message) {
680  LOG(WARNING) << object_path_.value()
681               << ": Failed to start connection monitor: " << error_name << ": "
682               << error_message;
683  error_callback.Run();
684}
685
686void BluetoothDeviceChromeOS::OnDisconnect(const base::Closure& callback) {
687  VLOG(1) << object_path_.value() << ": Disconnected";
688  callback.Run();
689}
690
691void BluetoothDeviceChromeOS::OnDisconnectError(
692    const ErrorCallback& error_callback,
693    const std::string& error_name,
694    const std::string& error_message) {
695  LOG(WARNING) << object_path_.value() << ": Failed to disconnect device: "
696               << error_name << ": " << error_message;
697  error_callback.Run();
698}
699
700void BluetoothDeviceChromeOS::OnForgetError(
701    const ErrorCallback& error_callback,
702    const std::string& error_name,
703    const std::string& error_message) {
704  LOG(WARNING) << object_path_.value() << ": Failed to remove device: "
705               << error_name << ": " << error_message;
706  error_callback.Run();
707}
708
709}  // namespace chromeos
710