OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/chromeos/bluetooth/bluetooth_adapter.h" | 5 #include "chrome/browser/chromeos/bluetooth/bluetooth_adapter.h" |
6 | 6 |
7 #include "base/bind.h" | |
8 #include "base/lazy_instance.h" | |
9 #include "base/logging.h" | |
10 #include "base/stl_util.h" | |
11 #include "base/values.h" | |
12 #include "chrome/browser/chromeos/bluetooth/bluetooth_device.h" | 7 #include "chrome/browser/chromeos/bluetooth/bluetooth_device.h" |
13 #include "chromeos/dbus/bluetooth_adapter_client.h" | |
14 #include "chromeos/dbus/bluetooth_device_client.h" | |
15 #include "chromeos/dbus/bluetooth_manager_client.h" | |
16 #include "chromeos/dbus/bluetooth_out_of_band_client.h" | |
17 #include "chromeos/dbus/dbus_thread_manager.h" | |
18 #include "dbus/object_path.h" | |
19 | |
20 namespace { | |
21 | |
22 // Shared default adapter instance, we don't want to keep this class around | |
23 // if nobody is using it so use a WeakPtr and create the object when needed; | |
24 // since Google C++ Style (and clang's static analyzer) forbids us having | |
25 // exit-time destructors we use a leaky lazy instance for it. | |
26 base::LazyInstance<base::WeakPtr<chromeos::BluetoothAdapter> >::Leaky | |
27 default_adapter = LAZY_INSTANCE_INITIALIZER; | |
28 | |
29 } // namespace | |
30 | 8 |
31 namespace chromeos { | 9 namespace chromeos { |
32 | 10 |
33 BluetoothAdapter::BluetoothAdapter() : track_default_(false), | 11 BluetoothAdapter::~BluetoothAdapter() { |
34 powered_(false), | |
35 discovering_(false), | |
36 weak_ptr_factory_(this) { | |
37 DBusThreadManager::Get()->GetBluetoothManagerClient()-> | |
38 AddObserver(this); | |
39 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> | |
40 AddObserver(this); | |
41 DBusThreadManager::Get()->GetBluetoothDeviceClient()-> | |
42 AddObserver(this); | |
43 } | 12 } |
44 | 13 |
45 BluetoothAdapter::~BluetoothAdapter() { | 14 const std::string& BluetoothAdapter::address() const { |
46 DBusThreadManager::Get()->GetBluetoothDeviceClient()-> | 15 return address_; |
47 RemoveObserver(this); | |
48 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> | |
49 RemoveObserver(this); | |
50 DBusThreadManager::Get()->GetBluetoothManagerClient()-> | |
51 RemoveObserver(this); | |
52 | |
53 STLDeleteValues(&devices_); | |
54 } | 16 } |
55 | 17 |
56 void BluetoothAdapter::AddObserver(Observer* observer) { | 18 const std::string& BluetoothAdapter::name() const { |
57 DCHECK(observer); | 19 return name_; |
58 observers_.AddObserver(observer); | |
59 } | |
60 | |
61 void BluetoothAdapter::RemoveObserver(Observer* observer) { | |
62 DCHECK(observer); | |
63 observers_.RemoveObserver(observer); | |
64 } | |
65 | |
66 bool BluetoothAdapter::IsPresent() const { | |
67 return !object_path_.value().empty() && !address_.empty(); | |
68 } | |
69 | |
70 bool BluetoothAdapter::IsPowered() const { | |
71 return powered_; | |
72 } | |
73 | |
74 void BluetoothAdapter::SetPowered(bool powered, | |
75 const base::Closure& callback, | |
76 const ErrorCallback& error_callback) { | |
77 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> | |
78 GetProperties(object_path_)->powered.Set( | |
79 powered, | |
80 base::Bind(&BluetoothAdapter::OnSetPowered, | |
81 weak_ptr_factory_.GetWeakPtr(), | |
82 callback, | |
83 error_callback)); | |
84 } | |
85 | |
86 bool BluetoothAdapter::IsDiscovering() const { | |
87 return discovering_; | |
88 } | |
89 | |
90 void BluetoothAdapter::SetDiscovering(bool discovering, | |
91 const base::Closure& callback, | |
92 const ErrorCallback& error_callback) { | |
93 if (discovering) { | |
94 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> | |
95 StartDiscovery(object_path_, | |
96 base::Bind(&BluetoothAdapter::OnStartDiscovery, | |
97 weak_ptr_factory_.GetWeakPtr(), | |
98 callback, | |
99 error_callback)); | |
100 } else { | |
101 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> | |
102 StopDiscovery(object_path_, | |
103 base::Bind(&BluetoothAdapter::OnStopDiscovery, | |
104 weak_ptr_factory_.GetWeakPtr(), | |
105 callback, | |
106 error_callback)); | |
107 } | |
108 } | 20 } |
109 | 21 |
110 BluetoothAdapter::DeviceList BluetoothAdapter::GetDevices() { | 22 BluetoothAdapter::DeviceList BluetoothAdapter::GetDevices() { |
111 ConstDeviceList const_devices = | 23 ConstDeviceList const_devices = |
112 const_cast<const BluetoothAdapter *>(this)->GetDevices(); | 24 const_cast<const BluetoothAdapter *>(this)->GetDevices(); |
113 | 25 |
114 DeviceList devices; | 26 DeviceList devices; |
115 for (ConstDeviceList::const_iterator i = const_devices.begin(); | 27 for (ConstDeviceList::const_iterator i = const_devices.begin(); |
116 i != const_devices.end(); ++i) | 28 i != const_devices.end(); ++i) |
117 devices.push_back(const_cast<BluetoothDevice *>(*i)); | 29 devices.push_back(const_cast<BluetoothDevice *>(*i)); |
118 | 30 |
119 return devices; | 31 return devices; |
120 } | 32 } |
121 | 33 |
122 BluetoothAdapter::ConstDeviceList BluetoothAdapter::GetDevices() const { | |
123 ConstDeviceList devices; | |
124 for (DevicesMap::const_iterator iter = devices_.begin(); | |
125 iter != devices_.end(); ++iter) | |
126 devices.push_back(iter->second); | |
127 | |
128 return devices; | |
129 } | |
130 | |
131 BluetoothDevice* BluetoothAdapter::GetDevice(const std::string& address) { | |
132 return const_cast<BluetoothDevice *>( | |
133 const_cast<const BluetoothAdapter *>(this)->GetDevice(address)); | |
134 } | |
135 | |
136 const BluetoothDevice* BluetoothAdapter::GetDevice( | |
137 const std::string& address) const { | |
138 DevicesMap::const_iterator iter = devices_.find(address); | |
139 if (iter != devices_.end()) | |
140 return iter->second; | |
141 | |
142 return NULL; | |
143 } | |
144 | |
145 void BluetoothAdapter::ReadLocalOutOfBandPairingData( | |
146 const BluetoothOutOfBandPairingDataCallback& callback, | |
147 const ErrorCallback& error_callback) { | |
148 DBusThreadManager::Get()->GetBluetoothOutOfBandClient()-> | |
149 ReadLocalData(object_path_, | |
150 base::Bind(&BluetoothAdapter::OnReadLocalData, | |
151 weak_ptr_factory_.GetWeakPtr(), | |
152 callback, | |
153 error_callback)); | |
154 } | |
155 | |
156 void BluetoothAdapter::TrackDefaultAdapter() { | |
157 DVLOG(1) << "Tracking default adapter"; | |
158 track_default_ = true; | |
159 DBusThreadManager::Get()->GetBluetoothManagerClient()-> | |
160 DefaultAdapter(base::Bind(&BluetoothAdapter::AdapterCallback, | |
161 weak_ptr_factory_.GetWeakPtr())); | |
162 } | |
163 | |
164 void BluetoothAdapter::FindAdapter(const std::string& address) { | |
165 DVLOG(1) << "Using adapter " << address; | |
166 track_default_ = false; | |
167 DBusThreadManager::Get()->GetBluetoothManagerClient()-> | |
168 FindAdapter(address, | |
169 base::Bind(&BluetoothAdapter::AdapterCallback, | |
170 weak_ptr_factory_.GetWeakPtr())); | |
171 } | |
172 | |
173 void BluetoothAdapter::AdapterCallback(const dbus::ObjectPath& adapter_path, | |
174 bool success) { | |
175 if (success) { | |
176 ChangeAdapter(adapter_path); | |
177 } else if (!object_path_.value().empty()) { | |
178 RemoveAdapter(); | |
179 } | |
180 } | |
181 | |
182 void BluetoothAdapter::DefaultAdapterChanged( | |
183 const dbus::ObjectPath& adapter_path) { | |
184 if (track_default_) | |
185 ChangeAdapter(adapter_path); | |
186 } | |
187 | |
188 void BluetoothAdapter::AdapterRemoved(const dbus::ObjectPath& adapter_path) { | |
189 if (adapter_path == object_path_) | |
190 RemoveAdapter(); | |
191 } | |
192 | |
193 void BluetoothAdapter::ChangeAdapter(const dbus::ObjectPath& adapter_path) { | |
194 if (object_path_.value().empty()) { | |
195 DVLOG(1) << "Adapter path initialized to " << adapter_path.value(); | |
196 } else if (object_path_.value() != adapter_path.value()) { | |
197 DVLOG(1) << "Adapter path changed from " << object_path_.value() | |
198 << " to " << adapter_path.value(); | |
199 | |
200 RemoveAdapter(); | |
201 } else { | |
202 DVLOG(1) << "Adapter address updated"; | |
203 } | |
204 | |
205 object_path_ = adapter_path; | |
206 | |
207 // Update properties to their new values. | |
208 BluetoothAdapterClient::Properties* properties = | |
209 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> | |
210 GetProperties(object_path_); | |
211 | |
212 address_ = properties->address.value(); | |
213 name_ = properties->name.value(); | |
214 | |
215 // Delay announcing a new adapter until we have an address. | |
216 if (address_.empty()) { | |
217 DVLOG(1) << "Adapter address not yet known"; | |
218 return; | |
219 } | |
220 | |
221 PoweredChanged(properties->powered.value()); | |
222 DiscoveringChanged(properties->discovering.value()); | |
223 DevicesChanged(properties->devices.value()); | |
224 | |
225 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
226 AdapterPresentChanged(this, true)); | |
227 } | |
228 | |
229 void BluetoothAdapter::RemoveAdapter() { | |
230 const bool adapter_was_present = IsPresent(); | |
231 | |
232 DVLOG(1) << "Adapter lost."; | |
233 PoweredChanged(false); | |
234 DiscoveringChanged(false); | |
235 ClearDevices(); | |
236 | |
237 object_path_ = dbus::ObjectPath(""); | |
238 address_.clear(); | |
239 name_.clear(); | |
240 | |
241 if (adapter_was_present) | |
242 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
243 AdapterPresentChanged(this, false)); | |
244 } | |
245 | |
246 void BluetoothAdapter::OnSetPowered(const base::Closure& callback, | |
247 const ErrorCallback& error_callback, | |
248 bool success) { | |
249 if (success) | |
250 callback.Run(); | |
251 else | |
252 error_callback.Run(); | |
253 } | |
254 | |
255 void BluetoothAdapter::PoweredChanged(bool powered) { | |
256 if (powered == powered_) | |
257 return; | |
258 | |
259 powered_ = powered; | |
260 | |
261 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
262 AdapterPoweredChanged(this, powered_)); | |
263 } | |
264 | |
265 void BluetoothAdapter::OnStartDiscovery(const base::Closure& callback, | |
266 const ErrorCallback& error_callback, | |
267 const dbus::ObjectPath& adapter_path, | |
268 bool success) { | |
269 if (success) { | |
270 DVLOG(1) << object_path_.value() << ": started discovery."; | |
271 | |
272 // Clear devices found in previous discovery attempts | |
273 ClearDiscoveredDevices(); | |
274 callback.Run(); | |
275 } else { | |
276 // TODO(keybuk): in future, don't run the callback if the error was just | |
277 // that we were already discovering. | |
278 error_callback.Run(); | |
279 } | |
280 } | |
281 | |
282 void BluetoothAdapter::OnStopDiscovery(const base::Closure& callback, | |
283 const ErrorCallback& error_callback, | |
284 const dbus::ObjectPath& adapter_path, | |
285 bool success) { | |
286 if (success) { | |
287 DVLOG(1) << object_path_.value() << ": stopped discovery."; | |
288 callback.Run(); | |
289 // Leave found devices available for perusing. | |
290 } else { | |
291 // TODO(keybuk): in future, don't run the callback if the error was just | |
292 // that we weren't discovering. | |
293 error_callback.Run(); | |
294 } | |
295 } | |
296 | |
297 void BluetoothAdapter::DiscoveringChanged(bool discovering) { | |
298 if (discovering == discovering_) | |
299 return; | |
300 | |
301 discovering_ = discovering; | |
302 | |
303 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
304 AdapterDiscoveringChanged(this, discovering_)); | |
305 } | |
306 | |
307 void BluetoothAdapter::OnReadLocalData( | |
308 const BluetoothOutOfBandPairingDataCallback& callback, | |
309 const ErrorCallback& error_callback, | |
310 const BluetoothOutOfBandPairingData& data, | |
311 bool success) { | |
312 if (success) | |
313 callback.Run(data); | |
314 else | |
315 error_callback.Run(); | |
316 } | |
317 | |
318 void BluetoothAdapter::AdapterPropertyChanged( | |
319 const dbus::ObjectPath& adapter_path, | |
320 const std::string& property_name) { | |
321 if (adapter_path != object_path_) | |
322 return; | |
323 | |
324 BluetoothAdapterClient::Properties* properties = | |
325 DBusThreadManager::Get()->GetBluetoothAdapterClient()-> | |
326 GetProperties(object_path_); | |
327 | |
328 if (property_name == properties->address.name()) { | |
329 ChangeAdapter(object_path_); | |
330 | |
331 } else if (!address_.empty()) { | |
332 if (property_name == properties->powered.name()) { | |
333 PoweredChanged(properties->powered.value()); | |
334 | |
335 } else if (property_name == properties->discovering.name()) { | |
336 DiscoveringChanged(properties->discovering.value()); | |
337 | |
338 } else if (property_name == properties->devices.name()) { | |
339 DevicesChanged(properties->devices.value()); | |
340 | |
341 } else if (property_name == properties->name.name()) { | |
342 name_ = properties->name.value(); | |
343 | |
344 } | |
345 } | |
346 } | |
347 | |
348 void BluetoothAdapter::DevicePropertyChanged( | |
349 const dbus::ObjectPath& device_path, | |
350 const std::string& property_name) { | |
351 UpdateDevice(device_path); | |
352 } | |
353 | |
354 void BluetoothAdapter::UpdateDevice(const dbus::ObjectPath& device_path) { | |
355 BluetoothDeviceClient::Properties* properties = | |
356 DBusThreadManager::Get()->GetBluetoothDeviceClient()-> | |
357 GetProperties(device_path); | |
358 | |
359 // When we first see a device, we may not know the address yet and need to | |
360 // wait for the DevicePropertyChanged signal before adding the device. | |
361 const std::string address = properties->address.value(); | |
362 if (address.empty()) | |
363 return; | |
364 | |
365 // The device may be already known to us, either because this is an update | |
366 // to properties, or the device going from discovered to connected and | |
367 // pairing gaining an object path in the process. In any case, we want | |
368 // to update the existing object, not create a new one. | |
369 DevicesMap::iterator iter = devices_.find(address); | |
370 BluetoothDevice* device; | |
371 const bool update_device = (iter != devices_.end()); | |
372 if (update_device) { | |
373 device = iter->second; | |
374 } else { | |
375 device = BluetoothDevice::Create(this); | |
376 devices_[address] = device; | |
377 } | |
378 | |
379 const bool was_paired = device->IsPaired(); | |
380 if (!was_paired) { | |
381 DVLOG(1) << "Assigned object path " << device_path.value() << " to device " | |
382 << address; | |
383 device->SetObjectPath(device_path); | |
384 } | |
385 device->Update(properties, true); | |
386 | |
387 // Don't send a duplicate added event for supported devices that were | |
388 // previously visible or for already paired devices, send a changed | |
389 // event instead. We always send one event or the other since we always | |
390 // inform observers about paired devices whether or not they're supported. | |
391 if (update_device && (device->IsSupported() || was_paired)) { | |
392 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
393 DeviceChanged(this, device)); | |
394 } else { | |
395 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
396 DeviceAdded(this, device)); | |
397 } | |
398 } | |
399 | |
400 void BluetoothAdapter::ClearDevices() { | |
401 DevicesMap replace; | |
402 devices_.swap(replace); | |
403 for (DevicesMap::iterator iter = replace.begin(); | |
404 iter != replace.end(); ++iter) { | |
405 BluetoothDevice* device = iter->second; | |
406 if (device->IsSupported() || device->IsPaired()) | |
407 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
408 DeviceRemoved(this, device)); | |
409 | |
410 delete device; | |
411 } | |
412 } | |
413 | |
414 void BluetoothAdapter::DeviceCreated(const dbus::ObjectPath& adapter_path, | |
415 const dbus::ObjectPath& device_path) { | |
416 if (adapter_path != object_path_) | |
417 return; | |
418 | |
419 UpdateDevice(device_path); | |
420 } | |
421 | |
422 void BluetoothAdapter::DeviceRemoved(const dbus::ObjectPath& adapter_path, | |
423 const dbus::ObjectPath& device_path) { | |
424 if (adapter_path != object_path_) | |
425 return; | |
426 | |
427 DevicesMap::iterator iter = devices_.begin(); | |
428 while (iter != devices_.end()) { | |
429 BluetoothDevice* device = iter->second; | |
430 DevicesMap::iterator temp = iter; | |
431 ++iter; | |
432 | |
433 if (device->object_path_ != device_path) | |
434 continue; | |
435 | |
436 // DeviceRemoved can also be called to indicate a device that is visible | |
437 // during discovery has disconnected, but it is still visible to the | |
438 // adapter, so don't remove in that case and only clear the object path. | |
439 if (!device->IsVisible()) { | |
440 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
441 DeviceRemoved(this, device)); | |
442 | |
443 DVLOG(1) << "Removed device " << device->address(); | |
444 | |
445 delete device; | |
446 devices_.erase(temp); | |
447 } else { | |
448 DVLOG(1) << "Removed object path from device " << device->address(); | |
449 device->RemoveObjectPath(); | |
450 | |
451 // If the device is not supported then we want to act as if it was | |
452 // removed, even though it is still visible to the adapter. | |
453 if (!device->IsSupported()) { | |
454 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
455 DeviceRemoved(this, device)); | |
456 } else { | |
457 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
458 DeviceChanged(this, device)); | |
459 } | |
460 } | |
461 } | |
462 } | |
463 | |
464 void BluetoothAdapter::DevicesChanged( | |
465 const std::vector<dbus::ObjectPath>& devices) { | |
466 for (std::vector<dbus::ObjectPath>::const_iterator iter = | |
467 devices.begin(); iter != devices.end(); ++iter) | |
468 UpdateDevice(*iter); | |
469 } | |
470 | |
471 void BluetoothAdapter::ClearDiscoveredDevices() { | |
472 DevicesMap::iterator iter = devices_.begin(); | |
473 while (iter != devices_.end()) { | |
474 BluetoothDevice* device = iter->second; | |
475 DevicesMap::iterator temp = iter; | |
476 ++iter; | |
477 | |
478 if (!device->IsPaired()) { | |
479 if (device->IsSupported()) | |
480 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
481 DeviceRemoved(this, device)); | |
482 | |
483 delete device; | |
484 devices_.erase(temp); | |
485 } | |
486 } | |
487 } | |
488 | |
489 void BluetoothAdapter::DeviceFound( | |
490 const dbus::ObjectPath& adapter_path, const std::string& address, | |
491 const BluetoothDeviceClient::Properties& properties) { | |
492 if (adapter_path != object_path_) | |
493 return; | |
494 | |
495 // DeviceFound can also be called to indicate that a device we've | |
496 // paired with is now visible to the adapter during discovery, in which | |
497 // case we want to update the existing object, not create a new one. | |
498 BluetoothDevice* device; | |
499 DevicesMap::iterator iter = devices_.find(address); | |
500 const bool update_device = (iter != devices_.end()); | |
501 if (update_device) { | |
502 device = iter->second; | |
503 } else { | |
504 device = BluetoothDevice::Create(this); | |
505 devices_[address] = device; | |
506 } | |
507 | |
508 DVLOG(1) << "Device " << address << " is visible to the adapter"; | |
509 device->SetVisible(true); | |
510 device->Update(&properties, false); | |
511 | |
512 // Don't send a duplicated added event for duplicate signals for supported | |
513 // devices that were previously visible (should never happen) or for already | |
514 // paired devices, send a changed event instead. We do not inform observers | |
515 // if we find or update an unconnected and unsupported device. | |
516 if (update_device && (device->IsSupported() || device->IsPaired())) { | |
517 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
518 DeviceChanged(this, device)); | |
519 } else if (device->IsSupported()) { | |
520 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
521 DeviceAdded(this, device)); | |
522 } | |
523 } | |
524 | |
525 void BluetoothAdapter::DeviceDisappeared(const dbus::ObjectPath& adapter_path, | |
526 const std::string& address) { | |
527 if (adapter_path != object_path_) | |
528 return; | |
529 | |
530 DevicesMap::iterator iter = devices_.find(address); | |
531 if (iter == devices_.end()) | |
532 return; | |
533 | |
534 BluetoothDevice* device = iter->second; | |
535 | |
536 // DeviceDisappeared can also be called to indicate that a device we've | |
537 // paired with is no longer visible to the adapter, so don't remove | |
538 // in that case and only clear the visible flag. | |
539 if (!device->IsPaired()) { | |
540 if (device->IsSupported()) | |
541 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
542 DeviceRemoved(this, device)); | |
543 | |
544 DVLOG(1) << "Discovered device " << device->address() | |
545 << " is no longer visible to the adapter"; | |
546 | |
547 delete device; | |
548 devices_.erase(iter); | |
549 } else { | |
550 DVLOG(1) << "Paired device " << device->address() | |
551 << " is no longer visible to the adapter"; | |
552 device->SetVisible(false); | |
553 | |
554 FOR_EACH_OBSERVER(BluetoothAdapter::Observer, observers_, | |
555 DeviceChanged(this, device)); | |
556 } | |
557 } | |
558 | |
559 | |
560 // static | |
561 scoped_refptr<BluetoothAdapter> BluetoothAdapter::DefaultAdapter() { | |
562 if (!default_adapter.Get().get()) { | |
563 BluetoothAdapter* new_adapter = new BluetoothAdapter; | |
564 default_adapter.Get() = new_adapter->weak_ptr_factory_.GetWeakPtr(); | |
565 default_adapter.Get()->TrackDefaultAdapter(); | |
566 } | |
567 | |
568 return scoped_refptr<BluetoothAdapter>(default_adapter.Get()); | |
569 } | |
570 | |
571 // static | |
572 BluetoothAdapter* BluetoothAdapter::Create(const std::string& address) { | |
573 BluetoothAdapter* adapter = new BluetoothAdapter; | |
574 adapter->FindAdapter(address); | |
575 return adapter; | |
576 } | |
577 | |
578 } // namespace chromeos | 34 } // namespace chromeos |
OLD | NEW |