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.
5 #include "device/bluetooth/bluetooth_adapter_chromeos.h"
10 #include "base/logging.h"
11 #include "base/metrics/histogram.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/sys_info.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "chromeos/dbus/bluetooth_adapter_client.h"
17 #include "chromeos/dbus/bluetooth_agent_manager_client.h"
18 #include "chromeos/dbus/bluetooth_agent_service_provider.h"
19 #include "chromeos/dbus/bluetooth_device_client.h"
20 #include "chromeos/dbus/bluetooth_input_client.h"
21 #include "chromeos/dbus/dbus_thread_manager.h"
22 #include "device/bluetooth/bluetooth_device.h"
23 #include "device/bluetooth/bluetooth_device_chromeos.h"
24 #include "device/bluetooth/bluetooth_pairing_chromeos.h"
25 #include "device/bluetooth/bluetooth_socket_chromeos.h"
26 #include "device/bluetooth/bluetooth_socket_thread.h"
27 #include "device/bluetooth/bluetooth_uuid.h"
28 #include "third_party/cros_system_api/dbus/service_constants.h"
30 using device::BluetoothAdapter
;
31 using device::BluetoothDevice
;
32 using device::BluetoothSocket
;
33 using device::BluetoothUUID
;
37 // The agent path is relatively meaningless since BlueZ only permits one to
38 // exist per D-Bus connection, it just has to be unique within Chromium.
39 const char kAgentPath
[] = "/org/chromium/bluetooth_agent";
41 void OnUnregisterAgentError(const std::string
& error_name
,
42 const std::string
& error_message
) {
43 // It's okay if the agent didn't exist, it means we never saw an adapter.
44 if (error_name
== bluetooth_agent_manager::kErrorDoesNotExist
)
47 LOG(WARNING
) << "Failed to unregister pairing agent: "
48 << error_name
<< ": " << error_message
;
56 base::WeakPtr
<BluetoothAdapter
> BluetoothAdapter::CreateAdapter(
57 const InitCallback
& init_callback
) {
58 return chromeos::BluetoothAdapterChromeOS::CreateAdapter();
66 base::WeakPtr
<BluetoothAdapter
> BluetoothAdapterChromeOS::CreateAdapter() {
67 BluetoothAdapterChromeOS
* adapter
= new BluetoothAdapterChromeOS();
68 return adapter
->weak_ptr_factory_
.GetWeakPtr();
71 BluetoothAdapterChromeOS::BluetoothAdapterChromeOS()
72 : num_discovery_sessions_(0),
73 discovery_request_pending_(false),
74 weak_ptr_factory_(this) {
75 ui_task_runner_
= base::ThreadTaskRunnerHandle::Get();
76 socket_thread_
= device::BluetoothSocketThread::Get();
78 DBusThreadManager::Get()->GetBluetoothAdapterClient()->AddObserver(this);
79 DBusThreadManager::Get()->GetBluetoothDeviceClient()->AddObserver(this);
80 DBusThreadManager::Get()->GetBluetoothInputClient()->AddObserver(this);
82 // Register the pairing agent.
83 dbus::Bus
* system_bus
= DBusThreadManager::Get()->GetSystemBus();
84 agent_
.reset(BluetoothAgentServiceProvider::Create(
85 system_bus
, dbus::ObjectPath(kAgentPath
), this));
88 std::vector
<dbus::ObjectPath
> object_paths
=
89 DBusThreadManager::Get()->GetBluetoothAdapterClient()->GetAdapters();
91 if (!object_paths
.empty()) {
92 VLOG(1) << object_paths
.size() << " Bluetooth adapter(s) available.";
93 SetAdapter(object_paths
[0]);
97 BluetoothAdapterChromeOS::~BluetoothAdapterChromeOS() {
98 DBusThreadManager::Get()->GetBluetoothAdapterClient()->RemoveObserver(this);
99 DBusThreadManager::Get()->GetBluetoothDeviceClient()->RemoveObserver(this);
100 DBusThreadManager::Get()->GetBluetoothInputClient()->RemoveObserver(this);
102 VLOG(1) << "Unregistering pairing agent";
103 DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
105 dbus::ObjectPath(kAgentPath
),
106 base::Bind(&base::DoNothing
),
107 base::Bind(&OnUnregisterAgentError
));
110 void BluetoothAdapterChromeOS::AddObserver(
111 BluetoothAdapter::Observer
* observer
) {
113 observers_
.AddObserver(observer
);
116 void BluetoothAdapterChromeOS::RemoveObserver(
117 BluetoothAdapter::Observer
* observer
) {
119 observers_
.RemoveObserver(observer
);
122 std::string
BluetoothAdapterChromeOS::GetAddress() const {
124 return std::string();
126 BluetoothAdapterClient::Properties
* properties
=
127 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
128 GetProperties(object_path_
);
131 return BluetoothDevice::CanonicalizeAddress(properties
->address
.value());
134 std::string
BluetoothAdapterChromeOS::GetName() const {
136 return std::string();
138 BluetoothAdapterClient::Properties
* properties
=
139 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
140 GetProperties(object_path_
);
143 return properties
->alias
.value();
146 void BluetoothAdapterChromeOS::SetName(const std::string
& name
,
147 const base::Closure
& callback
,
148 const ErrorCallback
& error_callback
) {
150 error_callback
.Run();
152 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
153 GetProperties(object_path_
)->alias
.Set(
155 base::Bind(&BluetoothAdapterChromeOS::OnPropertyChangeCompleted
,
156 weak_ptr_factory_
.GetWeakPtr(),
161 bool BluetoothAdapterChromeOS::IsInitialized() const {
165 bool BluetoothAdapterChromeOS::IsPresent() const {
166 return !object_path_
.value().empty();
169 bool BluetoothAdapterChromeOS::IsPowered() const {
173 BluetoothAdapterClient::Properties
* properties
=
174 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
175 GetProperties(object_path_
);
177 return properties
->powered
.value();
180 void BluetoothAdapterChromeOS::SetPowered(
182 const base::Closure
& callback
,
183 const ErrorCallback
& error_callback
) {
185 error_callback
.Run();
187 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
188 GetProperties(object_path_
)->powered
.Set(
190 base::Bind(&BluetoothAdapterChromeOS::OnPropertyChangeCompleted
,
191 weak_ptr_factory_
.GetWeakPtr(),
196 bool BluetoothAdapterChromeOS::IsDiscoverable() const {
200 BluetoothAdapterClient::Properties
* properties
=
201 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
202 GetProperties(object_path_
);
204 return properties
->discoverable
.value();
207 void BluetoothAdapterChromeOS::SetDiscoverable(
209 const base::Closure
& callback
,
210 const ErrorCallback
& error_callback
) {
212 error_callback
.Run();
214 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
215 GetProperties(object_path_
)->discoverable
.Set(
217 base::Bind(&BluetoothAdapterChromeOS::OnSetDiscoverable
,
218 weak_ptr_factory_
.GetWeakPtr(),
223 bool BluetoothAdapterChromeOS::IsDiscovering() const {
227 BluetoothAdapterClient::Properties
* properties
=
228 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
229 GetProperties(object_path_
);
231 return properties
->discovering
.value();
234 void BluetoothAdapterChromeOS::ReadLocalOutOfBandPairingData(
235 const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback
& callback
,
236 const ErrorCallback
& error_callback
) {
237 error_callback
.Run();
240 void BluetoothAdapterChromeOS::CreateRfcommService(
241 const BluetoothUUID
& uuid
,
244 const CreateServiceCallback
& callback
,
245 const CreateServiceErrorCallback
& error_callback
) {
246 VLOG(1) << object_path_
.value() << ": Creating RFCOMM service: "
247 << uuid
.canonical_value();
248 scoped_refptr
<BluetoothSocketChromeOS
> socket
=
249 BluetoothSocketChromeOS::CreateBluetoothSocket(
253 net::NetLog::Source());
255 BluetoothSocketChromeOS::kRfcomm
,
259 base::Bind(callback
, socket
),
263 void BluetoothAdapterChromeOS::CreateL2capService(
264 const BluetoothUUID
& uuid
,
266 const CreateServiceCallback
& callback
,
267 const CreateServiceErrorCallback
& error_callback
) {
268 VLOG(1) << object_path_
.value() << ": Creating L2CAP service: "
269 << uuid
.canonical_value();
270 scoped_refptr
<BluetoothSocketChromeOS
> socket
=
271 BluetoothSocketChromeOS::CreateBluetoothSocket(
275 net::NetLog::Source());
277 BluetoothSocketChromeOS::kL2cap
,
281 base::Bind(callback
, socket
),
285 void BluetoothAdapterChromeOS::RemovePairingDelegateInternal(
286 BluetoothDevice::PairingDelegate
* pairing_delegate
) {
287 // Before removing a pairing delegate make sure that there aren't any devices
288 // currently using it; if there are, clear the pairing context which will
289 // make any responses no-ops.
290 for (DevicesMap::iterator iter
= devices_
.begin();
291 iter
!= devices_
.end(); ++iter
) {
292 BluetoothDeviceChromeOS
* device_chromeos
=
293 static_cast<BluetoothDeviceChromeOS
*>(iter
->second
);
295 BluetoothPairingChromeOS
* pairing
= device_chromeos
->GetPairing();
296 if (pairing
&& pairing
->GetPairingDelegate() == pairing_delegate
)
297 device_chromeos
->EndPairing();
301 void BluetoothAdapterChromeOS::AdapterAdded(
302 const dbus::ObjectPath
& object_path
) {
303 // Set the adapter to the newly added adapter only if no adapter is present.
305 SetAdapter(object_path
);
308 void BluetoothAdapterChromeOS::AdapterRemoved(
309 const dbus::ObjectPath
& object_path
) {
310 if (object_path
== object_path_
)
314 void BluetoothAdapterChromeOS::AdapterPropertyChanged(
315 const dbus::ObjectPath
& object_path
,
316 const std::string
& property_name
) {
317 if (object_path
!= object_path_
)
320 BluetoothAdapterClient::Properties
* properties
=
321 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
322 GetProperties(object_path_
);
324 if (property_name
== properties
->powered
.name())
325 PoweredChanged(properties
->powered
.value());
326 else if (property_name
== properties
->discoverable
.name())
327 DiscoverableChanged(properties
->discoverable
.value());
328 else if (property_name
== properties
->discovering
.name())
329 DiscoveringChanged(properties
->discovering
.value());
332 void BluetoothAdapterChromeOS::DeviceAdded(
333 const dbus::ObjectPath
& object_path
) {
334 BluetoothDeviceClient::Properties
* properties
=
335 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
336 GetProperties(object_path
);
337 if (properties
->adapter
.value() != object_path_
)
340 BluetoothDeviceChromeOS
* device_chromeos
=
341 new BluetoothDeviceChromeOS(this,
345 DCHECK(devices_
.find(device_chromeos
->GetAddress()) == devices_
.end());
347 devices_
[device_chromeos
->GetAddress()] = device_chromeos
;
349 FOR_EACH_OBSERVER(BluetoothAdapter::Observer
, observers_
,
350 DeviceAdded(this, device_chromeos
));
353 void BluetoothAdapterChromeOS::DeviceRemoved(
354 const dbus::ObjectPath
& object_path
) {
355 for (DevicesMap::iterator iter
= devices_
.begin();
356 iter
!= devices_
.end(); ++iter
) {
357 BluetoothDeviceChromeOS
* device_chromeos
=
358 static_cast<BluetoothDeviceChromeOS
*>(iter
->second
);
359 if (device_chromeos
->object_path() == object_path
) {
360 devices_
.erase(iter
);
362 FOR_EACH_OBSERVER(BluetoothAdapter::Observer
, observers_
,
363 DeviceRemoved(this, device_chromeos
));
364 delete device_chromeos
;
370 void BluetoothAdapterChromeOS::DevicePropertyChanged(
371 const dbus::ObjectPath
& object_path
,
372 const std::string
& property_name
) {
373 BluetoothDeviceChromeOS
* device_chromeos
= GetDeviceWithPath(object_path
);
374 if (!device_chromeos
)
377 BluetoothDeviceClient::Properties
* properties
=
378 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
379 GetProperties(object_path
);
381 if (property_name
== properties
->bluetooth_class
.name() ||
382 property_name
== properties
->address
.name() ||
383 property_name
== properties
->alias
.name() ||
384 property_name
== properties
->paired
.name() ||
385 property_name
== properties
->trusted
.name() ||
386 property_name
== properties
->connected
.name() ||
387 property_name
== properties
->uuids
.name() ||
388 property_name
== properties
->rssi
.name() ||
389 property_name
== properties
->connection_rssi
.name() ||
390 property_name
== properties
->connection_tx_power
.name())
391 NotifyDeviceChanged(device_chromeos
);
393 // When a device becomes paired, mark it as trusted so that the user does
394 // not need to approve every incoming connection
395 if (property_name
== properties
->paired
.name() &&
396 properties
->paired
.value() && !properties
->trusted
.value())
397 device_chromeos
->SetTrusted();
399 // UMA connection counting
400 if (property_name
== properties
->connected
.name()) {
401 // PlayStation joystick tries to reconnect after disconnection from USB.
402 // If it is still not trusted, set it, so it becomes available on the
403 // list of known devices.
404 if (properties
->connected
.value() && device_chromeos
->IsTrustable() &&
405 !properties
->trusted
.value())
406 device_chromeos
->SetTrusted();
410 for (DevicesMap::iterator iter
= devices_
.begin();
411 iter
!= devices_
.end(); ++iter
) {
412 if (iter
->second
->IsPaired() && iter
->second
->IsConnected())
416 UMA_HISTOGRAM_COUNTS_100("Bluetooth.ConnectedDeviceCount", count
);
420 void BluetoothAdapterChromeOS::InputPropertyChanged(
421 const dbus::ObjectPath
& object_path
,
422 const std::string
& property_name
) {
423 BluetoothDeviceChromeOS
* device_chromeos
= GetDeviceWithPath(object_path
);
424 if (!device_chromeos
)
427 BluetoothInputClient::Properties
* properties
=
428 DBusThreadManager::Get()->GetBluetoothInputClient()->
429 GetProperties(object_path
);
431 // Properties structure can be removed, which triggers a change in the
432 // BluetoothDevice::IsConnectable() property, as does a change in the
433 // actual reconnect_mode property.
435 property_name
== properties
->reconnect_mode
.name())
436 NotifyDeviceChanged(device_chromeos
);
439 void BluetoothAdapterChromeOS::Released() {
440 DCHECK(agent_
.get());
441 VLOG(1) << "Release";
443 // Called after we unregister the pairing agent, e.g. when changing I/O
444 // capabilities. Nothing much to be done right now.
447 void BluetoothAdapterChromeOS::RequestPinCode(
448 const dbus::ObjectPath
& device_path
,
449 const PinCodeCallback
& callback
) {
450 DCHECK(agent_
.get());
451 VLOG(1) << device_path
.value() << ": RequestPinCode";
453 BluetoothPairingChromeOS
* pairing
= GetPairing(device_path
);
455 callback
.Run(REJECTED
, "");
459 pairing
->RequestPinCode(callback
);
462 void BluetoothAdapterChromeOS::DisplayPinCode(
463 const dbus::ObjectPath
& device_path
,
464 const std::string
& pincode
) {
465 DCHECK(agent_
.get());
466 VLOG(1) << device_path
.value() << ": DisplayPinCode: " << pincode
;
468 BluetoothPairingChromeOS
* pairing
= GetPairing(device_path
);
472 pairing
->DisplayPinCode(pincode
);
475 void BluetoothAdapterChromeOS::RequestPasskey(
476 const dbus::ObjectPath
& device_path
,
477 const PasskeyCallback
& callback
) {
478 DCHECK(agent_
.get());
479 VLOG(1) << device_path
.value() << ": RequestPasskey";
481 BluetoothPairingChromeOS
* pairing
= GetPairing(device_path
);
483 callback
.Run(REJECTED
, 0);
487 pairing
->RequestPasskey(callback
);
490 void BluetoothAdapterChromeOS::DisplayPasskey(
491 const dbus::ObjectPath
& device_path
,
494 DCHECK(agent_
.get());
495 VLOG(1) << device_path
.value() << ": DisplayPasskey: " << passkey
496 << " (" << entered
<< " entered)";
498 BluetoothPairingChromeOS
* pairing
= GetPairing(device_path
);
503 pairing
->DisplayPasskey(passkey
);
505 pairing
->KeysEntered(entered
);
508 void BluetoothAdapterChromeOS::RequestConfirmation(
509 const dbus::ObjectPath
& device_path
,
511 const ConfirmationCallback
& callback
) {
512 DCHECK(agent_
.get());
513 VLOG(1) << device_path
.value() << ": RequestConfirmation: " << passkey
;
515 BluetoothPairingChromeOS
* pairing
= GetPairing(device_path
);
517 callback
.Run(REJECTED
);
521 pairing
->RequestConfirmation(passkey
, callback
);
524 void BluetoothAdapterChromeOS::RequestAuthorization(
525 const dbus::ObjectPath
& device_path
,
526 const ConfirmationCallback
& callback
) {
527 DCHECK(agent_
.get());
528 VLOG(1) << device_path
.value() << ": RequestAuthorization";
530 BluetoothPairingChromeOS
* pairing
= GetPairing(device_path
);
532 callback
.Run(REJECTED
);
536 pairing
->RequestAuthorization(callback
);
539 void BluetoothAdapterChromeOS::AuthorizeService(
540 const dbus::ObjectPath
& device_path
,
541 const std::string
& uuid
,
542 const ConfirmationCallback
& callback
) {
543 DCHECK(agent_
.get());
544 VLOG(1) << device_path
.value() << ": AuthorizeService: " << uuid
;
546 // TODO(keybuk): implement
547 callback
.Run(CANCELLED
);
550 void BluetoothAdapterChromeOS::Cancel() {
551 DCHECK(agent_
.get());
555 void BluetoothAdapterChromeOS::OnRegisterAgent() {
556 VLOG(1) << "Pairing agent registered, requesting to be made default";
558 DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
560 dbus::ObjectPath(kAgentPath
),
561 base::Bind(&BluetoothAdapterChromeOS::OnRequestDefaultAgent
,
562 weak_ptr_factory_
.GetWeakPtr()),
563 base::Bind(&BluetoothAdapterChromeOS::OnRequestDefaultAgentError
,
564 weak_ptr_factory_
.GetWeakPtr()));
568 void BluetoothAdapterChromeOS::OnRegisterAgentError(
569 const std::string
& error_name
,
570 const std::string
& error_message
) {
571 // Our agent being already registered isn't an error.
572 if (error_name
== bluetooth_agent_manager::kErrorAlreadyExists
)
575 LOG(WARNING
) << ": Failed to register pairing agent: "
576 << error_name
<< ": " << error_message
;
579 void BluetoothAdapterChromeOS::OnRequestDefaultAgent() {
580 VLOG(1) << "Pairing agent now default";
583 void BluetoothAdapterChromeOS::OnRequestDefaultAgentError(
584 const std::string
& error_name
,
585 const std::string
& error_message
) {
586 LOG(WARNING
) << ": Failed to make pairing agent default: "
587 << error_name
<< ": " << error_message
;
590 BluetoothDeviceChromeOS
*
591 BluetoothAdapterChromeOS::GetDeviceWithPath(
592 const dbus::ObjectPath
& object_path
) {
593 for (DevicesMap::iterator iter
= devices_
.begin();
594 iter
!= devices_
.end(); ++iter
) {
595 BluetoothDeviceChromeOS
* device_chromeos
=
596 static_cast<BluetoothDeviceChromeOS
*>(iter
->second
);
597 if (device_chromeos
->object_path() == object_path
)
598 return device_chromeos
;
604 BluetoothPairingChromeOS
* BluetoothAdapterChromeOS::GetPairing(
605 const dbus::ObjectPath
& object_path
)
607 BluetoothDeviceChromeOS
* device_chromeos
= GetDeviceWithPath(object_path
);
608 if (!device_chromeos
) {
609 LOG(WARNING
) << "Pairing Agent request for unknown device: "
610 << object_path
.value();
614 BluetoothPairingChromeOS
* pairing
= device_chromeos
->GetPairing();
618 // The device doesn't have its own pairing context, so this is an incoming
619 // pairing request that should use our best default delegate (if we have one).
620 BluetoothDevice::PairingDelegate
* pairing_delegate
= DefaultPairingDelegate();
621 if (!pairing_delegate
)
624 return device_chromeos
->BeginPairing(pairing_delegate
);
627 void BluetoothAdapterChromeOS::SetAdapter(const dbus::ObjectPath
& object_path
) {
628 DCHECK(!IsPresent());
629 object_path_
= object_path
;
631 VLOG(1) << object_path_
.value() << ": using adapter.";
633 VLOG(1) << "Registering pairing agent";
634 DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
636 dbus::ObjectPath(kAgentPath
),
637 bluetooth_agent_manager::kKeyboardDisplayCapability
,
638 base::Bind(&BluetoothAdapterChromeOS::OnRegisterAgent
,
639 weak_ptr_factory_
.GetWeakPtr()),
640 base::Bind(&BluetoothAdapterChromeOS::OnRegisterAgentError
,
641 weak_ptr_factory_
.GetWeakPtr()));
643 SetDefaultAdapterName();
645 BluetoothAdapterClient::Properties
* properties
=
646 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
647 GetProperties(object_path_
);
649 PresentChanged(true);
651 if (properties
->powered
.value())
652 PoweredChanged(true);
653 if (properties
->discoverable
.value())
654 DiscoverableChanged(true);
655 if (properties
->discovering
.value())
656 DiscoveringChanged(true);
658 std::vector
<dbus::ObjectPath
> device_paths
=
659 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
660 GetDevicesForAdapter(object_path_
);
662 for (std::vector
<dbus::ObjectPath
>::iterator iter
= device_paths
.begin();
663 iter
!= device_paths
.end(); ++iter
) {
668 void BluetoothAdapterChromeOS::SetDefaultAdapterName() {
669 std::string board
= base::SysInfo::GetLsbReleaseBoard();
671 if (board
.substr(0, 6) == "stumpy") {
673 } else if (board
.substr(0, 4) == "link") {
674 alias
= "Chromebook Pixel";
676 alias
= "Chromebook";
679 SetName(alias
, base::Bind(&base::DoNothing
), base::Bind(&base::DoNothing
));
682 void BluetoothAdapterChromeOS::RemoveAdapter() {
684 VLOG(1) << object_path_
.value() << ": adapter removed.";
686 BluetoothAdapterClient::Properties
* properties
=
687 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
688 GetProperties(object_path_
);
690 object_path_
= dbus::ObjectPath("");
692 if (properties
->powered
.value())
693 PoweredChanged(false);
694 if (properties
->discoverable
.value())
695 DiscoverableChanged(false);
696 if (properties
->discovering
.value())
697 DiscoveringChanged(false);
699 // Copy the devices list here and clear the original so that when we
700 // send DeviceRemoved(), GetDevices() returns no devices.
701 DevicesMap devices
= devices_
;
704 for (DevicesMap::iterator iter
= devices
.begin();
705 iter
!= devices
.end(); ++iter
) {
706 FOR_EACH_OBSERVER(BluetoothAdapter::Observer
, observers_
,
707 DeviceRemoved(this, iter
->second
));
711 PresentChanged(false);
714 void BluetoothAdapterChromeOS::PoweredChanged(bool powered
) {
715 FOR_EACH_OBSERVER(BluetoothAdapter::Observer
, observers_
,
716 AdapterPoweredChanged(this, powered
));
719 void BluetoothAdapterChromeOS::DiscoverableChanged(bool discoverable
) {
720 FOR_EACH_OBSERVER(BluetoothAdapter::Observer
, observers_
,
721 AdapterDiscoverableChanged(this, discoverable
));
724 void BluetoothAdapterChromeOS::DiscoveringChanged(
726 // If the adapter stopped discovery due to a reason other than a request by
727 // us, reset the count to 0.
728 VLOG(1) << "Discovering changed: " << discovering
;
729 if (!discovering
&& !discovery_request_pending_
730 && num_discovery_sessions_
> 0) {
731 VLOG(1) << "Marking sessions as inactive.";
732 num_discovery_sessions_
= 0;
733 MarkDiscoverySessionsAsInactive();
735 FOR_EACH_OBSERVER(BluetoothAdapter::Observer
, observers_
,
736 AdapterDiscoveringChanged(this, discovering
));
739 void BluetoothAdapterChromeOS::PresentChanged(bool present
) {
740 FOR_EACH_OBSERVER(BluetoothAdapter::Observer
, observers_
,
741 AdapterPresentChanged(this, present
));
744 void BluetoothAdapterChromeOS::NotifyDeviceChanged(
745 BluetoothDeviceChromeOS
* device
) {
746 DCHECK(device
->adapter_
== this);
748 FOR_EACH_OBSERVER(BluetoothAdapter::Observer
, observers_
,
749 DeviceChanged(this, device
));
752 void BluetoothAdapterChromeOS::OnSetDiscoverable(
753 const base::Closure
& callback
,
754 const ErrorCallback
& error_callback
,
756 // Set the discoverable_timeout property to zero so the adapter remains
757 // discoverable forever.
758 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
759 GetProperties(object_path_
)->discoverable_timeout
.Set(
761 base::Bind(&BluetoothAdapterChromeOS::OnPropertyChangeCompleted
,
762 weak_ptr_factory_
.GetWeakPtr(),
767 void BluetoothAdapterChromeOS::OnPropertyChangeCompleted(
768 const base::Closure
& callback
,
769 const ErrorCallback
& error_callback
,
774 error_callback
.Run();
777 void BluetoothAdapterChromeOS::AddDiscoverySession(
778 const base::Closure
& callback
,
779 const ErrorCallback
& error_callback
) {
781 if (discovery_request_pending_
) {
782 // The pending request is either to stop a previous session or to start a
783 // new one. Either way, queue this one.
784 DCHECK(num_discovery_sessions_
== 1 || num_discovery_sessions_
== 0);
785 VLOG(1) << "Pending request to start/stop device discovery. Queueing "
786 << "request to start a new discovery session.";
787 discovery_request_queue_
.push(std::make_pair(callback
, error_callback
));
791 // The adapter is already discovering.
792 if (num_discovery_sessions_
> 0) {
793 DCHECK(IsDiscovering());
794 DCHECK(!discovery_request_pending_
);
795 num_discovery_sessions_
++;
800 // There are no active discovery sessions.
801 DCHECK(num_discovery_sessions_
== 0);
803 // This is the first request to start device discovery.
804 discovery_request_pending_
= true;
805 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
808 base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery
,
809 weak_ptr_factory_
.GetWeakPtr(),
811 base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError
,
812 weak_ptr_factory_
.GetWeakPtr(),
817 void BluetoothAdapterChromeOS::RemoveDiscoverySession(
818 const base::Closure
& callback
,
819 const ErrorCallback
& error_callback
) {
821 // There are active sessions other than the one currently being removed.
822 if (num_discovery_sessions_
> 1) {
823 DCHECK(IsDiscovering());
824 DCHECK(!discovery_request_pending_
);
825 num_discovery_sessions_
--;
830 // If there is a pending request to BlueZ, then queue this request.
831 if (discovery_request_pending_
) {
832 VLOG(1) << "Pending request to start/stop device discovery. Queueing "
833 << "request to stop discovery session.";
834 error_callback
.Run();
838 // There are no active sessions. Return error.
839 if (num_discovery_sessions_
== 0) {
840 // TODO(armansito): This should never happen once we have the
841 // DiscoverySession API. Replace this case with an assert once it's
842 // the deprecated methods have been removed. (See crbug.com/3445008).
843 VLOG(1) << "No active discovery sessions. Returning error.";
844 error_callback
.Run();
848 // There is exactly one active discovery session. Request BlueZ to stop
850 DCHECK(num_discovery_sessions_
== 1);
851 discovery_request_pending_
= true;
852 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
855 base::Bind(&BluetoothAdapterChromeOS::OnStopDiscovery
,
856 weak_ptr_factory_
.GetWeakPtr(),
858 base::Bind(&BluetoothAdapterChromeOS::OnStopDiscoveryError
,
859 weak_ptr_factory_
.GetWeakPtr(),
863 void BluetoothAdapterChromeOS::OnStartDiscovery(const base::Closure
& callback
) {
864 // Report success on the original request and increment the count.
866 DCHECK(discovery_request_pending_
);
867 DCHECK(num_discovery_sessions_
== 0);
868 discovery_request_pending_
= false;
869 num_discovery_sessions_
++;
872 // Try to add a new discovery session for each queued request.
873 ProcessQueuedDiscoveryRequests();
876 void BluetoothAdapterChromeOS::OnStartDiscoveryError(
877 const base::Closure
& callback
,
878 const ErrorCallback
& error_callback
,
879 const std::string
& error_name
,
880 const std::string
& error_message
) {
881 LOG(WARNING
) << object_path_
.value() << ": Failed to start discovery: "
882 << error_name
<< ": " << error_message
;
884 // Failed to start discovery. This can only happen if the count is at 0.
885 DCHECK(num_discovery_sessions_
== 0);
886 DCHECK(discovery_request_pending_
);
887 discovery_request_pending_
= false;
889 // Discovery request may fail if discovery was previously initiated by Chrome,
890 // but the session were invalidated due to the discovery state unexpectedly
891 // changing to false and then back to true. In this case, report success.
892 if (error_name
== bluetooth_device::kErrorInProgress
&& IsDiscovering()) {
893 VLOG(1) << "Discovery previously initiated. Reporting success.";
894 num_discovery_sessions_
++;
897 error_callback
.Run();
900 // Try to add a new discovery session for each queued request.
901 ProcessQueuedDiscoveryRequests();
904 void BluetoothAdapterChromeOS::OnStopDiscovery(const base::Closure
& callback
) {
905 // Report success on the original request and decrement the count.
907 DCHECK(discovery_request_pending_
);
908 DCHECK(num_discovery_sessions_
== 1);
909 discovery_request_pending_
= false;
910 num_discovery_sessions_
--;
913 // Try to add a new discovery session for each queued request.
914 ProcessQueuedDiscoveryRequests();
917 void BluetoothAdapterChromeOS::OnStopDiscoveryError(
918 const ErrorCallback
& error_callback
,
919 const std::string
& error_name
,
920 const std::string
& error_message
) {
921 LOG(WARNING
) << object_path_
.value() << ": Failed to stop discovery: "
922 << error_name
<< ": " << error_message
;
924 // Failed to stop discovery. This can only happen if the count is at 1.
925 DCHECK(discovery_request_pending_
);
926 DCHECK(num_discovery_sessions_
== 1);
927 discovery_request_pending_
= false;
928 error_callback
.Run();
930 // Try to add a new discovery session for each queued request.
931 ProcessQueuedDiscoveryRequests();
934 void BluetoothAdapterChromeOS::ProcessQueuedDiscoveryRequests() {
935 while (!discovery_request_queue_
.empty()) {
936 VLOG(1) << "Process queued discovery request.";
937 DiscoveryCallbackPair callbacks
= discovery_request_queue_
.front();
938 discovery_request_queue_
.pop();
939 AddDiscoverySession(callbacks
.first
, callbacks
.second
);
941 // If the queued request resulted in a pending call, then let it
942 // asynchonously process the remaining queued requests once the pending
944 if (discovery_request_pending_
)
949 } // namespace chromeos