Create a new-installs-only uniformity trial.
[chromium-blink-merge.git] / device / bluetooth / bluetooth_device_chromeos.cc
blob2a09cac150fee814232e94c1ac8e062947b2db85
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
3 // found in the LICENSE file.
5 #include "device/bluetooth/bluetooth_device_chromeos.h"
7 #include <map>
8 #include <string>
9 #include <vector>
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_vector.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/string16.h"
17 #include "base/string_util.h"
18 #include "base/values.h"
19 #include "chromeos/dbus/bluetooth_adapter_client.h"
20 #include "chromeos/dbus/bluetooth_agent_service_provider.h"
21 #include "chromeos/dbus/bluetooth_device_client.h"
22 #include "chromeos/dbus/bluetooth_input_client.h"
23 #include "chromeos/dbus/bluetooth_out_of_band_client.h"
24 #include "chromeos/dbus/dbus_thread_manager.h"
25 #include "chromeos/dbus/introspectable_client.h"
26 #include "dbus/bus.h"
27 #include "dbus/object_path.h"
28 #include "device/bluetooth/bluetooth_adapter_chromeos.h"
29 #include "device/bluetooth/bluetooth_out_of_band_pairing_data.h"
30 #include "device/bluetooth/bluetooth_service_record.h"
31 #include "device/bluetooth/bluetooth_socket_chromeos.h"
32 #include "device/bluetooth/bluetooth_utils.h"
33 #include "third_party/cros_system_api/dbus/service_constants.h"
35 using device::BluetoothDevice;
36 using device::BluetoothOutOfBandPairingData;
37 using device::BluetoothServiceRecord;
38 using device::BluetoothSocket;
40 namespace chromeos {
42 BluetoothDeviceChromeOs::BluetoothDeviceChromeOs(
43 BluetoothAdapterChromeOs* adapter)
44 : BluetoothDevice(),
45 adapter_(adapter),
46 pairing_delegate_(NULL),
47 connecting_applications_counter_(0),
48 weak_ptr_factory_(this) {
51 BluetoothDeviceChromeOs::~BluetoothDeviceChromeOs() {
54 bool BluetoothDeviceChromeOs::IsPaired() const {
55 return !object_path_.value().empty();
58 const BluetoothDevice::ServiceList&
59 BluetoothDeviceChromeOs::GetServices() const {
60 return service_uuids_;
63 void BluetoothDeviceChromeOs::GetServiceRecords(
64 const ServiceRecordsCallback& callback,
65 const ErrorCallback& error_callback) {
66 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
67 DiscoverServices(
68 object_path_,
69 "", // empty pattern to browse all services
70 base::Bind(&BluetoothDeviceChromeOs::CollectServiceRecordsCallback,
71 weak_ptr_factory_.GetWeakPtr(),
72 callback,
73 error_callback));
76 bool BluetoothDeviceChromeOs::ProvidesServiceWithUUID(
77 const std::string& uuid) const {
78 const BluetoothDevice::ServiceList& services = GetServices();
79 for (BluetoothDevice::ServiceList::const_iterator iter = services.begin();
80 iter != services.end();
81 ++iter) {
82 if (device::bluetooth_utils::CanonicalUuid(*iter) == uuid)
83 return true;
85 return false;
88 void BluetoothDeviceChromeOs::ProvidesServiceWithName(
89 const std::string& name,
90 const ProvidesServiceCallback& callback) {
91 GetServiceRecords(
92 base::Bind(&BluetoothDeviceChromeOs::SearchServicesForNameCallback,
93 weak_ptr_factory_.GetWeakPtr(),
94 name,
95 callback),
96 base::Bind(&BluetoothDeviceChromeOs::SearchServicesForNameErrorCallback,
97 weak_ptr_factory_.GetWeakPtr(),
98 callback));
101 bool BluetoothDeviceChromeOs::ExpectingPinCode() const {
102 return !pincode_callback_.is_null();
105 bool BluetoothDeviceChromeOs::ExpectingPasskey() const {
106 return !passkey_callback_.is_null();
109 bool BluetoothDeviceChromeOs::ExpectingConfirmation() const {
110 return !confirmation_callback_.is_null();
113 void BluetoothDeviceChromeOs::Connect(PairingDelegate* pairing_delegate,
114 const base::Closure& callback,
115 const ErrorCallback& error_callback) {
116 if (IsPaired() || IsBonded() || IsConnected()) {
117 // Connection to already paired or connected device.
118 ConnectApplications(callback, error_callback);
120 } else if (!pairing_delegate) {
121 // No pairing delegate supplied, initiate low-security connection only.
122 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
123 CreateDevice(adapter_->object_path_,
124 address_,
125 base::Bind(&BluetoothDeviceChromeOs::ConnectCallback,
126 weak_ptr_factory_.GetWeakPtr(),
127 callback,
128 error_callback),
129 base::Bind(&BluetoothDeviceChromeOs::ConnectErrorCallback,
130 weak_ptr_factory_.GetWeakPtr(),
131 error_callback));
132 } else {
133 // Initiate high-security connection with pairing.
134 DCHECK(!pairing_delegate_);
135 pairing_delegate_ = pairing_delegate;
137 // The agent path is relatively meaningless, we use the device address
138 // to generate it as we only support one pairing attempt at a time for
139 // a given bluetooth device.
140 DCHECK(agent_.get() == NULL);
142 std::string agent_path_basename;
143 ReplaceChars(address_, ":", "_", &agent_path_basename);
144 dbus::ObjectPath agent_path("/org/chromium/bluetooth_agent/" +
145 agent_path_basename);
147 dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
148 if (system_bus) {
149 agent_.reset(BluetoothAgentServiceProvider::Create(system_bus,
150 agent_path,
151 this));
152 } else {
153 agent_.reset(NULL);
156 DVLOG(1) << "Pairing: " << address_;
157 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
158 CreatePairedDevice(
159 adapter_->object_path_,
160 address_,
161 agent_path,
162 bluetooth_agent::kDisplayYesNoCapability,
163 base::Bind(&BluetoothDeviceChromeOs::ConnectCallback,
164 weak_ptr_factory_.GetWeakPtr(),
165 callback,
166 error_callback),
167 base::Bind(&BluetoothDeviceChromeOs::ConnectErrorCallback,
168 weak_ptr_factory_.GetWeakPtr(),
169 error_callback));
173 void BluetoothDeviceChromeOs::SetPinCode(const std::string& pincode) {
174 if (!agent_.get() || pincode_callback_.is_null())
175 return;
177 pincode_callback_.Run(SUCCESS, pincode);
178 pincode_callback_.Reset();
181 void BluetoothDeviceChromeOs::SetPasskey(uint32 passkey) {
182 if (!agent_.get() || passkey_callback_.is_null())
183 return;
185 passkey_callback_.Run(SUCCESS, passkey);
186 passkey_callback_.Reset();
189 void BluetoothDeviceChromeOs::ConfirmPairing() {
190 if (!agent_.get() || confirmation_callback_.is_null())
191 return;
193 confirmation_callback_.Run(SUCCESS);
194 confirmation_callback_.Reset();
197 void BluetoothDeviceChromeOs::RejectPairing() {
198 if (!agent_.get())
199 return;
201 if (!pincode_callback_.is_null()) {
202 pincode_callback_.Run(REJECTED, "");
203 pincode_callback_.Reset();
205 if (!passkey_callback_.is_null()) {
206 passkey_callback_.Run(REJECTED, 0);
207 passkey_callback_.Reset();
209 if (!confirmation_callback_.is_null()) {
210 confirmation_callback_.Run(REJECTED);
211 confirmation_callback_.Reset();
215 void BluetoothDeviceChromeOs::CancelPairing() {
216 if (!agent_.get())
217 return;
219 if (!pincode_callback_.is_null()) {
220 pincode_callback_.Run(CANCELLED, "");
221 pincode_callback_.Reset();
223 if (!passkey_callback_.is_null()) {
224 passkey_callback_.Run(CANCELLED, 0);
225 passkey_callback_.Reset();
227 if (!confirmation_callback_.is_null()) {
228 confirmation_callback_.Run(CANCELLED);
229 confirmation_callback_.Reset();
233 void BluetoothDeviceChromeOs::Disconnect(const base::Closure& callback,
234 const ErrorCallback& error_callback) {
235 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
236 Disconnect(object_path_,
237 base::Bind(&BluetoothDeviceChromeOs::DisconnectCallback,
238 weak_ptr_factory_.GetWeakPtr(),
239 callback,
240 error_callback));
244 void BluetoothDeviceChromeOs::Forget(const ErrorCallback& error_callback) {
245 DBusThreadManager::Get()->GetBluetoothAdapterClient()->
246 RemoveDevice(adapter_->object_path_,
247 object_path_,
248 base::Bind(&BluetoothDeviceChromeOs::ForgetCallback,
249 weak_ptr_factory_.GetWeakPtr(),
250 error_callback));
253 void BluetoothDeviceChromeOs::ConnectToService(const std::string& service_uuid,
254 const SocketCallback& callback) {
255 GetServiceRecords(
256 base::Bind(&BluetoothDeviceChromeOs::GetServiceRecordsForConnectCallback,
257 weak_ptr_factory_.GetWeakPtr(),
258 service_uuid,
259 callback),
260 base::Bind(
261 &BluetoothDeviceChromeOs::GetServiceRecordsForConnectErrorCallback,
262 weak_ptr_factory_.GetWeakPtr(),
263 callback));
266 void BluetoothDeviceChromeOs::SetOutOfBandPairingData(
267 const BluetoothOutOfBandPairingData& data,
268 const base::Closure& callback,
269 const ErrorCallback& error_callback) {
270 DBusThreadManager::Get()->GetBluetoothOutOfBandClient()->
271 AddRemoteData(
272 object_path_,
273 address(),
274 data,
275 base::Bind(&BluetoothDeviceChromeOs::OnRemoteDataCallback,
276 weak_ptr_factory_.GetWeakPtr(),
277 callback,
278 error_callback));
281 void BluetoothDeviceChromeOs::ClearOutOfBandPairingData(
282 const base::Closure& callback,
283 const ErrorCallback& error_callback) {
284 DBusThreadManager::Get()->GetBluetoothOutOfBandClient()->
285 RemoveRemoteData(
286 object_path_,
287 address(),
288 base::Bind(&BluetoothDeviceChromeOs::OnRemoteDataCallback,
289 weak_ptr_factory_.GetWeakPtr(),
290 callback,
291 error_callback));
294 void BluetoothDeviceChromeOs::SetObjectPath(
295 const dbus::ObjectPath& object_path) {
296 DCHECK(object_path_ == dbus::ObjectPath(""));
297 object_path_ = object_path;
300 void BluetoothDeviceChromeOs::RemoveObjectPath() {
301 DCHECK(object_path_ != dbus::ObjectPath(""));
302 object_path_ = dbus::ObjectPath("");
305 void BluetoothDeviceChromeOs::Update(
306 const BluetoothDeviceClient::Properties* properties,
307 bool update_state) {
308 std::string address = properties->address.value();
309 std::string name = properties->name.value();
310 uint32 bluetooth_class = properties->bluetooth_class.value();
311 const std::vector<std::string>& uuids = properties->uuids.value();
313 if (!address.empty())
314 address_ = address;
315 if (!name.empty())
316 name_ = name;
317 if (bluetooth_class)
318 bluetooth_class_ = bluetooth_class;
319 if (!uuids.empty()) {
320 service_uuids_.clear();
321 service_uuids_.assign(uuids.begin(), uuids.end());
324 if (update_state) {
325 // BlueZ uses paired to mean link keys exchanged, whereas the Bluetooth
326 // spec refers to this as bonded. Use the spec name for our interface.
327 bonded_ = properties->paired.value();
328 connected_ = properties->connected.value();
332 void BluetoothDeviceChromeOs::ConnectCallback(
333 const base::Closure& callback,
334 const ErrorCallback& error_callback,
335 const dbus::ObjectPath& device_path) {
336 DVLOG(1) << "Connection successful: " << device_path.value();
337 if (object_path_.value().empty()) {
338 object_path_ = device_path;
339 } else {
340 LOG_IF(WARNING, object_path_ != device_path)
341 << "Conflicting device paths for objects, result gave: "
342 << device_path.value() << " but signal gave: "
343 << object_path_.value();
346 // Mark the device trusted so it can connect to us automatically, and
347 // we can connect after rebooting. This information is part of the
348 // pairing information of the device, and is unique to the combination
349 // of our bluetooth address and the device's bluetooth address. A
350 // different host needs a new pairing, so it's not useful to sync.
351 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
352 GetProperties(object_path_)->trusted.Set(
353 true,
354 base::Bind(&BluetoothDeviceChromeOs::OnSetTrusted,
355 weak_ptr_factory_.GetWeakPtr(),
356 callback,
357 error_callback));
359 // Connect application-layer protocols.
360 ConnectApplications(callback, error_callback);
363 void BluetoothDeviceChromeOs::ConnectErrorCallback(
364 const ErrorCallback& error_callback,
365 const std::string& error_name,
366 const std::string& error_message) {
367 LOG(WARNING) << "Connection failed: " << address_
368 << ": " << error_name << ": " << error_message;
369 error_callback.Run();
372 void BluetoothDeviceChromeOs::CollectServiceRecordsCallback(
373 const ServiceRecordsCallback& callback,
374 const ErrorCallback& error_callback,
375 const dbus::ObjectPath& device_path,
376 const BluetoothDeviceClient::ServiceMap& service_map,
377 bool success) {
378 if (!success) {
379 error_callback.Run();
380 return;
383 ScopedVector<BluetoothServiceRecord> records;
384 for (BluetoothDeviceClient::ServiceMap::const_iterator i =
385 service_map.begin(); i != service_map.end(); ++i) {
386 records.push_back(
387 new BluetoothServiceRecord(address(), i->second));
389 callback.Run(records);
392 void BluetoothDeviceChromeOs::OnSetTrusted(const base::Closure& callback,
393 const ErrorCallback& error_callback,
394 bool success) {
395 if (success) {
396 callback.Run();
397 } else {
398 LOG(WARNING) << "Failed to set device as trusted: " << address_;
399 error_callback.Run();
403 void BluetoothDeviceChromeOs::ConnectApplications(
404 const base::Closure& callback,
405 const ErrorCallback& error_callback) {
406 // Introspect the device object to determine supported applications.
407 DBusThreadManager::Get()->GetIntrospectableClient()->
408 Introspect(bluetooth_device::kBluetoothDeviceServiceName,
409 object_path_,
410 base::Bind(&BluetoothDeviceChromeOs::OnIntrospect,
411 weak_ptr_factory_.GetWeakPtr(),
412 callback,
413 error_callback));
416 void BluetoothDeviceChromeOs::OnIntrospect(const base::Closure& callback,
417 const ErrorCallback& error_callback,
418 const std::string& service_name,
419 const dbus::ObjectPath& device_path,
420 const std::string& xml_data,
421 bool success) {
422 if (!success) {
423 LOG(WARNING) << "Failed to determine supported applications: " << address_;
424 error_callback.Run();
425 return;
428 // The introspection data for the device object may list one or more
429 // additional D-Bus interfaces that BlueZ supports for this particular
430 // device. Send appropraite Connect calls for each of those interfaces
431 // to connect all of the application protocols for this device.
432 std::vector<std::string> interfaces =
433 IntrospectableClient::GetInterfacesFromIntrospectResult(xml_data);
435 DCHECK_EQ(0, connecting_applications_counter_);
436 connecting_applications_counter_ = 0;
437 for (std::vector<std::string>::iterator iter = interfaces.begin();
438 iter != interfaces.end(); ++iter) {
439 if (*iter == bluetooth_input::kBluetoothInputInterface) {
440 connecting_applications_counter_++;
441 // Supports Input interface.
442 DBusThreadManager::Get()->GetBluetoothInputClient()->
443 Connect(object_path_,
444 base::Bind(&BluetoothDeviceChromeOs::OnConnect,
445 weak_ptr_factory_.GetWeakPtr(),
446 callback,
447 *iter),
448 base::Bind(&BluetoothDeviceChromeOs::OnConnectError,
449 weak_ptr_factory_.GetWeakPtr(),
450 error_callback, *iter));
454 // If OnConnect has been called for every call to Connect above, then this
455 // will decrement the counter to -1. In that case, call the callback
456 // directly as it has not been called by any of the OnConnect callbacks.
457 // This is safe because OnIntrospect and OnConnect run on the same thread.
458 connecting_applications_counter_--;
459 if (connecting_applications_counter_ == -1)
460 callback.Run();
463 void BluetoothDeviceChromeOs::OnConnect(const base::Closure& callback,
464 const std::string& interface_name,
465 const dbus::ObjectPath& device_path) {
466 DVLOG(1) << "Application connection successful: " << device_path.value()
467 << ": " << interface_name;
469 connecting_applications_counter_--;
470 // |callback| should only be called once, meaning it cannot be called before
471 // all requests have been started. The extra decrement after all requests
472 // have been started, and the check for -1 instead of 0 below, insure only a
473 // single call to |callback| will occur (provided OnConnect and OnIntrospect
474 // run on the same thread, which is true).
475 if (connecting_applications_counter_ == -1) {
476 connecting_applications_counter_ = 0;
477 callback.Run();
481 void BluetoothDeviceChromeOs::OnConnectError(
482 const ErrorCallback& error_callback,
483 const std::string& interface_name,
484 const dbus::ObjectPath& device_path,
485 const std::string& error_name,
486 const std::string& error_message) {
487 LOG(WARNING) << "Connection failed: " << address_ << ": " << interface_name
488 << ": " << error_name << ": " << error_message;
489 error_callback.Run();
492 void BluetoothDeviceChromeOs::DisconnectCallback(
493 const base::Closure& callback,
494 const ErrorCallback& error_callback,
495 const dbus::ObjectPath& device_path,
496 bool success) {
497 DCHECK(device_path == object_path_);
498 if (success) {
499 DVLOG(1) << "Disconnection successful: " << address_;
500 callback.Run();
501 } else {
502 LOG(WARNING) << "Disconnection failed: " << address_;
503 error_callback.Run();
507 void BluetoothDeviceChromeOs::ForgetCallback(
508 const ErrorCallback& error_callback,
509 const dbus::ObjectPath& adapter_path,
510 bool success) {
511 // It's quite normal that this path never gets called on success; we use a
512 // weak pointer, and bluetoothd might send the DeviceRemoved signal before
513 // the method reply, in which case this object is deleted and the
514 // callback never takes place. Therefore don't do anything here for the
515 // success case.
516 if (!success) {
517 LOG(WARNING) << "Forget failed: " << address_;
518 error_callback.Run();
522 void BluetoothDeviceChromeOs::SearchServicesForNameErrorCallback(
523 const ProvidesServiceCallback& callback) {
524 callback.Run(false);
527 void BluetoothDeviceChromeOs::SearchServicesForNameCallback(
528 const std::string& name,
529 const ProvidesServiceCallback& callback,
530 const ServiceRecordList& list) {
531 for (ServiceRecordList::const_iterator i = list.begin();
532 i != list.end(); ++i) {
533 if ((*i)->name() == name) {
534 callback.Run(true);
535 return;
538 callback.Run(false);
541 void BluetoothDeviceChromeOs::GetServiceRecordsForConnectErrorCallback(
542 const SocketCallback& callback) {
543 callback.Run(NULL);
546 void BluetoothDeviceChromeOs::GetServiceRecordsForConnectCallback(
547 const std::string& service_uuid,
548 const SocketCallback& callback,
549 const ServiceRecordList& list) {
550 for (ServiceRecordList::const_iterator i = list.begin();
551 i != list.end(); ++i) {
552 if ((*i)->uuid() == service_uuid) {
553 // If multiple service records are found, use the first one that works.
554 scoped_refptr<BluetoothSocket> socket(
555 BluetoothSocketChromeOs::CreateBluetoothSocket(**i));
556 if (socket.get() != NULL) {
557 callback.Run(socket);
558 return;
562 callback.Run(NULL);
565 void BluetoothDeviceChromeOs::OnRemoteDataCallback(
566 const base::Closure& callback,
567 const ErrorCallback& error_callback,
568 bool success) {
569 if (success)
570 callback.Run();
571 else
572 error_callback.Run();
575 void BluetoothDeviceChromeOs::DisconnectRequested(
576 const dbus::ObjectPath& object_path) {
577 DCHECK(object_path == object_path_);
580 void BluetoothDeviceChromeOs::Release() {
581 DCHECK(agent_.get());
582 DVLOG(1) << "Release: " << address_;
584 DCHECK(pairing_delegate_);
585 pairing_delegate_->DismissDisplayOrConfirm();
586 pairing_delegate_ = NULL;
588 pincode_callback_.Reset();
589 passkey_callback_.Reset();
590 confirmation_callback_.Reset();
592 agent_.reset();
595 void BluetoothDeviceChromeOs::RequestPinCode(
596 const dbus::ObjectPath& device_path,
597 const PinCodeCallback& callback) {
598 DCHECK(agent_.get());
599 DVLOG(1) << "RequestPinCode: " << device_path.value();
601 DCHECK(pairing_delegate_);
602 DCHECK(pincode_callback_.is_null());
603 pincode_callback_ = callback;
604 pairing_delegate_->RequestPinCode(this);
607 void BluetoothDeviceChromeOs::RequestPasskey(
608 const dbus::ObjectPath& device_path,
609 const PasskeyCallback& callback) {
610 DCHECK(agent_.get());
611 DCHECK(device_path == object_path_);
612 DVLOG(1) << "RequestPasskey: " << device_path.value();
614 DCHECK(pairing_delegate_);
615 DCHECK(passkey_callback_.is_null());
616 passkey_callback_ = callback;
617 pairing_delegate_->RequestPasskey(this);
620 void BluetoothDeviceChromeOs::DisplayPinCode(
621 const dbus::ObjectPath& device_path,
622 const std::string& pincode) {
623 DCHECK(agent_.get());
624 DCHECK(device_path == object_path_);
625 DVLOG(1) << "DisplayPinCode: " << device_path.value() << " " << pincode;
627 DCHECK(pairing_delegate_);
628 pairing_delegate_->DisplayPinCode(this, pincode);
631 void BluetoothDeviceChromeOs::DisplayPasskey(
632 const dbus::ObjectPath& device_path,
633 uint32 passkey) {
634 DCHECK(agent_.get());
635 DCHECK(device_path == object_path_);
636 DVLOG(1) << "DisplayPasskey: " << device_path.value() << " " << passkey;
638 DCHECK(pairing_delegate_);
639 pairing_delegate_->DisplayPasskey(this, passkey);
642 void BluetoothDeviceChromeOs::RequestConfirmation(
643 const dbus::ObjectPath& device_path,
644 uint32 passkey,
645 const ConfirmationCallback& callback) {
646 DCHECK(agent_.get());
647 DCHECK(device_path == object_path_);
648 DVLOG(1) << "RequestConfirmation: " << device_path.value() << " " << passkey;
650 DCHECK(pairing_delegate_);
651 DCHECK(confirmation_callback_.is_null());
652 confirmation_callback_ = callback;
653 pairing_delegate_->ConfirmPasskey(this, passkey);
656 void BluetoothDeviceChromeOs::Authorize(const dbus::ObjectPath& device_path,
657 const std::string& uuid,
658 const ConfirmationCallback& callback) {
659 DCHECK(agent_.get());
660 DCHECK(device_path == object_path_);
661 LOG(WARNING) << "Rejected authorization for service: " << uuid
662 << " requested from device: " << device_path.value();
663 callback.Run(REJECTED);
666 void BluetoothDeviceChromeOs::ConfirmModeChange(
667 Mode mode,
668 const ConfirmationCallback& callback) {
669 DCHECK(agent_.get());
670 LOG(WARNING) << "Rejected adapter-level mode change: " << mode
671 << " made on agent for device: " << address_;
672 callback.Run(REJECTED);
675 void BluetoothDeviceChromeOs::Cancel() {
676 DCHECK(agent_.get());
677 DVLOG(1) << "Cancel: " << address_;
679 DCHECK(pairing_delegate_);
680 pairing_delegate_->DismissDisplayOrConfirm();
684 // static
685 BluetoothDeviceChromeOs* BluetoothDeviceChromeOs::Create(
686 BluetoothAdapterChromeOs* adapter) {
687 return new BluetoothDeviceChromeOs(adapter);
690 } // namespace chromeos