1 // Copyright (c) 2012 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.
5 #include "device/bluetooth/bluetooth_device_chromeos.h"
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_vector.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/string16.h"
17 #include "base/string_util.h"
18 #include "base/values.h"
19 #include "chromeos/dbus/bluetooth_adapter_client.h"
20 #include "chromeos/dbus/bluetooth_agent_service_provider.h"
21 #include "chromeos/dbus/bluetooth_device_client.h"
22 #include "chromeos/dbus/bluetooth_input_client.h"
23 #include "chromeos/dbus/bluetooth_out_of_band_client.h"
24 #include "chromeos/dbus/dbus_thread_manager.h"
25 #include "chromeos/dbus/introspectable_client.h"
27 #include "dbus/object_path.h"
28 #include "device/bluetooth/bluetooth_adapter_chromeos.h"
29 #include "device/bluetooth/bluetooth_out_of_band_pairing_data.h"
30 #include "device/bluetooth/bluetooth_service_record.h"
31 #include "device/bluetooth/bluetooth_socket_chromeos.h"
32 #include "device/bluetooth/bluetooth_utils.h"
33 #include "third_party/cros_system_api/dbus/service_constants.h"
35 using device::BluetoothDevice
;
36 using device::BluetoothOutOfBandPairingData
;
37 using device::BluetoothServiceRecord
;
38 using device::BluetoothSocket
;
42 BluetoothDeviceChromeOs::BluetoothDeviceChromeOs(
43 BluetoothAdapterChromeOs
* adapter
)
46 pairing_delegate_(NULL
),
47 connecting_applications_counter_(0),
48 weak_ptr_factory_(this) {
51 BluetoothDeviceChromeOs::~BluetoothDeviceChromeOs() {
54 bool BluetoothDeviceChromeOs::IsPaired() const {
55 return !object_path_
.value().empty();
58 const BluetoothDevice::ServiceList
&
59 BluetoothDeviceChromeOs::GetServices() const {
60 return service_uuids_
;
63 void BluetoothDeviceChromeOs::GetServiceRecords(
64 const ServiceRecordsCallback
& callback
,
65 const ErrorCallback
& error_callback
) {
66 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
69 "", // empty pattern to browse all services
70 base::Bind(&BluetoothDeviceChromeOs::CollectServiceRecordsCallback
,
71 weak_ptr_factory_
.GetWeakPtr(),
76 bool BluetoothDeviceChromeOs::ProvidesServiceWithUUID(
77 const std::string
& uuid
) const {
78 const BluetoothDevice::ServiceList
& services
= GetServices();
79 for (BluetoothDevice::ServiceList::const_iterator iter
= services
.begin();
80 iter
!= services
.end();
82 if (device::bluetooth_utils::CanonicalUuid(*iter
) == uuid
)
88 void BluetoothDeviceChromeOs::ProvidesServiceWithName(
89 const std::string
& name
,
90 const ProvidesServiceCallback
& callback
) {
92 base::Bind(&BluetoothDeviceChromeOs::SearchServicesForNameCallback
,
93 weak_ptr_factory_
.GetWeakPtr(),
96 base::Bind(&BluetoothDeviceChromeOs::SearchServicesForNameErrorCallback
,
97 weak_ptr_factory_
.GetWeakPtr(),
101 bool BluetoothDeviceChromeOs::ExpectingPinCode() const {
102 return !pincode_callback_
.is_null();
105 bool BluetoothDeviceChromeOs::ExpectingPasskey() const {
106 return !passkey_callback_
.is_null();
109 bool BluetoothDeviceChromeOs::ExpectingConfirmation() const {
110 return !confirmation_callback_
.is_null();
113 void BluetoothDeviceChromeOs::Connect(PairingDelegate
* pairing_delegate
,
114 const base::Closure
& callback
,
115 const ErrorCallback
& error_callback
) {
116 if (IsPaired() || IsBonded() || IsConnected()) {
117 // Connection to already paired or connected device.
118 ConnectApplications(callback
, error_callback
);
120 } else if (!pairing_delegate
) {
121 // No pairing delegate supplied, initiate low-security connection only.
122 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
123 CreateDevice(adapter_
->object_path_
,
125 base::Bind(&BluetoothDeviceChromeOs::ConnectCallback
,
126 weak_ptr_factory_
.GetWeakPtr(),
129 base::Bind(&BluetoothDeviceChromeOs::ConnectErrorCallback
,
130 weak_ptr_factory_
.GetWeakPtr(),
133 // Initiate high-security connection with pairing.
134 DCHECK(!pairing_delegate_
);
135 pairing_delegate_
= pairing_delegate
;
137 // The agent path is relatively meaningless, we use the device address
138 // to generate it as we only support one pairing attempt at a time for
139 // a given bluetooth device.
140 DCHECK(agent_
.get() == NULL
);
142 std::string agent_path_basename
;
143 ReplaceChars(address_
, ":", "_", &agent_path_basename
);
144 dbus::ObjectPath
agent_path("/org/chromium/bluetooth_agent/" +
145 agent_path_basename
);
147 dbus::Bus
* system_bus
= DBusThreadManager::Get()->GetSystemBus();
149 agent_
.reset(BluetoothAgentServiceProvider::Create(system_bus
,
156 DVLOG(1) << "Pairing: " << address_
;
157 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
159 adapter_
->object_path_
,
162 bluetooth_agent::kDisplayYesNoCapability
,
163 base::Bind(&BluetoothDeviceChromeOs::ConnectCallback
,
164 weak_ptr_factory_
.GetWeakPtr(),
167 base::Bind(&BluetoothDeviceChromeOs::ConnectErrorCallback
,
168 weak_ptr_factory_
.GetWeakPtr(),
173 void BluetoothDeviceChromeOs::SetPinCode(const std::string
& pincode
) {
174 if (!agent_
.get() || pincode_callback_
.is_null())
177 pincode_callback_
.Run(SUCCESS
, pincode
);
178 pincode_callback_
.Reset();
181 void BluetoothDeviceChromeOs::SetPasskey(uint32 passkey
) {
182 if (!agent_
.get() || passkey_callback_
.is_null())
185 passkey_callback_
.Run(SUCCESS
, passkey
);
186 passkey_callback_
.Reset();
189 void BluetoothDeviceChromeOs::ConfirmPairing() {
190 if (!agent_
.get() || confirmation_callback_
.is_null())
193 confirmation_callback_
.Run(SUCCESS
);
194 confirmation_callback_
.Reset();
197 void BluetoothDeviceChromeOs::RejectPairing() {
201 if (!pincode_callback_
.is_null()) {
202 pincode_callback_
.Run(REJECTED
, "");
203 pincode_callback_
.Reset();
205 if (!passkey_callback_
.is_null()) {
206 passkey_callback_
.Run(REJECTED
, 0);
207 passkey_callback_
.Reset();
209 if (!confirmation_callback_
.is_null()) {
210 confirmation_callback_
.Run(REJECTED
);
211 confirmation_callback_
.Reset();
215 void BluetoothDeviceChromeOs::CancelPairing() {
219 if (!pincode_callback_
.is_null()) {
220 pincode_callback_
.Run(CANCELLED
, "");
221 pincode_callback_
.Reset();
223 if (!passkey_callback_
.is_null()) {
224 passkey_callback_
.Run(CANCELLED
, 0);
225 passkey_callback_
.Reset();
227 if (!confirmation_callback_
.is_null()) {
228 confirmation_callback_
.Run(CANCELLED
);
229 confirmation_callback_
.Reset();
233 void BluetoothDeviceChromeOs::Disconnect(const base::Closure
& callback
,
234 const ErrorCallback
& error_callback
) {
235 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
236 Disconnect(object_path_
,
237 base::Bind(&BluetoothDeviceChromeOs::DisconnectCallback
,
238 weak_ptr_factory_
.GetWeakPtr(),
244 void BluetoothDeviceChromeOs::Forget(const ErrorCallback
& error_callback
) {
245 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
246 RemoveDevice(adapter_
->object_path_
,
248 base::Bind(&BluetoothDeviceChromeOs::ForgetCallback
,
249 weak_ptr_factory_
.GetWeakPtr(),
253 void BluetoothDeviceChromeOs::ConnectToService(const std::string
& service_uuid
,
254 const SocketCallback
& callback
) {
256 base::Bind(&BluetoothDeviceChromeOs::GetServiceRecordsForConnectCallback
,
257 weak_ptr_factory_
.GetWeakPtr(),
261 &BluetoothDeviceChromeOs::GetServiceRecordsForConnectErrorCallback
,
262 weak_ptr_factory_
.GetWeakPtr(),
266 void BluetoothDeviceChromeOs::SetOutOfBandPairingData(
267 const BluetoothOutOfBandPairingData
& data
,
268 const base::Closure
& callback
,
269 const ErrorCallback
& error_callback
) {
270 DBusThreadManager::Get()->GetBluetoothOutOfBandClient()->
275 base::Bind(&BluetoothDeviceChromeOs::OnRemoteDataCallback
,
276 weak_ptr_factory_
.GetWeakPtr(),
281 void BluetoothDeviceChromeOs::ClearOutOfBandPairingData(
282 const base::Closure
& callback
,
283 const ErrorCallback
& error_callback
) {
284 DBusThreadManager::Get()->GetBluetoothOutOfBandClient()->
288 base::Bind(&BluetoothDeviceChromeOs::OnRemoteDataCallback
,
289 weak_ptr_factory_
.GetWeakPtr(),
294 void BluetoothDeviceChromeOs::SetObjectPath(
295 const dbus::ObjectPath
& object_path
) {
296 DCHECK(object_path_
== dbus::ObjectPath(""));
297 object_path_
= object_path
;
300 void BluetoothDeviceChromeOs::RemoveObjectPath() {
301 DCHECK(object_path_
!= dbus::ObjectPath(""));
302 object_path_
= dbus::ObjectPath("");
305 void BluetoothDeviceChromeOs::Update(
306 const BluetoothDeviceClient::Properties
* properties
,
308 std::string address
= properties
->address
.value();
309 std::string name
= properties
->name
.value();
310 uint32 bluetooth_class
= properties
->bluetooth_class
.value();
311 const std::vector
<std::string
>& uuids
= properties
->uuids
.value();
313 if (!address
.empty())
318 bluetooth_class_
= bluetooth_class
;
319 if (!uuids
.empty()) {
320 service_uuids_
.clear();
321 service_uuids_
.assign(uuids
.begin(), uuids
.end());
325 // BlueZ uses paired to mean link keys exchanged, whereas the Bluetooth
326 // spec refers to this as bonded. Use the spec name for our interface.
327 bonded_
= properties
->paired
.value();
328 connected_
= properties
->connected
.value();
332 void BluetoothDeviceChromeOs::ConnectCallback(
333 const base::Closure
& callback
,
334 const ErrorCallback
& error_callback
,
335 const dbus::ObjectPath
& device_path
) {
336 DVLOG(1) << "Connection successful: " << device_path
.value();
337 if (object_path_
.value().empty()) {
338 object_path_
= device_path
;
340 LOG_IF(WARNING
, object_path_
!= device_path
)
341 << "Conflicting device paths for objects, result gave: "
342 << device_path
.value() << " but signal gave: "
343 << object_path_
.value();
346 // Mark the device trusted so it can connect to us automatically, and
347 // we can connect after rebooting. This information is part of the
348 // pairing information of the device, and is unique to the combination
349 // of our bluetooth address and the device's bluetooth address. A
350 // different host needs a new pairing, so it's not useful to sync.
351 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
352 GetProperties(object_path_
)->trusted
.Set(
354 base::Bind(&BluetoothDeviceChromeOs::OnSetTrusted
,
355 weak_ptr_factory_
.GetWeakPtr(),
359 // Connect application-layer protocols.
360 ConnectApplications(callback
, error_callback
);
363 void BluetoothDeviceChromeOs::ConnectErrorCallback(
364 const ErrorCallback
& error_callback
,
365 const std::string
& error_name
,
366 const std::string
& error_message
) {
367 LOG(WARNING
) << "Connection failed: " << address_
368 << ": " << error_name
<< ": " << error_message
;
369 error_callback
.Run();
372 void BluetoothDeviceChromeOs::CollectServiceRecordsCallback(
373 const ServiceRecordsCallback
& callback
,
374 const ErrorCallback
& error_callback
,
375 const dbus::ObjectPath
& device_path
,
376 const BluetoothDeviceClient::ServiceMap
& service_map
,
379 error_callback
.Run();
383 ScopedVector
<BluetoothServiceRecord
> records
;
384 for (BluetoothDeviceClient::ServiceMap::const_iterator i
=
385 service_map
.begin(); i
!= service_map
.end(); ++i
) {
387 new BluetoothServiceRecord(address(), i
->second
));
389 callback
.Run(records
);
392 void BluetoothDeviceChromeOs::OnSetTrusted(const base::Closure
& callback
,
393 const ErrorCallback
& error_callback
,
398 LOG(WARNING
) << "Failed to set device as trusted: " << address_
;
399 error_callback
.Run();
403 void BluetoothDeviceChromeOs::ConnectApplications(
404 const base::Closure
& callback
,
405 const ErrorCallback
& error_callback
) {
406 // Introspect the device object to determine supported applications.
407 DBusThreadManager::Get()->GetIntrospectableClient()->
408 Introspect(bluetooth_device::kBluetoothDeviceServiceName
,
410 base::Bind(&BluetoothDeviceChromeOs::OnIntrospect
,
411 weak_ptr_factory_
.GetWeakPtr(),
416 void BluetoothDeviceChromeOs::OnIntrospect(const base::Closure
& callback
,
417 const ErrorCallback
& error_callback
,
418 const std::string
& service_name
,
419 const dbus::ObjectPath
& device_path
,
420 const std::string
& xml_data
,
423 LOG(WARNING
) << "Failed to determine supported applications: " << address_
;
424 error_callback
.Run();
428 // The introspection data for the device object may list one or more
429 // additional D-Bus interfaces that BlueZ supports for this particular
430 // device. Send appropraite Connect calls for each of those interfaces
431 // to connect all of the application protocols for this device.
432 std::vector
<std::string
> interfaces
=
433 IntrospectableClient::GetInterfacesFromIntrospectResult(xml_data
);
435 DCHECK_EQ(0, connecting_applications_counter_
);
436 connecting_applications_counter_
= 0;
437 for (std::vector
<std::string
>::iterator iter
= interfaces
.begin();
438 iter
!= interfaces
.end(); ++iter
) {
439 if (*iter
== bluetooth_input::kBluetoothInputInterface
) {
440 connecting_applications_counter_
++;
441 // Supports Input interface.
442 DBusThreadManager::Get()->GetBluetoothInputClient()->
443 Connect(object_path_
,
444 base::Bind(&BluetoothDeviceChromeOs::OnConnect
,
445 weak_ptr_factory_
.GetWeakPtr(),
448 base::Bind(&BluetoothDeviceChromeOs::OnConnectError
,
449 weak_ptr_factory_
.GetWeakPtr(),
450 error_callback
, *iter
));
454 // If OnConnect has been called for every call to Connect above, then this
455 // will decrement the counter to -1. In that case, call the callback
456 // directly as it has not been called by any of the OnConnect callbacks.
457 // This is safe because OnIntrospect and OnConnect run on the same thread.
458 connecting_applications_counter_
--;
459 if (connecting_applications_counter_
== -1)
463 void BluetoothDeviceChromeOs::OnConnect(const base::Closure
& callback
,
464 const std::string
& interface_name
,
465 const dbus::ObjectPath
& device_path
) {
466 DVLOG(1) << "Application connection successful: " << device_path
.value()
467 << ": " << interface_name
;
469 connecting_applications_counter_
--;
470 // |callback| should only be called once, meaning it cannot be called before
471 // all requests have been started. The extra decrement after all requests
472 // have been started, and the check for -1 instead of 0 below, insure only a
473 // single call to |callback| will occur (provided OnConnect and OnIntrospect
474 // run on the same thread, which is true).
475 if (connecting_applications_counter_
== -1) {
476 connecting_applications_counter_
= 0;
481 void BluetoothDeviceChromeOs::OnConnectError(
482 const ErrorCallback
& error_callback
,
483 const std::string
& interface_name
,
484 const dbus::ObjectPath
& device_path
,
485 const std::string
& error_name
,
486 const std::string
& error_message
) {
487 LOG(WARNING
) << "Connection failed: " << address_
<< ": " << interface_name
488 << ": " << error_name
<< ": " << error_message
;
489 error_callback
.Run();
492 void BluetoothDeviceChromeOs::DisconnectCallback(
493 const base::Closure
& callback
,
494 const ErrorCallback
& error_callback
,
495 const dbus::ObjectPath
& device_path
,
497 DCHECK(device_path
== object_path_
);
499 DVLOG(1) << "Disconnection successful: " << address_
;
502 LOG(WARNING
) << "Disconnection failed: " << address_
;
503 error_callback
.Run();
507 void BluetoothDeviceChromeOs::ForgetCallback(
508 const ErrorCallback
& error_callback
,
509 const dbus::ObjectPath
& adapter_path
,
511 // It's quite normal that this path never gets called on success; we use a
512 // weak pointer, and bluetoothd might send the DeviceRemoved signal before
513 // the method reply, in which case this object is deleted and the
514 // callback never takes place. Therefore don't do anything here for the
517 LOG(WARNING
) << "Forget failed: " << address_
;
518 error_callback
.Run();
522 void BluetoothDeviceChromeOs::SearchServicesForNameErrorCallback(
523 const ProvidesServiceCallback
& callback
) {
527 void BluetoothDeviceChromeOs::SearchServicesForNameCallback(
528 const std::string
& name
,
529 const ProvidesServiceCallback
& callback
,
530 const ServiceRecordList
& list
) {
531 for (ServiceRecordList::const_iterator i
= list
.begin();
532 i
!= list
.end(); ++i
) {
533 if ((*i
)->name() == name
) {
541 void BluetoothDeviceChromeOs::GetServiceRecordsForConnectErrorCallback(
542 const SocketCallback
& callback
) {
546 void BluetoothDeviceChromeOs::GetServiceRecordsForConnectCallback(
547 const std::string
& service_uuid
,
548 const SocketCallback
& callback
,
549 const ServiceRecordList
& list
) {
550 for (ServiceRecordList::const_iterator i
= list
.begin();
551 i
!= list
.end(); ++i
) {
552 if ((*i
)->uuid() == service_uuid
) {
553 // If multiple service records are found, use the first one that works.
554 scoped_refptr
<BluetoothSocket
> socket(
555 BluetoothSocketChromeOs::CreateBluetoothSocket(**i
));
556 if (socket
.get() != NULL
) {
557 callback
.Run(socket
);
565 void BluetoothDeviceChromeOs::OnRemoteDataCallback(
566 const base::Closure
& callback
,
567 const ErrorCallback
& error_callback
,
572 error_callback
.Run();
575 void BluetoothDeviceChromeOs::DisconnectRequested(
576 const dbus::ObjectPath
& object_path
) {
577 DCHECK(object_path
== object_path_
);
580 void BluetoothDeviceChromeOs::Release() {
581 DCHECK(agent_
.get());
582 DVLOG(1) << "Release: " << address_
;
584 DCHECK(pairing_delegate_
);
585 pairing_delegate_
->DismissDisplayOrConfirm();
586 pairing_delegate_
= NULL
;
588 pincode_callback_
.Reset();
589 passkey_callback_
.Reset();
590 confirmation_callback_
.Reset();
595 void BluetoothDeviceChromeOs::RequestPinCode(
596 const dbus::ObjectPath
& device_path
,
597 const PinCodeCallback
& callback
) {
598 DCHECK(agent_
.get());
599 DVLOG(1) << "RequestPinCode: " << device_path
.value();
601 DCHECK(pairing_delegate_
);
602 DCHECK(pincode_callback_
.is_null());
603 pincode_callback_
= callback
;
604 pairing_delegate_
->RequestPinCode(this);
607 void BluetoothDeviceChromeOs::RequestPasskey(
608 const dbus::ObjectPath
& device_path
,
609 const PasskeyCallback
& callback
) {
610 DCHECK(agent_
.get());
611 DCHECK(device_path
== object_path_
);
612 DVLOG(1) << "RequestPasskey: " << device_path
.value();
614 DCHECK(pairing_delegate_
);
615 DCHECK(passkey_callback_
.is_null());
616 passkey_callback_
= callback
;
617 pairing_delegate_
->RequestPasskey(this);
620 void BluetoothDeviceChromeOs::DisplayPinCode(
621 const dbus::ObjectPath
& device_path
,
622 const std::string
& pincode
) {
623 DCHECK(agent_
.get());
624 DCHECK(device_path
== object_path_
);
625 DVLOG(1) << "DisplayPinCode: " << device_path
.value() << " " << pincode
;
627 DCHECK(pairing_delegate_
);
628 pairing_delegate_
->DisplayPinCode(this, pincode
);
631 void BluetoothDeviceChromeOs::DisplayPasskey(
632 const dbus::ObjectPath
& device_path
,
634 DCHECK(agent_
.get());
635 DCHECK(device_path
== object_path_
);
636 DVLOG(1) << "DisplayPasskey: " << device_path
.value() << " " << passkey
;
638 DCHECK(pairing_delegate_
);
639 pairing_delegate_
->DisplayPasskey(this, passkey
);
642 void BluetoothDeviceChromeOs::RequestConfirmation(
643 const dbus::ObjectPath
& device_path
,
645 const ConfirmationCallback
& callback
) {
646 DCHECK(agent_
.get());
647 DCHECK(device_path
== object_path_
);
648 DVLOG(1) << "RequestConfirmation: " << device_path
.value() << " " << passkey
;
650 DCHECK(pairing_delegate_
);
651 DCHECK(confirmation_callback_
.is_null());
652 confirmation_callback_
= callback
;
653 pairing_delegate_
->ConfirmPasskey(this, passkey
);
656 void BluetoothDeviceChromeOs::Authorize(const dbus::ObjectPath
& device_path
,
657 const std::string
& uuid
,
658 const ConfirmationCallback
& callback
) {
659 DCHECK(agent_
.get());
660 DCHECK(device_path
== object_path_
);
661 LOG(WARNING
) << "Rejected authorization for service: " << uuid
662 << " requested from device: " << device_path
.value();
663 callback
.Run(REJECTED
);
666 void BluetoothDeviceChromeOs::ConfirmModeChange(
668 const ConfirmationCallback
& callback
) {
669 DCHECK(agent_
.get());
670 LOG(WARNING
) << "Rejected adapter-level mode change: " << mode
671 << " made on agent for device: " << address_
;
672 callback
.Run(REJECTED
);
675 void BluetoothDeviceChromeOs::Cancel() {
676 DCHECK(agent_
.get());
677 DVLOG(1) << "Cancel: " << address_
;
679 DCHECK(pairing_delegate_
);
680 pairing_delegate_
->DismissDisplayOrConfirm();
685 BluetoothDeviceChromeOs
* BluetoothDeviceChromeOs::Create(
686 BluetoothAdapterChromeOs
* adapter
) {
687 return new BluetoothDeviceChromeOs(adapter
);
690 } // namespace chromeos