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