• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ParseModalias(const dbus::ObjectPath & object_path,uint16 * vendor_id,uint16 * product_id,uint16 * device_id)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 
RecordPairingResult(BluetoothDevice::ConnectErrorCode error_code)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 
BluetoothDeviceChromeOS(BluetoothAdapterChromeOS * adapter,const dbus::ObjectPath & object_path)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 
~BluetoothDeviceChromeOS()141 BluetoothDeviceChromeOS::~BluetoothDeviceChromeOS() {
142 }
143 
GetBluetoothClass() const144 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 
GetDeviceName() const153 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 
GetAddress() const162 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 
GetVendorID() const171 uint16 BluetoothDeviceChromeOS::GetVendorID() const {
172   uint16 vendor_id  = 0;
173   ParseModalias(object_path_, &vendor_id, NULL, NULL);
174   return vendor_id;
175 }
176 
GetProductID() const177 uint16 BluetoothDeviceChromeOS::GetProductID() const {
178   uint16 product_id  = 0;
179   ParseModalias(object_path_, NULL, &product_id, NULL);
180   return product_id;
181 }
182 
GetDeviceID() const183 uint16 BluetoothDeviceChromeOS::GetDeviceID() const {
184   uint16 device_id  = 0;
185   ParseModalias(object_path_, NULL, NULL, &device_id);
186   return device_id;
187 }
188 
IsPaired() const189 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 
IsConnected() const201 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 
IsConnectable() const210 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 
IsConnecting() const222 bool BluetoothDeviceChromeOS::IsConnecting() const {
223   return num_connecting_calls_ > 0;
224 }
225 
GetServices() const226 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 
GetServiceRecords(const ServiceRecordsCallback & callback,const ErrorCallback & error_callback)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 
ProvidesServiceWithName(const std::string & name,const ProvidesServiceCallback & callback)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 
ExpectingPinCode() const250 bool BluetoothDeviceChromeOS::ExpectingPinCode() const {
251   return !pincode_callback_.is_null();
252 }
253 
ExpectingPasskey() const254 bool BluetoothDeviceChromeOS::ExpectingPasskey() const {
255   return !passkey_callback_.is_null();
256 }
257 
ExpectingConfirmation() const258 bool BluetoothDeviceChromeOS::ExpectingConfirmation() const {
259   return !confirmation_callback_.is_null();
260 }
261 
Connect(BluetoothDevice::PairingDelegate * pairing_delegate,const base::Closure & callback,const ConnectErrorCallback & error_callback)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 
SetPinCode(const std::string & pincode)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 
SetPasskey(uint32 passkey)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 
ConfirmPairing()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 
RejectPairing()329 void BluetoothDeviceChromeOS::RejectPairing() {
330   RunPairingCallbacks(REJECTED);
331 }
332 
CancelPairing()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 
Disconnect(const base::Closure & callback,const ErrorCallback & error_callback)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 
Forget(const ErrorCallback & error_callback)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 
ConnectToService(const std::string & service_uuid,const SocketCallback & callback)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 
ConnectToProfile(device::BluetoothProfile * profile,const base::Closure & callback,const ErrorCallback & error_callback)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 
SetOutOfBandPairingData(const device::BluetoothOutOfBandPairingData & data,const base::Closure & callback,const ErrorCallback & error_callback)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 
ClearOutOfBandPairingData(const base::Closure & callback,const ErrorCallback & error_callback)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 
Release()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 
RequestPinCode(const dbus::ObjectPath & device_path,const PinCodeCallback & callback)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 
DisplayPinCode(const dbus::ObjectPath & device_path,const std::string & pincode)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 
RequestPasskey(const dbus::ObjectPath & device_path,const PasskeyCallback & callback)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 
DisplayPasskey(const dbus::ObjectPath & device_path,uint32 passkey,uint16 entered)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 
RequestConfirmation(const dbus::ObjectPath & device_path,uint32 passkey,const ConfirmationCallback & callback)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 
RequestAuthorization(const dbus::ObjectPath & device_path,const ConfirmationCallback & callback)527 void BluetoothDeviceChromeOS::RequestAuthorization(
528     const dbus::ObjectPath& device_path,
529     const ConfirmationCallback& callback) {
530   // TODO(keybuk): implement
531   callback.Run(CANCELLED);
532 }
533 
AuthorizeService(const dbus::ObjectPath & device_path,const std::string & uuid,const ConfirmationCallback & callback)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 
Cancel()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 
ConnectInternal(bool after_pairing,const base::Closure & callback,const ConnectErrorCallback & error_callback)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 
OnConnect(bool after_pairing,const base::Closure & callback)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 
OnConnectError(bool after_pairing,const ConnectErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)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 
OnRegisterAgent(const base::Closure & callback,const ConnectErrorCallback & error_callback)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 
OnRegisterAgentError(const ConnectErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)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 
OnPair(const base::Closure & callback,const ConnectErrorCallback & error_callback)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 
OnPairError(const ConnectErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)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 
OnCancelPairingError(const std::string & error_name,const std::string & error_message)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 
SetTrusted()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 
OnSetTrusted(bool success)723 void BluetoothDeviceChromeOS::OnSetTrusted(bool success) {
724   LOG_IF(WARNING, !success) << object_path_.value()
725                             << ": Failed to set device as trusted";
726 }
727 
UnregisterAgent()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 
OnUnregisterAgentError(const std::string & error_name,const std::string & error_message)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 
OnDisconnect(const base::Closure & callback)760 void BluetoothDeviceChromeOS::OnDisconnect(const base::Closure& callback) {
761   VLOG(1) << object_path_.value() << ": Disconnected";
762   callback.Run();
763 }
764 
OnDisconnectError(const ErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)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 
OnForgetError(const ErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)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 
RunPairingCallbacks(Status status)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 
OnConnectProfile(device::BluetoothProfile * profile,const base::Closure & callback)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 
OnConnectProfileError(device::BluetoothProfile * profile,const ErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)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