bluetooth_device_chromeos.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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.
4
5#include "device/bluetooth/bluetooth_device_chromeos.h"
6
7#include <map>
8#include <string>
9#include <vector>
10
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_service_record_chromeos.h"
32#include "device/bluetooth/bluetooth_socket_chromeos.h"
33#include "device/bluetooth/bluetooth_utils.h"
34#include "third_party/cros_system_api/dbus/service_constants.h"
35
36using device::BluetoothDevice;
37using device::BluetoothOutOfBandPairingData;
38using device::BluetoothServiceRecord;
39using device::BluetoothSocket;
40
41namespace {
42
43void DoNothingServiceRecordList(const BluetoothDevice::ServiceRecordList&) {}
44
45} // namespace
46
47namespace chromeos {
48
49BluetoothDeviceChromeOS::BluetoothDeviceChromeOS(
50    BluetoothAdapterChromeOS* adapter)
51    : BluetoothDevice(),
52      adapter_(adapter),
53      pairing_delegate_(NULL),
54      connecting_applications_counter_(0),
55      connecting_calls_(0),
56      service_records_loaded_(false),
57      weak_ptr_factory_(this) {
58}
59
60BluetoothDeviceChromeOS::~BluetoothDeviceChromeOS() {
61}
62
63bool BluetoothDeviceChromeOS::IsPaired() const {
64  return !object_path_.value().empty();
65}
66
67const BluetoothDevice::ServiceList&
68BluetoothDeviceChromeOS::GetServices() const {
69  return service_uuids_;
70}
71
72void BluetoothDeviceChromeOS::GetServiceRecords(
73    const ServiceRecordsCallback& callback,
74    const ErrorCallback& error_callback) {
75  DBusThreadManager::Get()->GetBluetoothDeviceClient()->
76      DiscoverServices(
77          object_path_,
78          "",  // empty pattern to browse all services
79          base::Bind(&BluetoothDeviceChromeOS::CollectServiceRecordsCallback,
80                     weak_ptr_factory_.GetWeakPtr(),
81                     callback,
82                     base::Bind(
83                         &BluetoothDeviceChromeOS::OnGetServiceRecordsError,
84                         weak_ptr_factory_.GetWeakPtr(),
85                         callback,
86                         error_callback)));
87}
88
89void BluetoothDeviceChromeOS::ProvidesServiceWithName(
90    const std::string& name,
91    const ProvidesServiceCallback& callback) {
92  GetServiceRecords(
93      base::Bind(&BluetoothDeviceChromeOS::SearchServicesForNameCallback,
94                 weak_ptr_factory_.GetWeakPtr(),
95                 name,
96                 callback),
97      base::Bind(&BluetoothDeviceChromeOS::SearchServicesForNameErrorCallback,
98                 weak_ptr_factory_.GetWeakPtr(),
99                 callback));
100}
101
102bool BluetoothDeviceChromeOS::ExpectingPinCode() const {
103  return !pincode_callback_.is_null();
104}
105
106bool BluetoothDeviceChromeOS::ExpectingPasskey() const {
107  return !passkey_callback_.is_null();
108}
109
110bool BluetoothDeviceChromeOS::ExpectingConfirmation() const {
111  return !confirmation_callback_.is_null();
112}
113
114void BluetoothDeviceChromeOS::Connect(
115    PairingDelegate* pairing_delegate,
116    const base::Closure& callback,
117    const ConnectErrorCallback& error_callback) {
118  // This is safe because Connect() and its callbacks are called in the same
119  // thread.
120  connecting_calls_++;
121  connecting_ = !!connecting_calls_;
122  // Set the decrement to be issued when either callback is called.
123  base::Closure wrapped_callback = base::Bind(
124      &BluetoothDeviceChromeOS::OnConnectCallbackCalled,
125      weak_ptr_factory_.GetWeakPtr(),
126      callback);
127  ConnectErrorCallback wrapped_error_callback = base::Bind(
128      &BluetoothDeviceChromeOS::OnConnectErrorCallbackCalled,
129      weak_ptr_factory_.GetWeakPtr(),
130      error_callback);
131
132  if (IsPaired() || IsBonded() || IsConnected()) {
133    // Connection to already paired or connected device.
134    ConnectApplications(wrapped_callback, wrapped_error_callback);
135
136  } else if (!pairing_delegate) {
137    // No pairing delegate supplied, initiate low-security connection only.
138    DBusThreadManager::Get()->GetBluetoothAdapterClient()->
139        CreateDevice(adapter_->object_path_,
140                     address_,
141                     base::Bind(&BluetoothDeviceChromeOS::OnCreateDevice,
142                                weak_ptr_factory_.GetWeakPtr(),
143                                wrapped_callback,
144                                wrapped_error_callback),
145                     base::Bind(&BluetoothDeviceChromeOS::OnCreateDeviceError,
146                                weak_ptr_factory_.GetWeakPtr(),
147                                wrapped_error_callback));
148  } else {
149    // Initiate high-security connection with pairing.
150    DCHECK(!pairing_delegate_);
151    pairing_delegate_ = pairing_delegate;
152
153    // The agent path is relatively meaningless, we use the device address
154    // to generate it as we only support one pairing attempt at a time for
155    // a given bluetooth device.
156    DCHECK(agent_.get() == NULL);
157
158    std::string agent_path_basename;
159    ReplaceChars(address_, ":", "_", &agent_path_basename);
160    dbus::ObjectPath agent_path("/org/chromium/bluetooth_agent/" +
161                                agent_path_basename);
162
163    dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
164    if (system_bus) {
165      agent_.reset(BluetoothAgentServiceProvider::Create(system_bus,
166                                                         agent_path,
167                                                         this));
168    } else {
169      agent_.reset(NULL);
170    }
171
172    VLOG(1) << "Pairing: " << address_;
173    DBusThreadManager::Get()->GetBluetoothAdapterClient()->
174        CreatePairedDevice(
175            adapter_->object_path_,
176            address_,
177            agent_path,
178            bluetooth_agent::kDisplayYesNoCapability,
179            base::Bind(&BluetoothDeviceChromeOS::OnCreateDevice,
180                       weak_ptr_factory_.GetWeakPtr(),
181                       wrapped_callback,
182                       wrapped_error_callback),
183            base::Bind(&BluetoothDeviceChromeOS::OnCreateDeviceError,
184                       weak_ptr_factory_.GetWeakPtr(),
185                       wrapped_error_callback));
186  }
187}
188
189void BluetoothDeviceChromeOS::SetPinCode(const std::string& pincode) {
190  if (!agent_.get() || pincode_callback_.is_null())
191    return;
192
193  pincode_callback_.Run(SUCCESS, pincode);
194  pincode_callback_.Reset();
195}
196
197void BluetoothDeviceChromeOS::SetPasskey(uint32 passkey) {
198  if (!agent_.get() || passkey_callback_.is_null())
199    return;
200
201  passkey_callback_.Run(SUCCESS, passkey);
202  passkey_callback_.Reset();
203}
204
205void BluetoothDeviceChromeOS::ConfirmPairing() {
206  if (!agent_.get() || confirmation_callback_.is_null())
207    return;
208
209  confirmation_callback_.Run(SUCCESS);
210  confirmation_callback_.Reset();
211}
212
213void BluetoothDeviceChromeOS::RejectPairing() {
214  if (!agent_.get())
215    return;
216
217  if (!pincode_callback_.is_null()) {
218    pincode_callback_.Run(REJECTED, "");
219    pincode_callback_.Reset();
220  }
221  if (!passkey_callback_.is_null()) {
222    passkey_callback_.Run(REJECTED, 0);
223    passkey_callback_.Reset();
224  }
225  if (!confirmation_callback_.is_null()) {
226    confirmation_callback_.Run(REJECTED);
227    confirmation_callback_.Reset();
228  }
229}
230
231void BluetoothDeviceChromeOS::CancelPairing() {
232  bool have_callback = false;
233  if (agent_.get()) {
234    if (!pincode_callback_.is_null()) {
235      pincode_callback_.Run(CANCELLED, "");
236      pincode_callback_.Reset();
237      have_callback = true;
238    }
239    if (!passkey_callback_.is_null()) {
240      passkey_callback_.Run(CANCELLED, 0);
241      passkey_callback_.Reset();
242      have_callback = true;
243    }
244    if (!confirmation_callback_.is_null()) {
245      confirmation_callback_.Run(CANCELLED);
246      confirmation_callback_.Reset();
247      have_callback = true;
248    }
249  }
250
251  if (!have_callback) {
252    // User cancels the pairing process.
253    DBusThreadManager::Get()->GetBluetoothAdapterClient()->CancelDeviceCreation(
254        adapter_->object_path_,
255        address_,
256        base::Bind(&BluetoothDeviceChromeOS::OnCancelDeviceCreation,
257                   weak_ptr_factory_.GetWeakPtr()));
258
259    pairing_delegate_ = NULL;
260    agent_.reset();
261  }
262}
263
264void BluetoothDeviceChromeOS::Disconnect(const base::Closure& callback,
265                                         const ErrorCallback& error_callback) {
266  DBusThreadManager::Get()->GetBluetoothDeviceClient()->
267      Disconnect(object_path_,
268                 base::Bind(&BluetoothDeviceChromeOS::DisconnectCallback,
269                            weak_ptr_factory_.GetWeakPtr(),
270                            callback,
271                            error_callback));
272
273}
274
275void BluetoothDeviceChromeOS::Forget(const ErrorCallback& error_callback) {
276  DBusThreadManager::Get()->GetBluetoothAdapterClient()->
277      RemoveDevice(adapter_->object_path_,
278                   object_path_,
279                   base::Bind(&BluetoothDeviceChromeOS::ForgetCallback,
280                              weak_ptr_factory_.GetWeakPtr(),
281                              error_callback));
282}
283
284void BluetoothDeviceChromeOS::ConnectToService(const std::string& service_uuid,
285                                               const SocketCallback& callback) {
286  GetServiceRecords(
287      base::Bind(&BluetoothDeviceChromeOS::GetServiceRecordsForConnectCallback,
288                 weak_ptr_factory_.GetWeakPtr(),
289                 service_uuid,
290                 callback),
291      base::Bind(
292          &BluetoothDeviceChromeOS::GetServiceRecordsForConnectErrorCallback,
293          weak_ptr_factory_.GetWeakPtr(),
294          callback));
295}
296
297void BluetoothDeviceChromeOS::SetOutOfBandPairingData(
298    const BluetoothOutOfBandPairingData& data,
299    const base::Closure& callback,
300    const ErrorCallback& error_callback) {
301  DBusThreadManager::Get()->GetBluetoothOutOfBandClient()->
302      AddRemoteData(
303          object_path_,
304          address(),
305          data,
306          base::Bind(&BluetoothDeviceChromeOS::OnRemoteDataCallback,
307                     weak_ptr_factory_.GetWeakPtr(),
308                     callback,
309                     error_callback));
310}
311
312void BluetoothDeviceChromeOS::ClearOutOfBandPairingData(
313    const base::Closure& callback,
314    const ErrorCallback& error_callback) {
315  DBusThreadManager::Get()->GetBluetoothOutOfBandClient()->
316      RemoveRemoteData(
317          object_path_,
318          address(),
319          base::Bind(&BluetoothDeviceChromeOS::OnRemoteDataCallback,
320                     weak_ptr_factory_.GetWeakPtr(),
321                     callback,
322                     error_callback));
323}
324
325void BluetoothDeviceChromeOS::SetObjectPath(
326    const dbus::ObjectPath& object_path) {
327  DCHECK(object_path_ == dbus::ObjectPath(""));
328  object_path_ = object_path;
329}
330
331void BluetoothDeviceChromeOS::RemoveObjectPath() {
332  DCHECK(object_path_ != dbus::ObjectPath(""));
333  object_path_ = dbus::ObjectPath("");
334}
335
336void BluetoothDeviceChromeOS::Update(
337    const BluetoothDeviceClient::Properties* properties,
338    bool update_state) {
339  std::string address = properties->address.value();
340  std::string name = properties->name.value();
341  uint32 bluetooth_class = properties->bluetooth_class.value();
342  const std::vector<std::string>& uuids = properties->uuids.value();
343
344  if (!address.empty())
345    address_ = address;
346  if (!name.empty())
347    name_ = name;
348  if (bluetooth_class)
349    bluetooth_class_ = bluetooth_class;
350  if (!uuids.empty()) {
351    service_uuids_.clear();
352    service_uuids_.assign(uuids.begin(), uuids.end());
353  }
354
355  if (update_state) {
356    // When the device reconnects and we don't have any service records for it,
357    // try to update the cache or fail silently.
358    if (!service_records_loaded_ && !connected_ &&
359        properties->connected.value())
360      GetServiceRecords(base::Bind(&DoNothingServiceRecordList),
361                        base::Bind(&base::DoNothing));
362
363    // BlueZ uses paired to mean link keys exchanged, whereas the Bluetooth
364    // spec refers to this as bonded. Use the spec name for our interface.
365    bonded_ = properties->paired.value();
366    connected_ = properties->connected.value();
367  }
368}
369
370void BluetoothDeviceChromeOS::OnCreateDevice(
371    const base::Closure& callback,
372    const ConnectErrorCallback& error_callback,
373    const dbus::ObjectPath& device_path) {
374  VLOG(1) << "Connection successful: " << device_path.value();
375  if (object_path_.value().empty()) {
376    object_path_ = device_path;
377  } else {
378    LOG_IF(WARNING, object_path_ != device_path)
379        << "Conflicting device paths for objects, result gave: "
380        << device_path.value() << " but signal gave: "
381        << object_path_.value();
382  }
383
384  // Mark the device trusted so it can connect to us automatically, and
385  // we can connect after rebooting. This information is part of the
386  // pairing information of the device, and is unique to the combination
387  // of our bluetooth address and the device's bluetooth address. A
388  // different host needs a new pairing, so it's not useful to sync.
389  DBusThreadManager::Get()->GetBluetoothDeviceClient()->
390      GetProperties(object_path_)->trusted.Set(
391          true,
392          base::Bind(&BluetoothDeviceChromeOS::OnSetTrusted,
393                     weak_ptr_factory_.GetWeakPtr()));
394
395  // In parallel with the |trusted| property change, call GetServiceRecords to
396  // retrieve the SDP from the device and then, either on success or failure,
397  // call ConnectApplications.
398  GetServiceRecords(
399      base::Bind(&BluetoothDeviceChromeOS::OnInitialGetServiceRecords,
400                 weak_ptr_factory_.GetWeakPtr(),
401                 callback,
402                 error_callback),
403      base::Bind(&BluetoothDeviceChromeOS::OnInitialGetServiceRecordsError,
404                 weak_ptr_factory_.GetWeakPtr(),
405                 callback,
406                 error_callback));
407}
408
409void BluetoothDeviceChromeOS::OnCreateDeviceError(
410    const ConnectErrorCallback& error_callback,
411    const std::string& error_name,
412    const std::string& error_message) {
413  // The default |error_code| is an unknown error.
414  ConnectErrorCode error_code = ERROR_UNKNOWN;
415
416  // Report any error in the log, even if we know the possible source of it.
417  LOG(WARNING) << "Connection failed (on CreatePairedDevice): "
418               << "\"" << name_ << "\" (" << address_ << "): "
419               << error_name << ": \"" << error_message << "\"";
420
421  // Determines the right error code from error_name, assuming the error name
422  // comes from CreatePairedDevice bluez function.
423  if (error_name == bluetooth_adapter::kErrorConnectionAttemptFailed) {
424    error_code = ERROR_FAILED;
425  } else if (error_name == bluetooth_adapter::kErrorAuthenticationFailed) {
426    error_code = ERROR_AUTH_FAILED;
427  } else if (error_name == bluetooth_adapter::kErrorAuthenticationRejected) {
428    error_code = ERROR_AUTH_REJECTED;
429  } else if (error_name == bluetooth_adapter::kErrorAuthenticationTimeout) {
430    error_code = ERROR_AUTH_TIMEOUT;
431  }
432  error_callback.Run(error_code);
433}
434
435void BluetoothDeviceChromeOS::CollectServiceRecordsCallback(
436    const ServiceRecordsCallback& callback,
437    const ErrorCallback& error_callback,
438    const dbus::ObjectPath& device_path,
439    const BluetoothDeviceClient::ServiceMap& service_map,
440    bool success) {
441  if (!success) {
442    error_callback.Run();
443    return;
444  }
445
446  // Update the cache. No other thread is executing a GetServiceRecords
447  // callback, so it is safe to delete the previous objects here.
448  service_records_.clear();
449  // TODO(deymo): Perhaps don't update the cache if the new SDP information is
450  // empty and we had something before. Some devices only answer this
451  // information while paired, and this callback could be called in any order if
452  // several calls to GetServiceRecords are made while initial pairing with the
453  // device. This requires more investigation.
454  for (BluetoothDeviceClient::ServiceMap::const_iterator i =
455      service_map.begin(); i != service_map.end(); ++i) {
456    service_records_.push_back(
457        new BluetoothServiceRecordChromeOS(address(), i->second));
458  }
459  service_records_loaded_ = true;
460
461  callback.Run(service_records_);
462}
463
464void BluetoothDeviceChromeOS::OnSetTrusted(bool success) {
465  LOG_IF(WARNING, !success) << "Failed to set device as trusted: " << address_;
466}
467
468void BluetoothDeviceChromeOS::OnInitialGetServiceRecords(
469    const base::Closure& callback,
470    const ConnectErrorCallback& error_callback,
471    const ServiceRecordList& list) {
472  // Connect application-layer protocols.
473  ConnectApplications(callback, error_callback);
474}
475
476void BluetoothDeviceChromeOS::OnInitialGetServiceRecordsError(
477    const base::Closure& callback,
478    const ConnectErrorCallback& error_callback) {
479  // Ignore the error retrieving the service records and continue.
480  LOG(WARNING) << "Error retrieving SDP for " << address_ << " after pairing.";
481  // Connect application-layer protocols.
482  ConnectApplications(callback, error_callback);
483}
484
485void BluetoothDeviceChromeOS::OnGetServiceRecordsError(
486    const ServiceRecordsCallback& callback,
487    const ErrorCallback& error_callback) {
488  if (service_records_loaded_) {
489    callback.Run(service_records_);
490  } else {
491    error_callback.Run();
492  }
493}
494
495void BluetoothDeviceChromeOS::OnConnectCallbackCalled(
496    const base::Closure& callback) {
497  // Update the connecting status.
498  connecting_calls_--;
499  connecting_ = !!connecting_calls_;
500  callback.Run();
501}
502
503void BluetoothDeviceChromeOS::OnConnectErrorCallbackCalled(
504    const ConnectErrorCallback& error_callback,
505    enum ConnectErrorCode error_code) {
506  // Update the connecting status.
507  connecting_calls_--;
508  connecting_ = !!connecting_calls_;
509  error_callback.Run(error_code);
510}
511
512void BluetoothDeviceChromeOS::ConnectApplications(
513    const base::Closure& callback,
514    const ConnectErrorCallback& error_callback) {
515  // Introspect the device object to determine supported applications.
516  DBusThreadManager::Get()->GetIntrospectableClient()->
517      Introspect(bluetooth_device::kBluetoothDeviceServiceName,
518                 object_path_,
519                 base::Bind(&BluetoothDeviceChromeOS::OnIntrospect,
520                            weak_ptr_factory_.GetWeakPtr(),
521                            callback,
522                            error_callback));
523}
524
525void BluetoothDeviceChromeOS::OnIntrospect(
526    const base::Closure& callback,
527    const ConnectErrorCallback& error_callback,
528    const std::string& service_name,
529    const dbus::ObjectPath& device_path,
530    const std::string& xml_data,
531    bool success) {
532  if (!success) {
533    LOG(WARNING) << "Failed to determine supported applications: " << address_;
534    error_callback.Run(ERROR_UNKNOWN);
535    return;
536  }
537
538  // The introspection data for the device object may list one or more
539  // additional D-Bus interfaces that BlueZ supports for this particular
540  // device. Send appropraite Connect calls for each of those interfaces
541  // to connect all of the application protocols for this device.
542  std::vector<std::string> interfaces =
543      IntrospectableClient::GetInterfacesFromIntrospectResult(xml_data);
544
545  DCHECK_EQ(0, connecting_applications_counter_);
546  connecting_applications_counter_ = 0;
547  for (std::vector<std::string>::iterator iter = interfaces.begin();
548       iter != interfaces.end(); ++iter) {
549    if (*iter == bluetooth_input::kBluetoothInputInterface) {
550      connecting_applications_counter_++;
551      // Supports Input interface.
552      DBusThreadManager::Get()->GetBluetoothInputClient()->
553          Connect(object_path_,
554                  base::Bind(&BluetoothDeviceChromeOS::OnConnect,
555                             weak_ptr_factory_.GetWeakPtr(),
556                             callback,
557                             *iter),
558                  base::Bind(&BluetoothDeviceChromeOS::OnConnectError,
559                             weak_ptr_factory_.GetWeakPtr(),
560                             error_callback, *iter));
561    }
562  }
563
564  // If OnConnect has been called for every call to Connect above, then this
565  // will decrement the counter to -1.  In that case, call the callback
566  // directly as it has not been called by any of the OnConnect callbacks.
567  // This is safe because OnIntrospect and OnConnect run on the same thread.
568  connecting_applications_counter_--;
569  if (connecting_applications_counter_ == -1)
570    callback.Run();
571}
572
573void BluetoothDeviceChromeOS::OnConnect(const base::Closure& callback,
574                                        const std::string& interface_name,
575                                        const dbus::ObjectPath& device_path) {
576  VLOG(1) << "Application connection successful: " << device_path.value()
577          << ": " << interface_name;
578
579  connecting_applications_counter_--;
580  // |callback| should only be called once, meaning it cannot be called before
581  // all requests have been started.  The extra decrement after all requests
582  // have been started, and the check for -1 instead of 0 below, insure only a
583  // single call to |callback| will occur (provided OnConnect and OnIntrospect
584  // run on the same thread, which is true).
585  if (connecting_applications_counter_ == -1) {
586    connecting_applications_counter_ = 0;
587    callback.Run();
588  }
589}
590
591void BluetoothDeviceChromeOS::OnConnectError(
592    const ConnectErrorCallback& error_callback,
593    const std::string& interface_name,
594    const dbus::ObjectPath& device_path,
595    const std::string& error_name,
596    const std::string& error_message) {
597  // The default |error_code| is an unknown error.
598  ConnectErrorCode error_code = ERROR_UNKNOWN;
599
600  // Report any error in the log, even if we know the possible source of it.
601  LOG(WARNING) << "Connection failed (on Connect): "
602               << interface_name << ": "
603               << "\"" << name_ << "\" (" << address_ << "): "
604               << error_name << ": \"" << error_message << "\"";
605
606  // Determines the right error code from error_name, assuming the error name
607  // comes from Connect bluez function.
608  if (error_name == bluetooth_adapter::kErrorFailed) {
609    error_code = ERROR_FAILED;
610  } else if (error_name == bluetooth_adapter::kErrorInProgress) {
611    error_code = ERROR_INPROGRESS;
612  } else if (error_name == bluetooth_adapter::kErrorNotSupported) {
613    error_code = ERROR_UNSUPPORTED_DEVICE;
614  }
615
616  error_callback.Run(error_code);
617}
618
619void BluetoothDeviceChromeOS::DisconnectCallback(
620    const base::Closure& callback,
621    const ErrorCallback& error_callback,
622    const dbus::ObjectPath& device_path,
623    bool success) {
624  DCHECK(device_path == object_path_);
625  if (success) {
626    VLOG(1) << "Disconnection successful: " << address_;
627    callback.Run();
628  } else {
629    if (connected_)  {
630      LOG(WARNING) << "Disconnection failed: " << address_;
631      error_callback.Run();
632    } else {
633      VLOG(1) << "Disconnection failed on a already disconnected device: "
634              << address_;
635      callback.Run();
636    }
637  }
638}
639
640void BluetoothDeviceChromeOS::ForgetCallback(
641    const ErrorCallback& error_callback,
642    const dbus::ObjectPath& adapter_path,
643    bool success) {
644  // It's quite normal that this path never gets called on success; we use a
645  // weak pointer, and bluetoothd might send the DeviceRemoved signal before
646  // the method reply, in which case this object is deleted and the
647  // callback never takes place. Therefore don't do anything here for the
648  // success case.
649  if (!success) {
650    LOG(WARNING) << "Forget failed: " << address_;
651    error_callback.Run();
652  }
653}
654
655void BluetoothDeviceChromeOS::OnCancelDeviceCreation(
656    const dbus::ObjectPath& adapter_path,
657    bool success) {
658  if (!success)
659    LOG(WARNING) << "CancelDeviceCreation failed: " << address_;
660}
661
662void BluetoothDeviceChromeOS::SearchServicesForNameErrorCallback(
663    const ProvidesServiceCallback& callback) {
664  callback.Run(false);
665}
666
667void BluetoothDeviceChromeOS::SearchServicesForNameCallback(
668    const std::string& name,
669    const ProvidesServiceCallback& callback,
670    const ServiceRecordList& list) {
671  for (ServiceRecordList::const_iterator i = list.begin();
672      i != list.end(); ++i) {
673    if ((*i)->name() == name) {
674      callback.Run(true);
675      return;
676    }
677  }
678  callback.Run(false);
679}
680
681void BluetoothDeviceChromeOS::GetServiceRecordsForConnectErrorCallback(
682    const SocketCallback& callback) {
683  callback.Run(NULL);
684}
685
686void BluetoothDeviceChromeOS::GetServiceRecordsForConnectCallback(
687    const std::string& service_uuid,
688    const SocketCallback& callback,
689    const ServiceRecordList& list) {
690  for (ServiceRecordList::const_iterator i = list.begin();
691      i != list.end(); ++i) {
692    if ((*i)->uuid() == service_uuid) {
693      // If multiple service records are found, use the first one that works.
694      scoped_refptr<BluetoothSocket> socket(
695          BluetoothSocketChromeOS::CreateBluetoothSocket(**i));
696      if (socket.get() != NULL) {
697        callback.Run(socket);
698        return;
699      }
700    }
701  }
702  callback.Run(NULL);
703}
704
705void BluetoothDeviceChromeOS::OnRemoteDataCallback(
706    const base::Closure& callback,
707    const ErrorCallback& error_callback,
708    bool success) {
709  if (success)
710    callback.Run();
711  else
712    error_callback.Run();
713}
714
715void BluetoothDeviceChromeOS::DisconnectRequested(
716    const dbus::ObjectPath& object_path) {
717  DCHECK(object_path == object_path_);
718}
719
720void BluetoothDeviceChromeOS::Release() {
721  DCHECK(agent_.get());
722  VLOG(1) << "Release: " << address_;
723
724  DCHECK(pairing_delegate_);
725  pairing_delegate_->DismissDisplayOrConfirm();
726  pairing_delegate_ = NULL;
727
728  pincode_callback_.Reset();
729  passkey_callback_.Reset();
730  confirmation_callback_.Reset();
731
732  agent_.reset();
733}
734
735void BluetoothDeviceChromeOS::RequestPinCode(
736    const dbus::ObjectPath& device_path,
737    const PinCodeCallback& callback) {
738  DCHECK(agent_.get());
739  VLOG(1) << "RequestPinCode: " << device_path.value();
740
741  DCHECK(pairing_delegate_);
742  DCHECK(pincode_callback_.is_null());
743  pincode_callback_ = callback;
744  pairing_delegate_->RequestPinCode(this);
745}
746
747void BluetoothDeviceChromeOS::RequestPasskey(
748    const dbus::ObjectPath& device_path,
749    const PasskeyCallback& callback) {
750  DCHECK(agent_.get());
751  DCHECK(device_path == object_path_);
752  VLOG(1) << "RequestPasskey: " << device_path.value();
753
754  DCHECK(pairing_delegate_);
755  DCHECK(passkey_callback_.is_null());
756  passkey_callback_ = callback;
757  pairing_delegate_->RequestPasskey(this);
758}
759
760void BluetoothDeviceChromeOS::DisplayPinCode(
761    const dbus::ObjectPath& device_path,
762    const std::string& pincode) {
763  DCHECK(agent_.get());
764  DCHECK(device_path == object_path_);
765  VLOG(1) << "DisplayPinCode: " << device_path.value() << " " << pincode;
766
767  DCHECK(pairing_delegate_);
768  pairing_delegate_->DisplayPinCode(this, pincode);
769}
770
771void BluetoothDeviceChromeOS::DisplayPasskey(
772    const dbus::ObjectPath& device_path,
773    uint32 passkey) {
774  DCHECK(agent_.get());
775  DCHECK(device_path == object_path_);
776  VLOG(1) << "DisplayPasskey: " << device_path.value() << " " << passkey;
777
778  DCHECK(pairing_delegate_);
779  pairing_delegate_->DisplayPasskey(this, passkey);
780}
781
782void BluetoothDeviceChromeOS::RequestConfirmation(
783    const dbus::ObjectPath& device_path,
784    uint32 passkey,
785    const ConfirmationCallback& callback) {
786  DCHECK(agent_.get());
787  DCHECK(device_path == object_path_);
788  VLOG(1) << "RequestConfirmation: " << device_path.value() << " " << passkey;
789
790  DCHECK(pairing_delegate_);
791  DCHECK(confirmation_callback_.is_null());
792  confirmation_callback_ = callback;
793  pairing_delegate_->ConfirmPasskey(this, passkey);
794}
795
796void BluetoothDeviceChromeOS::Authorize(const dbus::ObjectPath& device_path,
797                                        const std::string& uuid,
798                                        const ConfirmationCallback& callback) {
799  DCHECK(agent_.get());
800  DCHECK(device_path == object_path_);
801  LOG(WARNING) << "Rejected authorization for service: " << uuid
802               << " requested from device: " << device_path.value();
803  callback.Run(REJECTED);
804}
805
806void BluetoothDeviceChromeOS::ConfirmModeChange(
807    Mode mode,
808    const ConfirmationCallback& callback) {
809  DCHECK(agent_.get());
810  LOG(WARNING) << "Rejected adapter-level mode change: " << mode
811               << " made on agent for device: " << address_;
812  callback.Run(REJECTED);
813}
814
815void BluetoothDeviceChromeOS::Cancel() {
816  DCHECK(agent_.get());
817  VLOG(1) << "Cancel: " << address_;
818
819  DCHECK(pairing_delegate_);
820  pairing_delegate_->DismissDisplayOrConfirm();
821}
822
823
824// static
825BluetoothDeviceChromeOS* BluetoothDeviceChromeOS::Create(
826    BluetoothAdapterChromeOS* adapter) {
827  return new BluetoothDeviceChromeOS(adapter);
828}
829
830}  // namespace chromeos
831