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_adapter_chromeos.h"
6
7 #include <string>
8
9 #include "base/bind.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"
29
30 using device::BluetoothAdapter;
31 using device::BluetoothDevice;
32 using device::BluetoothSocket;
33 using device::BluetoothUUID;
34
35 namespace {
36
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";
40
OnUnregisterAgentError(const std::string & error_name,const std::string & error_message)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)
45 return;
46
47 LOG(WARNING) << "Failed to unregister pairing agent: "
48 << error_name << ": " << error_message;
49 }
50
51 } // namespace
52
53 namespace device {
54
55 // static
CreateAdapter(const InitCallback & init_callback)56 base::WeakPtr<BluetoothAdapter> BluetoothAdapter::CreateAdapter(
57 const InitCallback& init_callback) {
58 return chromeos::BluetoothAdapterChromeOS::CreateAdapter();
59 }
60
61 }
62
63 namespace chromeos {
64
65 // static
CreateAdapter()66 base::WeakPtr<BluetoothAdapter> BluetoothAdapterChromeOS::CreateAdapter() {
67 BluetoothAdapterChromeOS* adapter = new BluetoothAdapterChromeOS();
68 return adapter->weak_ptr_factory_.GetWeakPtr();
69 }
70
BluetoothAdapterChromeOS()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();
77
78 DBusThreadManager::Get()->GetBluetoothAdapterClient()->AddObserver(this);
79 DBusThreadManager::Get()->GetBluetoothDeviceClient()->AddObserver(this);
80 DBusThreadManager::Get()->GetBluetoothInputClient()->AddObserver(this);
81
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));
86 DCHECK(agent_.get());
87
88 std::vector<dbus::ObjectPath> object_paths =
89 DBusThreadManager::Get()->GetBluetoothAdapterClient()->GetAdapters();
90
91 if (!object_paths.empty()) {
92 VLOG(1) << object_paths.size() << " Bluetooth adapter(s) available.";
93 SetAdapter(object_paths[0]);
94 }
95 }
96
~BluetoothAdapterChromeOS()97 BluetoothAdapterChromeOS::~BluetoothAdapterChromeOS() {
98 DBusThreadManager::Get()->GetBluetoothAdapterClient()->RemoveObserver(this);
99 DBusThreadManager::Get()->GetBluetoothDeviceClient()->RemoveObserver(this);
100 DBusThreadManager::Get()->GetBluetoothInputClient()->RemoveObserver(this);
101
102 VLOG(1) << "Unregistering pairing agent";
103 DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
104 UnregisterAgent(
105 dbus::ObjectPath(kAgentPath),
106 base::Bind(&base::DoNothing),
107 base::Bind(&OnUnregisterAgentError));
108 }
109
AddObserver(BluetoothAdapter::Observer * observer)110 void BluetoothAdapterChromeOS::AddObserver(
111 BluetoothAdapter::Observer* observer) {
112 DCHECK(observer);
113 observers_.AddObserver(observer);
114 }
115
RemoveObserver(BluetoothAdapter::Observer * observer)116 void BluetoothAdapterChromeOS::RemoveObserver(
117 BluetoothAdapter::Observer* observer) {
118 DCHECK(observer);
119 observers_.RemoveObserver(observer);
120 }
121
GetAddress() const122 std::string BluetoothAdapterChromeOS::GetAddress() const {
123 if (!IsPresent())
124 return std::string();
125
126 BluetoothAdapterClient::Properties* properties =
127 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
128 GetProperties(object_path_);
129 DCHECK(properties);
130
131 return BluetoothDevice::CanonicalizeAddress(properties->address.value());
132 }
133
GetName() const134 std::string BluetoothAdapterChromeOS::GetName() const {
135 if (!IsPresent())
136 return std::string();
137
138 BluetoothAdapterClient::Properties* properties =
139 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
140 GetProperties(object_path_);
141 DCHECK(properties);
142
143 return properties->alias.value();
144 }
145
SetName(const std::string & name,const base::Closure & callback,const ErrorCallback & error_callback)146 void BluetoothAdapterChromeOS::SetName(const std::string& name,
147 const base::Closure& callback,
148 const ErrorCallback& error_callback) {
149 if (!IsPresent())
150 error_callback.Run();
151
152 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
153 GetProperties(object_path_)->alias.Set(
154 name,
155 base::Bind(&BluetoothAdapterChromeOS::OnPropertyChangeCompleted,
156 weak_ptr_factory_.GetWeakPtr(),
157 callback,
158 error_callback));
159 }
160
IsInitialized() const161 bool BluetoothAdapterChromeOS::IsInitialized() const {
162 return true;
163 }
164
IsPresent() const165 bool BluetoothAdapterChromeOS::IsPresent() const {
166 return !object_path_.value().empty();
167 }
168
IsPowered() const169 bool BluetoothAdapterChromeOS::IsPowered() const {
170 if (!IsPresent())
171 return false;
172
173 BluetoothAdapterClient::Properties* properties =
174 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
175 GetProperties(object_path_);
176
177 return properties->powered.value();
178 }
179
SetPowered(bool powered,const base::Closure & callback,const ErrorCallback & error_callback)180 void BluetoothAdapterChromeOS::SetPowered(
181 bool powered,
182 const base::Closure& callback,
183 const ErrorCallback& error_callback) {
184 if (!IsPresent())
185 error_callback.Run();
186
187 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
188 GetProperties(object_path_)->powered.Set(
189 powered,
190 base::Bind(&BluetoothAdapterChromeOS::OnPropertyChangeCompleted,
191 weak_ptr_factory_.GetWeakPtr(),
192 callback,
193 error_callback));
194 }
195
IsDiscoverable() const196 bool BluetoothAdapterChromeOS::IsDiscoverable() const {
197 if (!IsPresent())
198 return false;
199
200 BluetoothAdapterClient::Properties* properties =
201 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
202 GetProperties(object_path_);
203
204 return properties->discoverable.value();
205 }
206
SetDiscoverable(bool discoverable,const base::Closure & callback,const ErrorCallback & error_callback)207 void BluetoothAdapterChromeOS::SetDiscoverable(
208 bool discoverable,
209 const base::Closure& callback,
210 const ErrorCallback& error_callback) {
211 if (!IsPresent())
212 error_callback.Run();
213
214 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
215 GetProperties(object_path_)->discoverable.Set(
216 discoverable,
217 base::Bind(&BluetoothAdapterChromeOS::OnSetDiscoverable,
218 weak_ptr_factory_.GetWeakPtr(),
219 callback,
220 error_callback));
221 }
222
IsDiscovering() const223 bool BluetoothAdapterChromeOS::IsDiscovering() const {
224 if (!IsPresent())
225 return false;
226
227 BluetoothAdapterClient::Properties* properties =
228 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
229 GetProperties(object_path_);
230
231 return properties->discovering.value();
232 }
233
CreateRfcommService(const BluetoothUUID & uuid,int channel,const CreateServiceCallback & callback,const CreateServiceErrorCallback & error_callback)234 void BluetoothAdapterChromeOS::CreateRfcommService(
235 const BluetoothUUID& uuid,
236 int channel,
237 const CreateServiceCallback& callback,
238 const CreateServiceErrorCallback& error_callback) {
239 VLOG(1) << object_path_.value() << ": Creating RFCOMM service: "
240 << uuid.canonical_value();
241 scoped_refptr<BluetoothSocketChromeOS> socket =
242 BluetoothSocketChromeOS::CreateBluetoothSocket(
243 ui_task_runner_,
244 socket_thread_,
245 NULL,
246 net::NetLog::Source());
247 socket->Listen(this,
248 BluetoothSocketChromeOS::kRfcomm,
249 uuid,
250 channel,
251 base::Bind(callback, socket),
252 error_callback);
253 }
254
CreateL2capService(const BluetoothUUID & uuid,int psm,const CreateServiceCallback & callback,const CreateServiceErrorCallback & error_callback)255 void BluetoothAdapterChromeOS::CreateL2capService(
256 const BluetoothUUID& uuid,
257 int psm,
258 const CreateServiceCallback& callback,
259 const CreateServiceErrorCallback& error_callback) {
260 VLOG(1) << object_path_.value() << ": Creating L2CAP service: "
261 << uuid.canonical_value();
262 scoped_refptr<BluetoothSocketChromeOS> socket =
263 BluetoothSocketChromeOS::CreateBluetoothSocket(
264 ui_task_runner_,
265 socket_thread_,
266 NULL,
267 net::NetLog::Source());
268 socket->Listen(this,
269 BluetoothSocketChromeOS::kL2cap,
270 uuid,
271 psm,
272 base::Bind(callback, socket),
273 error_callback);
274 }
275
RemovePairingDelegateInternal(BluetoothDevice::PairingDelegate * pairing_delegate)276 void BluetoothAdapterChromeOS::RemovePairingDelegateInternal(
277 BluetoothDevice::PairingDelegate* pairing_delegate) {
278 // Before removing a pairing delegate make sure that there aren't any devices
279 // currently using it; if there are, clear the pairing context which will
280 // make any responses no-ops.
281 for (DevicesMap::iterator iter = devices_.begin();
282 iter != devices_.end(); ++iter) {
283 BluetoothDeviceChromeOS* device_chromeos =
284 static_cast<BluetoothDeviceChromeOS*>(iter->second);
285
286 BluetoothPairingChromeOS* pairing = device_chromeos->GetPairing();
287 if (pairing && pairing->GetPairingDelegate() == pairing_delegate)
288 device_chromeos->EndPairing();
289 }
290 }
291
AdapterAdded(const dbus::ObjectPath & object_path)292 void BluetoothAdapterChromeOS::AdapterAdded(
293 const dbus::ObjectPath& object_path) {
294 // Set the adapter to the newly added adapter only if no adapter is present.
295 if (!IsPresent())
296 SetAdapter(object_path);
297 }
298
AdapterRemoved(const dbus::ObjectPath & object_path)299 void BluetoothAdapterChromeOS::AdapterRemoved(
300 const dbus::ObjectPath& object_path) {
301 if (object_path == object_path_)
302 RemoveAdapter();
303 }
304
AdapterPropertyChanged(const dbus::ObjectPath & object_path,const std::string & property_name)305 void BluetoothAdapterChromeOS::AdapterPropertyChanged(
306 const dbus::ObjectPath& object_path,
307 const std::string& property_name) {
308 if (object_path != object_path_)
309 return;
310
311 BluetoothAdapterClient::Properties* properties =
312 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
313 GetProperties(object_path_);
314
315 if (property_name == properties->powered.name())
316 PoweredChanged(properties->powered.value());
317 else if (property_name == properties->discoverable.name())
318 DiscoverableChanged(properties->discoverable.value());
319 else if (property_name == properties->discovering.name())
320 DiscoveringChanged(properties->discovering.value());
321 }
322
DeviceAdded(const dbus::ObjectPath & object_path)323 void BluetoothAdapterChromeOS::DeviceAdded(
324 const dbus::ObjectPath& object_path) {
325 BluetoothDeviceClient::Properties* properties =
326 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
327 GetProperties(object_path);
328 if (properties->adapter.value() != object_path_)
329 return;
330
331 BluetoothDeviceChromeOS* device_chromeos =
332 new BluetoothDeviceChromeOS(this,
333 object_path,
334 ui_task_runner_,
335 socket_thread_);
336 DCHECK(devices_.find(device_chromeos->GetAddress()) == devices_.end());
337
338 devices_[device_chromeos->GetAddress()] = device_chromeos;
339
340 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
341 DeviceAdded(this, device_chromeos));
342 }
343
DeviceRemoved(const dbus::ObjectPath & object_path)344 void BluetoothAdapterChromeOS::DeviceRemoved(
345 const dbus::ObjectPath& object_path) {
346 for (DevicesMap::iterator iter = devices_.begin();
347 iter != devices_.end(); ++iter) {
348 BluetoothDeviceChromeOS* device_chromeos =
349 static_cast<BluetoothDeviceChromeOS*>(iter->second);
350 if (device_chromeos->object_path() == object_path) {
351 devices_.erase(iter);
352
353 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
354 DeviceRemoved(this, device_chromeos));
355 delete device_chromeos;
356 return;
357 }
358 }
359 }
360
DevicePropertyChanged(const dbus::ObjectPath & object_path,const std::string & property_name)361 void BluetoothAdapterChromeOS::DevicePropertyChanged(
362 const dbus::ObjectPath& object_path,
363 const std::string& property_name) {
364 BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path);
365 if (!device_chromeos)
366 return;
367
368 BluetoothDeviceClient::Properties* properties =
369 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
370 GetProperties(object_path);
371
372 if (property_name == properties->bluetooth_class.name() ||
373 property_name == properties->address.name() ||
374 property_name == properties->alias.name() ||
375 property_name == properties->paired.name() ||
376 property_name == properties->trusted.name() ||
377 property_name == properties->connected.name() ||
378 property_name == properties->uuids.name() ||
379 property_name == properties->rssi.name() ||
380 property_name == properties->connection_rssi.name() ||
381 property_name == properties->connection_tx_power.name())
382 NotifyDeviceChanged(device_chromeos);
383
384 // When a device becomes paired, mark it as trusted so that the user does
385 // not need to approve every incoming connection
386 if (property_name == properties->paired.name() &&
387 properties->paired.value() && !properties->trusted.value())
388 device_chromeos->SetTrusted();
389
390 // UMA connection counting
391 if (property_name == properties->connected.name()) {
392 // PlayStation joystick tries to reconnect after disconnection from USB.
393 // If it is still not trusted, set it, so it becomes available on the
394 // list of known devices.
395 if (properties->connected.value() && device_chromeos->IsTrustable() &&
396 !properties->trusted.value())
397 device_chromeos->SetTrusted();
398
399 int count = 0;
400
401 for (DevicesMap::iterator iter = devices_.begin();
402 iter != devices_.end(); ++iter) {
403 if (iter->second->IsPaired() && iter->second->IsConnected())
404 ++count;
405 }
406
407 UMA_HISTOGRAM_COUNTS_100("Bluetooth.ConnectedDeviceCount", count);
408 }
409 }
410
InputPropertyChanged(const dbus::ObjectPath & object_path,const std::string & property_name)411 void BluetoothAdapterChromeOS::InputPropertyChanged(
412 const dbus::ObjectPath& object_path,
413 const std::string& property_name) {
414 BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path);
415 if (!device_chromeos)
416 return;
417
418 BluetoothInputClient::Properties* properties =
419 DBusThreadManager::Get()->GetBluetoothInputClient()->
420 GetProperties(object_path);
421
422 // Properties structure can be removed, which triggers a change in the
423 // BluetoothDevice::IsConnectable() property, as does a change in the
424 // actual reconnect_mode property.
425 if (!properties ||
426 property_name == properties->reconnect_mode.name())
427 NotifyDeviceChanged(device_chromeos);
428 }
429
Released()430 void BluetoothAdapterChromeOS::Released() {
431 DCHECK(agent_.get());
432 VLOG(1) << "Release";
433
434 // Called after we unregister the pairing agent, e.g. when changing I/O
435 // capabilities. Nothing much to be done right now.
436 }
437
RequestPinCode(const dbus::ObjectPath & device_path,const PinCodeCallback & callback)438 void BluetoothAdapterChromeOS::RequestPinCode(
439 const dbus::ObjectPath& device_path,
440 const PinCodeCallback& callback) {
441 DCHECK(agent_.get());
442 VLOG(1) << device_path.value() << ": RequestPinCode";
443
444 BluetoothPairingChromeOS* pairing = GetPairing(device_path);
445 if (!pairing) {
446 callback.Run(REJECTED, "");
447 return;
448 }
449
450 pairing->RequestPinCode(callback);
451 }
452
DisplayPinCode(const dbus::ObjectPath & device_path,const std::string & pincode)453 void BluetoothAdapterChromeOS::DisplayPinCode(
454 const dbus::ObjectPath& device_path,
455 const std::string& pincode) {
456 DCHECK(agent_.get());
457 VLOG(1) << device_path.value() << ": DisplayPinCode: " << pincode;
458
459 BluetoothPairingChromeOS* pairing = GetPairing(device_path);
460 if (!pairing)
461 return;
462
463 pairing->DisplayPinCode(pincode);
464 }
465
RequestPasskey(const dbus::ObjectPath & device_path,const PasskeyCallback & callback)466 void BluetoothAdapterChromeOS::RequestPasskey(
467 const dbus::ObjectPath& device_path,
468 const PasskeyCallback& callback) {
469 DCHECK(agent_.get());
470 VLOG(1) << device_path.value() << ": RequestPasskey";
471
472 BluetoothPairingChromeOS* pairing = GetPairing(device_path);
473 if (!pairing) {
474 callback.Run(REJECTED, 0);
475 return;
476 }
477
478 pairing->RequestPasskey(callback);
479 }
480
DisplayPasskey(const dbus::ObjectPath & device_path,uint32 passkey,uint16 entered)481 void BluetoothAdapterChromeOS::DisplayPasskey(
482 const dbus::ObjectPath& device_path,
483 uint32 passkey,
484 uint16 entered) {
485 DCHECK(agent_.get());
486 VLOG(1) << device_path.value() << ": DisplayPasskey: " << passkey
487 << " (" << entered << " entered)";
488
489 BluetoothPairingChromeOS* pairing = GetPairing(device_path);
490 if (!pairing)
491 return;
492
493 if (entered == 0)
494 pairing->DisplayPasskey(passkey);
495
496 pairing->KeysEntered(entered);
497 }
498
RequestConfirmation(const dbus::ObjectPath & device_path,uint32 passkey,const ConfirmationCallback & callback)499 void BluetoothAdapterChromeOS::RequestConfirmation(
500 const dbus::ObjectPath& device_path,
501 uint32 passkey,
502 const ConfirmationCallback& callback) {
503 DCHECK(agent_.get());
504 VLOG(1) << device_path.value() << ": RequestConfirmation: " << passkey;
505
506 BluetoothPairingChromeOS* pairing = GetPairing(device_path);
507 if (!pairing) {
508 callback.Run(REJECTED);
509 return;
510 }
511
512 pairing->RequestConfirmation(passkey, callback);
513 }
514
RequestAuthorization(const dbus::ObjectPath & device_path,const ConfirmationCallback & callback)515 void BluetoothAdapterChromeOS::RequestAuthorization(
516 const dbus::ObjectPath& device_path,
517 const ConfirmationCallback& callback) {
518 DCHECK(agent_.get());
519 VLOG(1) << device_path.value() << ": RequestAuthorization";
520
521 BluetoothPairingChromeOS* pairing = GetPairing(device_path);
522 if (!pairing) {
523 callback.Run(REJECTED);
524 return;
525 }
526
527 pairing->RequestAuthorization(callback);
528 }
529
AuthorizeService(const dbus::ObjectPath & device_path,const std::string & uuid,const ConfirmationCallback & callback)530 void BluetoothAdapterChromeOS::AuthorizeService(
531 const dbus::ObjectPath& device_path,
532 const std::string& uuid,
533 const ConfirmationCallback& callback) {
534 DCHECK(agent_.get());
535 VLOG(1) << device_path.value() << ": AuthorizeService: " << uuid;
536
537 BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(device_path);
538 if (!device_chromeos) {
539 callback.Run(CANCELLED);
540 return;
541 }
542
543 // We always set paired devices to Trusted, so the only reason that this
544 // method call would ever be called is in the case of a race condition where
545 // our "Set('Trusted', true)" method call is still pending in the Bluetooth
546 // daemon because it's busy handling the incoming connection.
547 if (device_chromeos->IsPaired()) {
548 callback.Run(SUCCESS);
549 return;
550 }
551
552 // TODO(keybuk): reject service authorizations when not paired, determine
553 // whether this is acceptable long-term.
554 LOG(WARNING) << "Rejecting service connection from unpaired device "
555 << device_chromeos->GetAddress() << " for UUID " << uuid;
556 callback.Run(REJECTED);
557 }
558
Cancel()559 void BluetoothAdapterChromeOS::Cancel() {
560 DCHECK(agent_.get());
561 VLOG(1) << "Cancel";
562 }
563
OnRegisterAgent()564 void BluetoothAdapterChromeOS::OnRegisterAgent() {
565 VLOG(1) << "Pairing agent registered, requesting to be made default";
566
567 DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
568 RequestDefaultAgent(
569 dbus::ObjectPath(kAgentPath),
570 base::Bind(&BluetoothAdapterChromeOS::OnRequestDefaultAgent,
571 weak_ptr_factory_.GetWeakPtr()),
572 base::Bind(&BluetoothAdapterChromeOS::OnRequestDefaultAgentError,
573 weak_ptr_factory_.GetWeakPtr()));
574
575 }
576
OnRegisterAgentError(const std::string & error_name,const std::string & error_message)577 void BluetoothAdapterChromeOS::OnRegisterAgentError(
578 const std::string& error_name,
579 const std::string& error_message) {
580 // Our agent being already registered isn't an error.
581 if (error_name == bluetooth_agent_manager::kErrorAlreadyExists)
582 return;
583
584 LOG(WARNING) << ": Failed to register pairing agent: "
585 << error_name << ": " << error_message;
586 }
587
OnRequestDefaultAgent()588 void BluetoothAdapterChromeOS::OnRequestDefaultAgent() {
589 VLOG(1) << "Pairing agent now default";
590 }
591
OnRequestDefaultAgentError(const std::string & error_name,const std::string & error_message)592 void BluetoothAdapterChromeOS::OnRequestDefaultAgentError(
593 const std::string& error_name,
594 const std::string& error_message) {
595 LOG(WARNING) << ": Failed to make pairing agent default: "
596 << error_name << ": " << error_message;
597 }
598
599 BluetoothDeviceChromeOS*
GetDeviceWithPath(const dbus::ObjectPath & object_path)600 BluetoothAdapterChromeOS::GetDeviceWithPath(
601 const dbus::ObjectPath& object_path) {
602 for (DevicesMap::iterator iter = devices_.begin();
603 iter != devices_.end(); ++iter) {
604 BluetoothDeviceChromeOS* device_chromeos =
605 static_cast<BluetoothDeviceChromeOS*>(iter->second);
606 if (device_chromeos->object_path() == object_path)
607 return device_chromeos;
608 }
609
610 return NULL;
611 }
612
GetPairing(const dbus::ObjectPath & object_path)613 BluetoothPairingChromeOS* BluetoothAdapterChromeOS::GetPairing(
614 const dbus::ObjectPath& object_path)
615 {
616 BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path);
617 if (!device_chromeos) {
618 LOG(WARNING) << "Pairing Agent request for unknown device: "
619 << object_path.value();
620 return NULL;
621 }
622
623 BluetoothPairingChromeOS* pairing = device_chromeos->GetPairing();
624 if (pairing)
625 return pairing;
626
627 // The device doesn't have its own pairing context, so this is an incoming
628 // pairing request that should use our best default delegate (if we have one).
629 BluetoothDevice::PairingDelegate* pairing_delegate = DefaultPairingDelegate();
630 if (!pairing_delegate)
631 return NULL;
632
633 return device_chromeos->BeginPairing(pairing_delegate);
634 }
635
SetAdapter(const dbus::ObjectPath & object_path)636 void BluetoothAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) {
637 DCHECK(!IsPresent());
638 object_path_ = object_path;
639
640 VLOG(1) << object_path_.value() << ": using adapter.";
641
642 VLOG(1) << "Registering pairing agent";
643 DBusThreadManager::Get()->GetBluetoothAgentManagerClient()->
644 RegisterAgent(
645 dbus::ObjectPath(kAgentPath),
646 bluetooth_agent_manager::kKeyboardDisplayCapability,
647 base::Bind(&BluetoothAdapterChromeOS::OnRegisterAgent,
648 weak_ptr_factory_.GetWeakPtr()),
649 base::Bind(&BluetoothAdapterChromeOS::OnRegisterAgentError,
650 weak_ptr_factory_.GetWeakPtr()));
651
652 SetDefaultAdapterName();
653
654 BluetoothAdapterClient::Properties* properties =
655 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
656 GetProperties(object_path_);
657
658 PresentChanged(true);
659
660 if (properties->powered.value())
661 PoweredChanged(true);
662 if (properties->discoverable.value())
663 DiscoverableChanged(true);
664 if (properties->discovering.value())
665 DiscoveringChanged(true);
666
667 std::vector<dbus::ObjectPath> device_paths =
668 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
669 GetDevicesForAdapter(object_path_);
670
671 for (std::vector<dbus::ObjectPath>::iterator iter = device_paths.begin();
672 iter != device_paths.end(); ++iter) {
673 DeviceAdded(*iter);
674 }
675 }
676
SetDefaultAdapterName()677 void BluetoothAdapterChromeOS::SetDefaultAdapterName() {
678 std::string board = base::SysInfo::GetLsbReleaseBoard();
679 std::string alias;
680 if (board.substr(0, 6) == "stumpy") {
681 alias = "Chromebox";
682 } else if (board.substr(0, 4) == "link") {
683 alias = "Chromebook Pixel";
684 } else {
685 alias = "Chromebook";
686 }
687
688 SetName(alias, base::Bind(&base::DoNothing), base::Bind(&base::DoNothing));
689 }
690
RemoveAdapter()691 void BluetoothAdapterChromeOS::RemoveAdapter() {
692 DCHECK(IsPresent());
693 VLOG(1) << object_path_.value() << ": adapter removed.";
694
695 BluetoothAdapterClient::Properties* properties =
696 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
697 GetProperties(object_path_);
698
699 object_path_ = dbus::ObjectPath("");
700
701 if (properties->powered.value())
702 PoweredChanged(false);
703 if (properties->discoverable.value())
704 DiscoverableChanged(false);
705 if (properties->discovering.value())
706 DiscoveringChanged(false);
707
708 // Copy the devices list here and clear the original so that when we
709 // send DeviceRemoved(), GetDevices() returns no devices.
710 DevicesMap devices = devices_;
711 devices_.clear();
712
713 for (DevicesMap::iterator iter = devices.begin();
714 iter != devices.end(); ++iter) {
715 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
716 DeviceRemoved(this, iter->second));
717 delete iter->second;
718 }
719
720 PresentChanged(false);
721 }
722
PoweredChanged(bool powered)723 void BluetoothAdapterChromeOS::PoweredChanged(bool powered) {
724 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
725 AdapterPoweredChanged(this, powered));
726 }
727
DiscoverableChanged(bool discoverable)728 void BluetoothAdapterChromeOS::DiscoverableChanged(bool discoverable) {
729 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
730 AdapterDiscoverableChanged(this, discoverable));
731 }
732
DiscoveringChanged(bool discovering)733 void BluetoothAdapterChromeOS::DiscoveringChanged(
734 bool discovering) {
735 // If the adapter stopped discovery due to a reason other than a request by
736 // us, reset the count to 0.
737 VLOG(1) << "Discovering changed: " << discovering;
738 if (!discovering && !discovery_request_pending_
739 && num_discovery_sessions_ > 0) {
740 VLOG(1) << "Marking sessions as inactive.";
741 num_discovery_sessions_ = 0;
742 MarkDiscoverySessionsAsInactive();
743 }
744 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
745 AdapterDiscoveringChanged(this, discovering));
746 }
747
PresentChanged(bool present)748 void BluetoothAdapterChromeOS::PresentChanged(bool present) {
749 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
750 AdapterPresentChanged(this, present));
751 }
752
NotifyDeviceChanged(BluetoothDeviceChromeOS * device)753 void BluetoothAdapterChromeOS::NotifyDeviceChanged(
754 BluetoothDeviceChromeOS* device) {
755 DCHECK(device->adapter_ == this);
756
757 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
758 DeviceChanged(this, device));
759 }
760
OnSetDiscoverable(const base::Closure & callback,const ErrorCallback & error_callback,bool success)761 void BluetoothAdapterChromeOS::OnSetDiscoverable(
762 const base::Closure& callback,
763 const ErrorCallback& error_callback,
764 bool success) {
765 // Set the discoverable_timeout property to zero so the adapter remains
766 // discoverable forever.
767 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
768 GetProperties(object_path_)->discoverable_timeout.Set(
769 0,
770 base::Bind(&BluetoothAdapterChromeOS::OnPropertyChangeCompleted,
771 weak_ptr_factory_.GetWeakPtr(),
772 callback,
773 error_callback));
774 }
775
OnPropertyChangeCompleted(const base::Closure & callback,const ErrorCallback & error_callback,bool success)776 void BluetoothAdapterChromeOS::OnPropertyChangeCompleted(
777 const base::Closure& callback,
778 const ErrorCallback& error_callback,
779 bool success) {
780 if (success)
781 callback.Run();
782 else
783 error_callback.Run();
784 }
785
AddDiscoverySession(const base::Closure & callback,const ErrorCallback & error_callback)786 void BluetoothAdapterChromeOS::AddDiscoverySession(
787 const base::Closure& callback,
788 const ErrorCallback& error_callback) {
789 VLOG(1) << __func__;
790 if (discovery_request_pending_) {
791 // The pending request is either to stop a previous session or to start a
792 // new one. Either way, queue this one.
793 DCHECK(num_discovery_sessions_ == 1 || num_discovery_sessions_ == 0);
794 VLOG(1) << "Pending request to start/stop device discovery. Queueing "
795 << "request to start a new discovery session.";
796 discovery_request_queue_.push(std::make_pair(callback, error_callback));
797 return;
798 }
799
800 // The adapter is already discovering.
801 if (num_discovery_sessions_ > 0) {
802 DCHECK(IsDiscovering());
803 DCHECK(!discovery_request_pending_);
804 num_discovery_sessions_++;
805 callback.Run();
806 return;
807 }
808
809 // There are no active discovery sessions.
810 DCHECK(num_discovery_sessions_ == 0);
811
812 // This is the first request to start device discovery.
813 discovery_request_pending_ = true;
814 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
815 StartDiscovery(
816 object_path_,
817 base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery,
818 weak_ptr_factory_.GetWeakPtr(),
819 callback),
820 base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError,
821 weak_ptr_factory_.GetWeakPtr(),
822 callback,
823 error_callback));
824 }
825
RemoveDiscoverySession(const base::Closure & callback,const ErrorCallback & error_callback)826 void BluetoothAdapterChromeOS::RemoveDiscoverySession(
827 const base::Closure& callback,
828 const ErrorCallback& error_callback) {
829 VLOG(1) << __func__;
830 // There are active sessions other than the one currently being removed.
831 if (num_discovery_sessions_ > 1) {
832 DCHECK(IsDiscovering());
833 DCHECK(!discovery_request_pending_);
834 num_discovery_sessions_--;
835 callback.Run();
836 return;
837 }
838
839 // If there is a pending request to BlueZ, then queue this request.
840 if (discovery_request_pending_) {
841 VLOG(1) << "Pending request to start/stop device discovery. Queueing "
842 << "request to stop discovery session.";
843 error_callback.Run();
844 return;
845 }
846
847 // There are no active sessions. Return error.
848 if (num_discovery_sessions_ == 0) {
849 // TODO(armansito): This should never happen once we have the
850 // DiscoverySession API. Replace this case with an assert once it's
851 // the deprecated methods have been removed. (See crbug.com/3445008).
852 VLOG(1) << "No active discovery sessions. Returning error.";
853 error_callback.Run();
854 return;
855 }
856
857 // There is exactly one active discovery session. Request BlueZ to stop
858 // discovery.
859 DCHECK(num_discovery_sessions_ == 1);
860 discovery_request_pending_ = true;
861 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
862 StopDiscovery(
863 object_path_,
864 base::Bind(&BluetoothAdapterChromeOS::OnStopDiscovery,
865 weak_ptr_factory_.GetWeakPtr(),
866 callback),
867 base::Bind(&BluetoothAdapterChromeOS::OnStopDiscoveryError,
868 weak_ptr_factory_.GetWeakPtr(),
869 error_callback));
870 }
871
OnStartDiscovery(const base::Closure & callback)872 void BluetoothAdapterChromeOS::OnStartDiscovery(const base::Closure& callback) {
873 // Report success on the original request and increment the count.
874 VLOG(1) << __func__;
875 DCHECK(discovery_request_pending_);
876 DCHECK(num_discovery_sessions_ == 0);
877 discovery_request_pending_ = false;
878 num_discovery_sessions_++;
879 callback.Run();
880
881 // Try to add a new discovery session for each queued request.
882 ProcessQueuedDiscoveryRequests();
883 }
884
OnStartDiscoveryError(const base::Closure & callback,const ErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)885 void BluetoothAdapterChromeOS::OnStartDiscoveryError(
886 const base::Closure& callback,
887 const ErrorCallback& error_callback,
888 const std::string& error_name,
889 const std::string& error_message) {
890 LOG(WARNING) << object_path_.value() << ": Failed to start discovery: "
891 << error_name << ": " << error_message;
892
893 // Failed to start discovery. This can only happen if the count is at 0.
894 DCHECK(num_discovery_sessions_ == 0);
895 DCHECK(discovery_request_pending_);
896 discovery_request_pending_ = false;
897
898 // Discovery request may fail if discovery was previously initiated by Chrome,
899 // but the session were invalidated due to the discovery state unexpectedly
900 // changing to false and then back to true. In this case, report success.
901 if (error_name == bluetooth_device::kErrorInProgress && IsDiscovering()) {
902 VLOG(1) << "Discovery previously initiated. Reporting success.";
903 num_discovery_sessions_++;
904 callback.Run();
905 } else {
906 error_callback.Run();
907 }
908
909 // Try to add a new discovery session for each queued request.
910 ProcessQueuedDiscoveryRequests();
911 }
912
OnStopDiscovery(const base::Closure & callback)913 void BluetoothAdapterChromeOS::OnStopDiscovery(const base::Closure& callback) {
914 // Report success on the original request and decrement the count.
915 VLOG(1) << __func__;
916 DCHECK(discovery_request_pending_);
917 DCHECK(num_discovery_sessions_ == 1);
918 discovery_request_pending_ = false;
919 num_discovery_sessions_--;
920 callback.Run();
921
922 // Try to add a new discovery session for each queued request.
923 ProcessQueuedDiscoveryRequests();
924 }
925
OnStopDiscoveryError(const ErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)926 void BluetoothAdapterChromeOS::OnStopDiscoveryError(
927 const ErrorCallback& error_callback,
928 const std::string& error_name,
929 const std::string& error_message) {
930 LOG(WARNING) << object_path_.value() << ": Failed to stop discovery: "
931 << error_name << ": " << error_message;
932
933 // Failed to stop discovery. This can only happen if the count is at 1.
934 DCHECK(discovery_request_pending_);
935 DCHECK(num_discovery_sessions_ == 1);
936 discovery_request_pending_ = false;
937 error_callback.Run();
938
939 // Try to add a new discovery session for each queued request.
940 ProcessQueuedDiscoveryRequests();
941 }
942
ProcessQueuedDiscoveryRequests()943 void BluetoothAdapterChromeOS::ProcessQueuedDiscoveryRequests() {
944 while (!discovery_request_queue_.empty()) {
945 VLOG(1) << "Process queued discovery request.";
946 DiscoveryCallbackPair callbacks = discovery_request_queue_.front();
947 discovery_request_queue_.pop();
948 AddDiscoverySession(callbacks.first, callbacks.second);
949
950 // If the queued request resulted in a pending call, then let it
951 // asynchonously process the remaining queued requests once the pending
952 // call returns.
953 if (discovery_request_pending_)
954 return;
955 }
956 }
957
958 } // namespace chromeos
959