bluetooth_device_chromeos.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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
23using device::BluetoothDevice;
24
25namespace {
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).
30const char kAgentPath[] = "/org/chromium/bluetooth_agent";
31
32// Histogram enumerations for pairing methods.
33enum 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.
46enum 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
61void 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
93void 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
128namespace chromeos {
129
130BluetoothDeviceChromeOS::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
141BluetoothDeviceChromeOS::~BluetoothDeviceChromeOS() {
142}
143
144uint32 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
153std::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
162std::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
171uint16 BluetoothDeviceChromeOS::GetVendorID() const {
172  uint16 vendor_id  = 0;
173  ParseModalias(object_path_, &vendor_id, NULL, NULL);
174  return vendor_id;
175}
176
177uint16 BluetoothDeviceChromeOS::GetProductID() const {
178  uint16 product_id  = 0;
179  ParseModalias(object_path_, NULL, &product_id, NULL);
180  return product_id;
181}
182
183uint16 BluetoothDeviceChromeOS::GetDeviceID() const {
184  uint16 device_id  = 0;
185  ParseModalias(object_path_, NULL, NULL, &device_id);
186  return device_id;
187}
188
189bool 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
201bool 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
210bool 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
222bool BluetoothDeviceChromeOS::IsConnecting() const {
223  return num_connecting_calls_ > 0;
224}
225
226BluetoothDeviceChromeOS::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
236void BluetoothDeviceChromeOS::GetServiceRecords(
237    const ServiceRecordsCallback& callback,
238    const ErrorCallback& error_callback) {
239  // TODO(keybuk): not implemented; remove
240  error_callback.Run();
241}
242
243void BluetoothDeviceChromeOS::ProvidesServiceWithName(
244    const std::string& name,
245    const ProvidesServiceCallback& callback) {
246  // TODO(keybuk): not implemented; remove
247  callback.Run(false);
248}
249
250bool BluetoothDeviceChromeOS::ExpectingPinCode() const {
251  return !pincode_callback_.is_null();
252}
253
254bool BluetoothDeviceChromeOS::ExpectingPasskey() const {
255  return !passkey_callback_.is_null();
256}
257
258bool BluetoothDeviceChromeOS::ExpectingConfirmation() const {
259  return !confirmation_callback_.is_null();
260}
261
262void 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
305void 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
313void 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
321void BluetoothDeviceChromeOS::ConfirmPairing() {
322  if (!agent_.get() || confirmation_callback_.is_null())
323    return;
324
325  confirmation_callback_.Run(SUCCESS);
326  confirmation_callback_.Reset();
327}
328
329void BluetoothDeviceChromeOS::RejectPairing() {
330  RunPairingCallbacks(REJECTED);
331}
332
333void 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
350void 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
364void 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
376void 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
383void 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
407void 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
415void BluetoothDeviceChromeOS::ClearOutOfBandPairingData(
416    const base::Closure& callback,
417    const ErrorCallback& error_callback) {
418  // TODO(keybuk): implement
419  error_callback.Run();
420}
421
422
423void 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
435void 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
453void 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
469void 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
487void 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
508void 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
527void BluetoothDeviceChromeOS::RequestAuthorization(
528    const dbus::ObjectPath& device_path,
529    const ConfirmationCallback& callback) {
530  // TODO(keybuk): implement
531  callback.Run(CANCELLED);
532}
533
534void 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
542void BluetoothDeviceChromeOS::Cancel() {
543  DCHECK(agent_.get());
544  VLOG(1) << object_path_.value() << ": Cancel";
545
546  DCHECK(pairing_delegate_);
547  pairing_delegate_->DismissDisplayOrConfirm();
548}
549
550void 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
568void 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
587void 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
616void 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
631void 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
655void 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
669void 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
704void 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
711void 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
723void BluetoothDeviceChromeOS::OnSetTrusted(bool success) {
724  LOG_IF(WARNING, !success) << object_path_.value()
725                            << ": Failed to set device as trusted";
726}
727
728void 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
753void 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
760void BluetoothDeviceChromeOS::OnDisconnect(const base::Closure& callback) {
761  VLOG(1) << object_path_.value() << ": Disconnected";
762  callback.Run();
763}
764
765void 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
774void 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
783bool 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
809void 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
819void 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