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/sys_info.h"
13 #include "chromeos/dbus/bluetooth_adapter_client.h"
14 #include "chromeos/dbus/bluetooth_device_client.h"
15 #include "chromeos/dbus/bluetooth_input_client.h"
16 #include "chromeos/dbus/dbus_thread_manager.h"
17 #include "device/bluetooth/bluetooth_device.h"
18 #include "device/bluetooth/bluetooth_device_chromeos.h"
19
20 using device::BluetoothAdapter;
21 using device::BluetoothDevice;
22
23 namespace chromeos {
24
BluetoothAdapterChromeOS()25 BluetoothAdapterChromeOS::BluetoothAdapterChromeOS()
26 : weak_ptr_factory_(this) {
27 DBusThreadManager::Get()->GetBluetoothAdapterClient()->AddObserver(this);
28 DBusThreadManager::Get()->GetBluetoothDeviceClient()->AddObserver(this);
29 DBusThreadManager::Get()->GetBluetoothInputClient()->AddObserver(this);
30
31 std::vector<dbus::ObjectPath> object_paths =
32 DBusThreadManager::Get()->GetBluetoothAdapterClient()->GetAdapters();
33
34 if (!object_paths.empty()) {
35 VLOG(1) << object_paths.size() << " Bluetooth adapter(s) available.";
36 SetAdapter(object_paths[0]);
37 }
38 }
39
~BluetoothAdapterChromeOS()40 BluetoothAdapterChromeOS::~BluetoothAdapterChromeOS() {
41 DBusThreadManager::Get()->GetBluetoothAdapterClient()->RemoveObserver(this);
42 DBusThreadManager::Get()->GetBluetoothDeviceClient()->RemoveObserver(this);
43 DBusThreadManager::Get()->GetBluetoothInputClient()->RemoveObserver(this);
44 }
45
AddObserver(BluetoothAdapter::Observer * observer)46 void BluetoothAdapterChromeOS::AddObserver(
47 BluetoothAdapter::Observer* observer) {
48 DCHECK(observer);
49 observers_.AddObserver(observer);
50 }
51
RemoveObserver(BluetoothAdapter::Observer * observer)52 void BluetoothAdapterChromeOS::RemoveObserver(
53 BluetoothAdapter::Observer* observer) {
54 DCHECK(observer);
55 observers_.RemoveObserver(observer);
56 }
57
GetAddress() const58 std::string BluetoothAdapterChromeOS::GetAddress() const {
59 if (!IsPresent())
60 return std::string();
61
62 BluetoothAdapterClient::Properties* properties =
63 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
64 GetProperties(object_path_);
65 DCHECK(properties);
66
67 return properties->address.value();
68 }
69
GetName() const70 std::string BluetoothAdapterChromeOS::GetName() const {
71 if (!IsPresent())
72 return std::string();
73
74 BluetoothAdapterClient::Properties* properties =
75 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
76 GetProperties(object_path_);
77 DCHECK(properties);
78
79 return properties->alias.value();
80 }
81
IsInitialized() const82 bool BluetoothAdapterChromeOS::IsInitialized() const {
83 return true;
84 }
85
IsPresent() const86 bool BluetoothAdapterChromeOS::IsPresent() const {
87 return !object_path_.value().empty();
88 }
89
IsPowered() const90 bool BluetoothAdapterChromeOS::IsPowered() const {
91 if (!IsPresent())
92 return false;
93
94 BluetoothAdapterClient::Properties* properties =
95 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
96 GetProperties(object_path_);
97
98 return properties->powered.value();
99 }
100
SetPowered(bool powered,const base::Closure & callback,const ErrorCallback & error_callback)101 void BluetoothAdapterChromeOS::SetPowered(
102 bool powered,
103 const base::Closure& callback,
104 const ErrorCallback& error_callback) {
105 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
106 GetProperties(object_path_)->powered.Set(
107 powered,
108 base::Bind(&BluetoothAdapterChromeOS::OnSetPowered,
109 weak_ptr_factory_.GetWeakPtr(),
110 callback,
111 error_callback));
112 }
113
IsDiscovering() const114 bool BluetoothAdapterChromeOS::IsDiscovering() const {
115 if (!IsPresent())
116 return false;
117
118 BluetoothAdapterClient::Properties* properties =
119 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
120 GetProperties(object_path_);
121
122 return properties->discovering.value();
123 }
124
StartDiscovering(const base::Closure & callback,const ErrorCallback & error_callback)125 void BluetoothAdapterChromeOS::StartDiscovering(
126 const base::Closure& callback,
127 const ErrorCallback& error_callback) {
128 // BlueZ counts discovery sessions, and permits multiple sessions for a
129 // single connection, so issue a StartDiscovery() call for every use
130 // within Chromium for the right behavior.
131 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
132 StartDiscovery(
133 object_path_,
134 base::Bind(&BluetoothAdapterChromeOS::OnStartDiscovery,
135 weak_ptr_factory_.GetWeakPtr(),
136 callback),
137 base::Bind(&BluetoothAdapterChromeOS::OnStartDiscoveryError,
138 weak_ptr_factory_.GetWeakPtr(),
139 error_callback));
140 }
141
StopDiscovering(const base::Closure & callback,const ErrorCallback & error_callback)142 void BluetoothAdapterChromeOS::StopDiscovering(
143 const base::Closure& callback,
144 const ErrorCallback& error_callback) {
145 // Inform BlueZ to stop one of our open discovery sessions.
146 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
147 StopDiscovery(
148 object_path_,
149 base::Bind(&BluetoothAdapterChromeOS::OnStopDiscovery,
150 weak_ptr_factory_.GetWeakPtr(),
151 callback),
152 base::Bind(&BluetoothAdapterChromeOS::OnStopDiscoveryError,
153 weak_ptr_factory_.GetWeakPtr(),
154 error_callback));
155 }
156
ReadLocalOutOfBandPairingData(const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback & callback,const ErrorCallback & error_callback)157 void BluetoothAdapterChromeOS::ReadLocalOutOfBandPairingData(
158 const BluetoothAdapter::BluetoothOutOfBandPairingDataCallback& callback,
159 const ErrorCallback& error_callback) {
160 error_callback.Run();
161 }
162
AdapterAdded(const dbus::ObjectPath & object_path)163 void BluetoothAdapterChromeOS::AdapterAdded(
164 const dbus::ObjectPath& object_path) {
165 // Set the adapter to the newly added adapter only if no adapter is present.
166 if (!IsPresent())
167 SetAdapter(object_path);
168 }
169
AdapterRemoved(const dbus::ObjectPath & object_path)170 void BluetoothAdapterChromeOS::AdapterRemoved(
171 const dbus::ObjectPath& object_path) {
172 if (object_path == object_path_)
173 RemoveAdapter();
174 }
175
AdapterPropertyChanged(const dbus::ObjectPath & object_path,const std::string & property_name)176 void BluetoothAdapterChromeOS::AdapterPropertyChanged(
177 const dbus::ObjectPath& object_path,
178 const std::string& property_name) {
179 if (object_path != object_path_)
180 return;
181
182 BluetoothAdapterClient::Properties* properties =
183 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
184 GetProperties(object_path_);
185
186 if (property_name == properties->powered.name())
187 PoweredChanged(properties->powered.value());
188 else if (property_name == properties->discovering.name())
189 DiscoveringChanged(properties->discovering.value());
190 }
191
DeviceAdded(const dbus::ObjectPath & object_path)192 void BluetoothAdapterChromeOS::DeviceAdded(
193 const dbus::ObjectPath& object_path) {
194 BluetoothDeviceClient::Properties* properties =
195 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
196 GetProperties(object_path);
197 if (properties->adapter.value() != object_path_)
198 return;
199
200 BluetoothDeviceChromeOS* device_chromeos =
201 new BluetoothDeviceChromeOS(this, object_path);
202 DCHECK(devices_.find(device_chromeos->GetAddress()) == devices_.end());
203
204 devices_[device_chromeos->GetAddress()] = device_chromeos;
205
206 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
207 DeviceAdded(this, device_chromeos));
208 }
209
DeviceRemoved(const dbus::ObjectPath & object_path)210 void BluetoothAdapterChromeOS::DeviceRemoved(
211 const dbus::ObjectPath& object_path) {
212 for (DevicesMap::iterator iter = devices_.begin();
213 iter != devices_.end(); ++iter) {
214 BluetoothDeviceChromeOS* device_chromeos =
215 static_cast<BluetoothDeviceChromeOS*>(iter->second);
216 if (device_chromeos->object_path() == object_path) {
217 devices_.erase(iter);
218
219 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
220 DeviceRemoved(this, device_chromeos));
221 delete device_chromeos;
222 return;
223 }
224 }
225 }
226
DevicePropertyChanged(const dbus::ObjectPath & object_path,const std::string & property_name)227 void BluetoothAdapterChromeOS::DevicePropertyChanged(
228 const dbus::ObjectPath& object_path,
229 const std::string& property_name) {
230 BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path);
231 if (!device_chromeos)
232 return;
233
234 BluetoothDeviceClient::Properties* properties =
235 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
236 GetProperties(object_path);
237
238 if (property_name == properties->bluetooth_class.name() ||
239 property_name == properties->address.name() ||
240 property_name == properties->alias.name() ||
241 property_name == properties->paired.name() ||
242 property_name == properties->trusted.name() ||
243 property_name == properties->connected.name() ||
244 property_name == properties->uuids.name())
245 NotifyDeviceChanged(device_chromeos);
246
247 // UMA connection counting
248 if (property_name == properties->connected.name()) {
249 int count = 0;
250
251 for (DevicesMap::iterator iter = devices_.begin();
252 iter != devices_.end(); ++iter) {
253 if (iter->second->IsPaired() && iter->second->IsConnected())
254 ++count;
255 }
256
257 UMA_HISTOGRAM_COUNTS_100("Bluetooth.ConnectedDeviceCount", count);
258 }
259 }
260
InputPropertyChanged(const dbus::ObjectPath & object_path,const std::string & property_name)261 void BluetoothAdapterChromeOS::InputPropertyChanged(
262 const dbus::ObjectPath& object_path,
263 const std::string& property_name) {
264 BluetoothDeviceChromeOS* device_chromeos = GetDeviceWithPath(object_path);
265 if (!device_chromeos)
266 return;
267
268 BluetoothInputClient::Properties* properties =
269 DBusThreadManager::Get()->GetBluetoothInputClient()->
270 GetProperties(object_path);
271
272 // Properties structure can be removed, which triggers a change in the
273 // BluetoothDevice::IsConnectable() property, as does a change in the
274 // actual reconnect_mode property.
275 if (!properties ||
276 property_name == properties->reconnect_mode.name())
277 NotifyDeviceChanged(device_chromeos);
278 }
279
280 BluetoothDeviceChromeOS*
GetDeviceWithPath(const dbus::ObjectPath & object_path)281 BluetoothAdapterChromeOS::GetDeviceWithPath(
282 const dbus::ObjectPath& object_path) {
283 for (DevicesMap::iterator iter = devices_.begin();
284 iter != devices_.end(); ++iter) {
285 BluetoothDeviceChromeOS* device_chromeos =
286 static_cast<BluetoothDeviceChromeOS*>(iter->second);
287 if (device_chromeos->object_path() == object_path)
288 return device_chromeos;
289 }
290
291 return NULL;
292 }
293
SetAdapter(const dbus::ObjectPath & object_path)294 void BluetoothAdapterChromeOS::SetAdapter(const dbus::ObjectPath& object_path) {
295 DCHECK(!IsPresent());
296 object_path_ = object_path;
297
298 VLOG(1) << object_path_.value() << ": using adapter.";
299
300 SetAdapterName();
301
302 BluetoothAdapterClient::Properties* properties =
303 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
304 GetProperties(object_path_);
305
306 PresentChanged(true);
307
308 if (properties->powered.value())
309 PoweredChanged(true);
310 if (properties->discovering.value())
311 DiscoveringChanged(true);
312
313 std::vector<dbus::ObjectPath> device_paths =
314 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
315 GetDevicesForAdapter(object_path_);
316
317 for (std::vector<dbus::ObjectPath>::iterator iter = device_paths.begin();
318 iter != device_paths.end(); ++iter) {
319 BluetoothDeviceChromeOS* device_chromeos =
320 new BluetoothDeviceChromeOS(this, *iter);
321
322 devices_[device_chromeos->GetAddress()] = device_chromeos;
323
324 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
325 DeviceAdded(this, device_chromeos));
326 }
327 }
328
SetAdapterName()329 void BluetoothAdapterChromeOS::SetAdapterName() {
330 std::string board = base::SysInfo::GetLsbReleaseBoard();
331 std::string alias;
332 if (board.substr(0, 6) == "stumpy") {
333 alias = "Chromebox";
334 } else if (board.substr(0, 4) == "link") {
335 alias = "Chromebook Pixel";
336 } else {
337 alias = "Chromebook";
338 }
339
340 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
341 GetProperties(object_path_)->alias.Set(
342 alias,
343 base::Bind(&BluetoothAdapterChromeOS::OnSetAlias,
344 weak_ptr_factory_.GetWeakPtr()));
345 }
346
OnSetAlias(bool success)347 void BluetoothAdapterChromeOS::OnSetAlias(bool success) {
348 LOG_IF(WARNING, !success) << object_path_.value()
349 << ": Failed to set adapter alias";
350 }
351
RemoveAdapter()352 void BluetoothAdapterChromeOS::RemoveAdapter() {
353 DCHECK(IsPresent());
354 VLOG(1) << object_path_.value() << ": adapter removed.";
355
356 BluetoothAdapterClient::Properties* properties =
357 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
358 GetProperties(object_path_);
359
360 object_path_ = dbus::ObjectPath("");
361
362 if (properties->powered.value())
363 PoweredChanged(false);
364 if (properties->discovering.value())
365 DiscoveringChanged(false);
366
367 // Copy the devices list here and clear the original so that when we
368 // send DeviceRemoved(), GetDevices() returns no devices.
369 DevicesMap devices = devices_;
370 devices_.clear();
371
372 for (DevicesMap::iterator iter = devices.begin();
373 iter != devices.end(); ++iter) {
374 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
375 DeviceRemoved(this, iter->second));
376 delete iter->second;
377 }
378
379 PresentChanged(false);
380 }
381
PoweredChanged(bool powered)382 void BluetoothAdapterChromeOS::PoweredChanged(bool powered) {
383 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
384 AdapterPoweredChanged(this, powered));
385 }
386
DiscoveringChanged(bool discovering)387 void BluetoothAdapterChromeOS::DiscoveringChanged(
388 bool discovering) {
389 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
390 AdapterDiscoveringChanged(this, discovering));
391 }
392
PresentChanged(bool present)393 void BluetoothAdapterChromeOS::PresentChanged(bool present) {
394 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
395 AdapterPresentChanged(this, present));
396 }
397
NotifyDeviceChanged(BluetoothDeviceChromeOS * device)398 void BluetoothAdapterChromeOS::NotifyDeviceChanged(
399 BluetoothDeviceChromeOS* device) {
400 DCHECK(device->adapter_ == this);
401
402 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_,
403 DeviceChanged(this, device));
404 }
405
OnSetPowered(const base::Closure & callback,const ErrorCallback & error_callback,bool success)406 void BluetoothAdapterChromeOS::OnSetPowered(const base::Closure& callback,
407 const ErrorCallback& error_callback,
408 bool success) {
409 if (success)
410 callback.Run();
411 else
412 error_callback.Run();
413 }
414
OnStartDiscovery(const base::Closure & callback)415 void BluetoothAdapterChromeOS::OnStartDiscovery(const base::Closure& callback) {
416 callback.Run();
417 }
418
OnStartDiscoveryError(const ErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)419 void BluetoothAdapterChromeOS::OnStartDiscoveryError(
420 const ErrorCallback& error_callback,
421 const std::string& error_name,
422 const std::string& error_message) {
423 LOG(WARNING) << object_path_.value() << ": Failed to start discovery: "
424 << error_name << ": " << error_message;
425 error_callback.Run();
426 }
427
OnStopDiscovery(const base::Closure & callback)428 void BluetoothAdapterChromeOS::OnStopDiscovery(const base::Closure& callback) {
429 callback.Run();
430 }
431
OnStopDiscoveryError(const ErrorCallback & error_callback,const std::string & error_name,const std::string & error_message)432 void BluetoothAdapterChromeOS::OnStopDiscoveryError(
433 const ErrorCallback& error_callback,
434 const std::string& error_name,
435 const std::string& error_message) {
436 LOG(WARNING) << object_path_.value() << ": Failed to stop discovery: "
437 << error_name << ": " << error_message;
438 error_callback.Run();
439 }
440
441 } // namespace chromeos
442