• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "device/bluetooth/bluetooth_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