• 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_socket_chromeos.h"
6 
7 #include <queue>
8 #include <string>
9 
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/callback.h"
13 #include "base/logging.h"
14 #include "base/memory/linked_ptr.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/sequenced_task_runner.h"
18 #include "base/strings/string_util.h"
19 #include "base/task_runner_util.h"
20 #include "base/threading/thread_restrictions.h"
21 #include "base/threading/worker_pool.h"
22 #include "chromeos/dbus/bluetooth_device_client.h"
23 #include "chromeos/dbus/bluetooth_profile_manager_client.h"
24 #include "chromeos/dbus/bluetooth_profile_service_provider.h"
25 #include "chromeos/dbus/dbus_thread_manager.h"
26 #include "dbus/bus.h"
27 #include "dbus/file_descriptor.h"
28 #include "dbus/object_path.h"
29 #include "device/bluetooth/bluetooth_adapter.h"
30 #include "device/bluetooth/bluetooth_adapter_chromeos.h"
31 #include "device/bluetooth/bluetooth_device.h"
32 #include "device/bluetooth/bluetooth_device_chromeos.h"
33 #include "device/bluetooth/bluetooth_socket.h"
34 #include "device/bluetooth/bluetooth_socket_net.h"
35 #include "device/bluetooth/bluetooth_socket_thread.h"
36 #include "net/base/ip_endpoint.h"
37 #include "net/base/net_errors.h"
38 #include "third_party/cros_system_api/dbus/service_constants.h"
39 
40 using device::BluetoothAdapter;
41 using device::BluetoothDevice;
42 using device::BluetoothSocketThread;
43 using device::BluetoothUUID;
44 
45 namespace {
46 
47 const char kAcceptFailed[] = "Failed to accept connection.";
48 const char kInvalidUUID[] = "Invalid UUID";
49 const char kSocketNotListening[] = "Socket is not listening.";
50 
51 }  // namespace
52 
53 namespace chromeos {
54 
55 // static
56 scoped_refptr<BluetoothSocketChromeOS>
CreateBluetoothSocket(scoped_refptr<base::SequencedTaskRunner> ui_task_runner,scoped_refptr<BluetoothSocketThread> socket_thread,net::NetLog * net_log,const net::NetLog::Source & source)57 BluetoothSocketChromeOS::CreateBluetoothSocket(
58     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
59     scoped_refptr<BluetoothSocketThread> socket_thread,
60     net::NetLog* net_log,
61     const net::NetLog::Source& source) {
62   DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
63 
64   return make_scoped_refptr(
65       new BluetoothSocketChromeOS(
66           ui_task_runner, socket_thread, net_log, source));
67 }
68 
AcceptRequest()69 BluetoothSocketChromeOS::AcceptRequest::AcceptRequest() {}
70 
~AcceptRequest()71 BluetoothSocketChromeOS::AcceptRequest::~AcceptRequest() {}
72 
ConnectionRequest()73 BluetoothSocketChromeOS::ConnectionRequest::ConnectionRequest()
74     : accepting(false),
75       cancelled(false) {}
76 
~ConnectionRequest()77 BluetoothSocketChromeOS::ConnectionRequest::~ConnectionRequest() {}
78 
BluetoothSocketChromeOS(scoped_refptr<base::SequencedTaskRunner> ui_task_runner,scoped_refptr<BluetoothSocketThread> socket_thread,net::NetLog * net_log,const net::NetLog::Source & source)79 BluetoothSocketChromeOS::BluetoothSocketChromeOS(
80     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
81     scoped_refptr<BluetoothSocketThread> socket_thread,
82     net::NetLog* net_log,
83     const net::NetLog::Source& source)
84     : BluetoothSocketNet(ui_task_runner, socket_thread, net_log, source) {
85 }
86 
~BluetoothSocketChromeOS()87 BluetoothSocketChromeOS::~BluetoothSocketChromeOS() {
88   DCHECK(object_path_.value().empty());
89   DCHECK(profile_.get() == NULL);
90 
91   if (adapter_.get()) {
92     adapter_->RemoveObserver(this);
93     adapter_ = NULL;
94   }
95 }
96 
Connect(const BluetoothDeviceChromeOS * device,const BluetoothUUID & uuid,const base::Closure & success_callback,const ErrorCompletionCallback & error_callback)97 void BluetoothSocketChromeOS::Connect(
98     const BluetoothDeviceChromeOS* device,
99     const BluetoothUUID& uuid,
100     const base::Closure& success_callback,
101     const ErrorCompletionCallback& error_callback) {
102   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
103   DCHECK(object_path_.value().empty());
104   DCHECK(!profile_.get());
105 
106   if (!uuid.IsValid()) {
107     error_callback.Run(kInvalidUUID);
108     return;
109   }
110 
111   device_address_ = device->GetAddress();
112   device_path_ = device->object_path();
113   uuid_ = uuid;
114   options_.reset(new BluetoothProfileManagerClient::Options());
115 
116   RegisterProfile(success_callback, error_callback);
117 }
118 
Listen(scoped_refptr<BluetoothAdapter> adapter,SocketType socket_type,const BluetoothUUID & uuid,int psm_or_channel,const base::Closure & success_callback,const ErrorCompletionCallback & error_callback)119 void BluetoothSocketChromeOS::Listen(
120     scoped_refptr<BluetoothAdapter> adapter,
121     SocketType socket_type,
122     const BluetoothUUID& uuid,
123     int psm_or_channel,
124     const base::Closure& success_callback,
125     const ErrorCompletionCallback& error_callback) {
126   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
127   DCHECK(object_path_.value().empty());
128   DCHECK(!profile_.get());
129 
130   if (!uuid.IsValid()) {
131     error_callback.Run(kInvalidUUID);
132     return;
133   }
134 
135   adapter_ = adapter;
136   adapter_->AddObserver(this);
137 
138   uuid_ = uuid;
139   options_.reset(new BluetoothProfileManagerClient::Options());
140 
141   switch (socket_type) {
142     case kRfcomm:
143       options_->channel.reset(new uint16(
144           psm_or_channel == BluetoothAdapter::kChannelAuto
145               ? 0 : psm_or_channel));
146       break;
147     case kL2cap:
148       options_->psm.reset(new uint16(
149           psm_or_channel == BluetoothAdapter::kPsmAuto
150               ? 0 : psm_or_channel));
151       break;
152     default:
153       NOTREACHED();
154   }
155 
156   RegisterProfile(success_callback, error_callback);
157 }
158 
Close()159 void BluetoothSocketChromeOS::Close() {
160   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
161 
162   if (profile_)
163     UnregisterProfile();
164 
165   if (!device_path_.value().empty()) {
166     BluetoothSocketNet::Close();
167   } else {
168     DoCloseListening();
169   }
170 }
171 
Disconnect(const base::Closure & callback)172 void BluetoothSocketChromeOS::Disconnect(const base::Closure& callback) {
173   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
174 
175   if (profile_)
176    UnregisterProfile();
177 
178   if (!device_path_.value().empty()) {
179     BluetoothSocketNet::Disconnect(callback);
180   } else {
181     DoCloseListening();
182     callback.Run();
183   }
184 }
185 
Accept(const AcceptCompletionCallback & success_callback,const ErrorCompletionCallback & error_callback)186 void BluetoothSocketChromeOS::Accept(
187     const AcceptCompletionCallback& success_callback,
188     const ErrorCompletionCallback& error_callback) {
189   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
190 
191   if (!device_path_.value().empty()) {
192     error_callback.Run(kSocketNotListening);
193     return;
194   }
195 
196   // Only one pending accept at a time
197   if (accept_request_.get()) {
198     error_callback.Run(net::ErrorToString(net::ERR_IO_PENDING));
199     return;
200   }
201 
202   accept_request_.reset(new AcceptRequest);
203   accept_request_->success_callback = success_callback;
204   accept_request_->error_callback = error_callback;
205 
206   if (connection_request_queue_.size() >= 1) {
207     AcceptConnectionRequest();
208   }
209 }
210 
RegisterProfile(const base::Closure & success_callback,const ErrorCompletionCallback & error_callback)211 void BluetoothSocketChromeOS::RegisterProfile(
212     const base::Closure& success_callback,
213     const ErrorCompletionCallback& error_callback) {
214   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
215   DCHECK(object_path_.value().empty());
216   DCHECK(!profile_.get());
217 
218   // The object path is relatively meaningless, but has to be unique, so for
219   // connecting profiles use a combination of the device address and profile
220   // UUID.
221   std::string device_address_path, uuid_path;
222   base::ReplaceChars(device_address_, ":-", "_", &device_address_path);
223   base::ReplaceChars(uuid_.canonical_value(), ":-", "_", &uuid_path);
224   if (!device_address_path.empty()) {
225     object_path_ = dbus::ObjectPath("/org/chromium/bluetooth_profile/" +
226                                     device_address_path + "/" + uuid_path);
227   } else {
228     object_path_ = dbus::ObjectPath("/org/chromium/bluetooth_profile/" +
229                                     uuid_path);
230   }
231 
232   // Create the service provider for the profile object.
233   dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
234   profile_.reset(BluetoothProfileServiceProvider::Create(
235       system_bus, object_path_, this));
236   DCHECK(profile_.get());
237 
238   // Before reaching out to the Bluetooth Daemon to register a listening socket,
239   // make sure it's actually running. If not, report success and carry on;
240   // the profile will be registered when the daemon becomes available.
241   if (adapter_ && !adapter_->IsPresent()) {
242     VLOG(1) << object_path_.value() << ": Delaying profile registration.";
243     success_callback.Run();
244     return;
245   }
246 
247   VLOG(1) << object_path_.value() << ": Registering profile.";
248   DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
249       RegisterProfile(
250           object_path_,
251           uuid_.canonical_value(),
252           *options_,
253           base::Bind(&BluetoothSocketChromeOS::OnRegisterProfile,
254                      this,
255                      success_callback,
256                      error_callback),
257           base::Bind(&BluetoothSocketChromeOS::OnRegisterProfileError,
258                      this,
259                      error_callback));
260 }
261 
OnRegisterProfile(const base::Closure & success_callback,const ErrorCompletionCallback & error_callback)262 void BluetoothSocketChromeOS::OnRegisterProfile(
263     const base::Closure& success_callback,
264     const ErrorCompletionCallback& error_callback) {
265   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
266   if (!device_path_.value().empty()) {
267     VLOG(1) << object_path_.value() << ": Profile registered, connecting to "
268             << device_path_.value();
269 
270     DBusThreadManager::Get()->GetBluetoothDeviceClient()->
271         ConnectProfile(
272             device_path_,
273             uuid_.canonical_value(),
274             base::Bind(
275                 &BluetoothSocketChromeOS::OnConnectProfile,
276                 this,
277                 success_callback),
278             base::Bind(
279                 &BluetoothSocketChromeOS::OnConnectProfileError,
280                 this,
281                 error_callback));
282   } else {
283     VLOG(1) << object_path_.value() << ": Profile registered.";
284     success_callback.Run();
285   }
286 }
287 
OnRegisterProfileError(const ErrorCompletionCallback & error_callback,const std::string & error_name,const std::string & error_message)288 void BluetoothSocketChromeOS::OnRegisterProfileError(
289     const ErrorCompletionCallback& error_callback,
290     const std::string& error_name,
291     const std::string& error_message) {
292   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
293   LOG(WARNING) << object_path_.value() << ": Failed to register profile: "
294                << error_name << ": " << error_message;
295   error_callback.Run(error_message);
296 }
297 
OnConnectProfile(const base::Closure & success_callback)298 void BluetoothSocketChromeOS::OnConnectProfile(
299     const base::Closure& success_callback) {
300   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
301   VLOG(1) << object_path_.value() << ": Profile connected.";
302   UnregisterProfile();
303   success_callback.Run();
304 }
305 
OnConnectProfileError(const ErrorCompletionCallback & error_callback,const std::string & error_name,const std::string & error_message)306 void BluetoothSocketChromeOS::OnConnectProfileError(
307     const ErrorCompletionCallback& error_callback,
308     const std::string& error_name,
309     const std::string& error_message) {
310   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
311   LOG(WARNING) << object_path_.value() << ": Failed to connect profile: "
312                << error_name << ": " << error_message;
313   UnregisterProfile();
314   error_callback.Run(error_message);
315 }
316 
AdapterPresentChanged(BluetoothAdapter * adapter,bool present)317 void BluetoothSocketChromeOS::AdapterPresentChanged(BluetoothAdapter* adapter,
318                                                     bool present) {
319   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
320   DCHECK(!object_path_.value().empty());
321   DCHECK(profile_.get());
322 
323   if (!present)
324     return;
325 
326   VLOG(1) << object_path_.value() << ": Re-register profile.";
327   DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
328       RegisterProfile(
329           object_path_,
330           uuid_.canonical_value(),
331           *options_,
332           base::Bind(&BluetoothSocketChromeOS::OnInternalRegisterProfile,
333                      this),
334           base::Bind(&BluetoothSocketChromeOS::OnInternalRegisterProfileError,
335                      this));
336 }
337 
OnInternalRegisterProfile()338 void BluetoothSocketChromeOS::OnInternalRegisterProfile() {
339   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
340 
341   VLOG(1) << object_path_.value() << ": Profile re-registered";
342 }
343 
OnInternalRegisterProfileError(const std::string & error_name,const std::string & error_message)344 void BluetoothSocketChromeOS::OnInternalRegisterProfileError(
345     const std::string& error_name,
346     const std::string& error_message) {
347   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
348 
349   // It's okay if the profile already exists, it means we registered it on
350   // initialization.
351   if (error_name == bluetooth_profile_manager::kErrorAlreadyExists)
352     return;
353 
354   LOG(WARNING) << object_path_.value() << ": Failed to re-register profile: "
355                << error_name << ": " << error_message;
356 }
357 
Released()358 void BluetoothSocketChromeOS::Released() {
359   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
360   VLOG(1) << object_path_.value() << ": Release";
361 }
362 
NewConnection(const dbus::ObjectPath & device_path,scoped_ptr<dbus::FileDescriptor> fd,const BluetoothProfileServiceProvider::Delegate::Options & options,const ConfirmationCallback & callback)363 void BluetoothSocketChromeOS::NewConnection(
364     const dbus::ObjectPath& device_path,
365     scoped_ptr<dbus::FileDescriptor> fd,
366     const BluetoothProfileServiceProvider::Delegate::Options& options,
367     const ConfirmationCallback& callback) {
368   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
369   VLOG(1) << object_path_.value() << ": New connection from device: "
370           << device_path.value();
371 
372   if (!device_path_.value().empty()) {
373     DCHECK(device_path_ == device_path);
374 
375     socket_thread()->task_runner()->PostTask(
376         FROM_HERE,
377         base::Bind(
378             &BluetoothSocketChromeOS::DoNewConnection,
379             this,
380             device_path_,
381             base::Passed(&fd),
382             options,
383             callback));
384   } else {
385     linked_ptr<ConnectionRequest> request(new ConnectionRequest());
386     request->device_path = device_path;
387     request->fd = fd.Pass();
388     request->options = options;
389     request->callback = callback;
390 
391     connection_request_queue_.push(request);
392     VLOG(1) << object_path_.value() << ": Connection is now pending.";
393     if (accept_request_) {
394       AcceptConnectionRequest();
395     }
396   }
397 }
398 
RequestDisconnection(const dbus::ObjectPath & device_path,const ConfirmationCallback & callback)399 void BluetoothSocketChromeOS::RequestDisconnection(
400     const dbus::ObjectPath& device_path,
401     const ConfirmationCallback& callback) {
402   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
403   VLOG(1) << object_path_.value() << ": Request disconnection";
404   callback.Run(SUCCESS);
405 }
406 
Cancel()407 void BluetoothSocketChromeOS::Cancel() {
408   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
409   VLOG(1) << object_path_.value() << ": Cancel";
410 
411   if (!connection_request_queue_.size())
412     return;
413 
414   // If the front request is being accepted mark it as cancelled, otherwise
415   // just pop it from the queue.
416   linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
417   if (!request->accepting) {
418     request->cancelled = true;
419   } else {
420     connection_request_queue_.pop();
421   }
422 }
423 
AcceptConnectionRequest()424 void BluetoothSocketChromeOS::AcceptConnectionRequest() {
425   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
426   DCHECK(accept_request_.get());
427   DCHECK(connection_request_queue_.size() >= 1);
428 
429   VLOG(1) << object_path_.value() << ": Accepting pending connection.";
430 
431   linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
432   request->accepting = true;
433 
434   BluetoothDeviceChromeOS* device =
435       static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->
436           GetDeviceWithPath(request->device_path);
437   DCHECK(device);
438 
439   scoped_refptr<BluetoothSocketChromeOS> client_socket =
440       BluetoothSocketChromeOS::CreateBluetoothSocket(
441           ui_task_runner(),
442           socket_thread(),
443           net_log(),
444           source());
445 
446   client_socket->device_address_ = device->GetAddress();
447   client_socket->device_path_ = request->device_path;
448   client_socket->uuid_ = uuid_;
449 
450   socket_thread()->task_runner()->PostTask(
451       FROM_HERE,
452       base::Bind(
453           &BluetoothSocketChromeOS::DoNewConnection,
454           client_socket,
455           request->device_path,
456           base::Passed(&request->fd),
457           request->options,
458           base::Bind(&BluetoothSocketChromeOS::OnNewConnection,
459                      this,
460                      client_socket,
461                      request->callback)));
462 }
463 
DoNewConnection(const dbus::ObjectPath & device_path,scoped_ptr<dbus::FileDescriptor> fd,const BluetoothProfileServiceProvider::Delegate::Options & options,const ConfirmationCallback & callback)464 void BluetoothSocketChromeOS::DoNewConnection(
465     const dbus::ObjectPath& device_path,
466     scoped_ptr<dbus::FileDescriptor> fd,
467     const BluetoothProfileServiceProvider::Delegate::Options& options,
468     const ConfirmationCallback& callback) {
469   DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread());
470   base::ThreadRestrictions::AssertIOAllowed();
471   fd->CheckValidity();
472 
473   VLOG(1) << object_path_.value() << ": Validity check complete.";
474   if (!fd->is_valid()) {
475     LOG(WARNING) << object_path_.value() << " :" << fd->value()
476                  << ": Invalid file descriptor received from Bluetooth Daemon.";
477     ui_task_runner()->PostTask(FROM_HERE,
478                                base::Bind(callback, REJECTED));;
479     return;
480   }
481 
482   if (tcp_socket()) {
483     LOG(WARNING) << object_path_.value() << ": Already connected";
484     ui_task_runner()->PostTask(FROM_HERE,
485                                base::Bind(callback, REJECTED));;
486     return;
487   }
488 
489   ResetTCPSocket();
490 
491   // Note: We don't have a meaningful |IPEndPoint|, but that is ok since the
492   // TCPSocket implementation does not actually require one.
493   int net_result = tcp_socket()->AdoptConnectedSocket(fd->value(),
494                                                       net::IPEndPoint());
495   if (net_result != net::OK) {
496     LOG(WARNING) << object_path_.value() << ": Error adopting socket: "
497                  << std::string(net::ErrorToString(net_result));
498     ui_task_runner()->PostTask(FROM_HERE,
499                                base::Bind(callback, REJECTED));;
500     return;
501   }
502 
503   VLOG(2) << object_path_.value() << ": Taking descriptor, confirming success.";
504   fd->TakeValue();
505   ui_task_runner()->PostTask(FROM_HERE,
506                              base::Bind(callback, SUCCESS));;
507 }
508 
OnNewConnection(scoped_refptr<BluetoothSocket> socket,const ConfirmationCallback & callback,Status status)509 void BluetoothSocketChromeOS::OnNewConnection(
510     scoped_refptr<BluetoothSocket> socket,
511     const ConfirmationCallback& callback,
512     Status status) {
513   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
514   DCHECK(accept_request_.get());
515   DCHECK(connection_request_queue_.size() >= 1);
516 
517   linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
518   if (status == SUCCESS && !request->cancelled) {
519     BluetoothDeviceChromeOS* device =
520         static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->
521             GetDeviceWithPath(request->device_path);
522     DCHECK(device);
523 
524     accept_request_->success_callback.Run(device, socket);
525   } else {
526     accept_request_->error_callback.Run(kAcceptFailed);
527   }
528 
529   accept_request_.reset(NULL);
530   connection_request_queue_.pop();
531 
532   callback.Run(status);
533 }
534 
DoCloseListening()535 void BluetoothSocketChromeOS::DoCloseListening() {
536   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
537 
538   if (accept_request_) {
539     accept_request_->error_callback.Run(
540         net::ErrorToString(net::ERR_CONNECTION_CLOSED));
541     accept_request_.reset(NULL);
542   }
543 
544   while (connection_request_queue_.size() > 0) {
545     linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
546     request->callback.Run(REJECTED);
547     connection_request_queue_.pop();
548   }
549 }
550 
UnregisterProfile()551 void BluetoothSocketChromeOS::UnregisterProfile() {
552   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
553   DCHECK(!object_path_.value().empty());
554   DCHECK(profile_.get());
555 
556   VLOG(1) << object_path_.value() << ": Unregister profile";
557   DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
558       UnregisterProfile(
559           object_path_,
560           base::Bind(&BluetoothSocketChromeOS::OnUnregisterProfile,
561                      this,
562                      object_path_),
563           base::Bind(&BluetoothSocketChromeOS::OnUnregisterProfileError,
564                      this,
565                      object_path_));
566 
567   profile_.reset();
568   object_path_ = dbus::ObjectPath("");
569 }
570 
OnUnregisterProfile(const dbus::ObjectPath & object_path)571 void BluetoothSocketChromeOS::OnUnregisterProfile(
572     const dbus::ObjectPath& object_path) {
573   VLOG(1) << object_path.value() << ": Profile unregistered";
574 }
575 
OnUnregisterProfileError(const dbus::ObjectPath & object_path,const std::string & error_name,const std::string & error_message)576 void BluetoothSocketChromeOS::OnUnregisterProfileError(
577     const dbus::ObjectPath& object_path,
578     const std::string& error_name,
579     const std::string& error_message) {
580   // It's okay if the profile doesn't exist, it means we haven't registered it
581   // yet.
582   if (error_name == bluetooth_profile_manager::kErrorDoesNotExist)
583     return;
584 
585   LOG(WARNING) << object_path_.value() << ": Failed to unregister profile: "
586                << error_name << ": " << error_message;
587 }
588 
589 }  // namespace chromeos
590