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 "chromeos/dbus/bluetooth_agent_service_provider.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/logging.h"
11#include "base/memory/ref_counted.h"
12#include "base/threading/platform_thread.h"
13#include "chromeos/dbus/dbus_thread_manager.h"
14#include "chromeos/dbus/fake_bluetooth_agent_service_provider.h"
15#include "dbus/bus.h"
16#include "dbus/exported_object.h"
17#include "dbus/message.h"
18#include "dbus/object_path.h"
19#include "third_party/cros_system_api/dbus/service_constants.h"
20
21namespace chromeos {
22
23// The BluetoothAgentServiceProvider implementation used in production.
24class BluetoothAgentServiceProviderImpl
25    : public BluetoothAgentServiceProvider {
26 public:
27  BluetoothAgentServiceProviderImpl(dbus::Bus* bus,
28                                    const dbus::ObjectPath& object_path,
29                                    Delegate* delegate)
30      : origin_thread_id_(base::PlatformThread::CurrentId()),
31        bus_(bus),
32        delegate_(delegate),
33        object_path_(object_path),
34        weak_ptr_factory_(this) {
35    VLOG(1) << "Creating Bluetooth Agent: " << object_path_.value();
36
37    exported_object_ = bus_->GetExportedObject(object_path_);
38
39    exported_object_->ExportMethod(
40        bluetooth_agent::kBluetoothAgentInterface,
41        bluetooth_agent::kRelease,
42        base::Bind(&BluetoothAgentServiceProviderImpl::Release,
43                   weak_ptr_factory_.GetWeakPtr()),
44        base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
45                   weak_ptr_factory_.GetWeakPtr()));
46
47    exported_object_->ExportMethod(
48        bluetooth_agent::kBluetoothAgentInterface,
49        bluetooth_agent::kRequestPinCode,
50        base::Bind(&BluetoothAgentServiceProviderImpl::RequestPinCode,
51                   weak_ptr_factory_.GetWeakPtr()),
52        base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
53                   weak_ptr_factory_.GetWeakPtr()));
54
55    exported_object_->ExportMethod(
56        bluetooth_agent::kBluetoothAgentInterface,
57        bluetooth_agent::kDisplayPinCode,
58        base::Bind(&BluetoothAgentServiceProviderImpl::DisplayPinCode,
59                   weak_ptr_factory_.GetWeakPtr()),
60        base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
61                   weak_ptr_factory_.GetWeakPtr()));
62
63    exported_object_->ExportMethod(
64        bluetooth_agent::kBluetoothAgentInterface,
65        bluetooth_agent::kRequestPasskey,
66        base::Bind(&BluetoothAgentServiceProviderImpl::RequestPasskey,
67                   weak_ptr_factory_.GetWeakPtr()),
68        base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
69                   weak_ptr_factory_.GetWeakPtr()));
70
71    exported_object_->ExportMethod(
72        bluetooth_agent::kBluetoothAgentInterface,
73        bluetooth_agent::kDisplayPasskey,
74        base::Bind(&BluetoothAgentServiceProviderImpl::DisplayPasskey,
75                   weak_ptr_factory_.GetWeakPtr()),
76        base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
77                   weak_ptr_factory_.GetWeakPtr()));
78
79    exported_object_->ExportMethod(
80        bluetooth_agent::kBluetoothAgentInterface,
81        bluetooth_agent::kRequestConfirmation,
82        base::Bind(&BluetoothAgentServiceProviderImpl::RequestConfirmation,
83                   weak_ptr_factory_.GetWeakPtr()),
84        base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
85                   weak_ptr_factory_.GetWeakPtr()));
86
87    exported_object_->ExportMethod(
88        bluetooth_agent::kBluetoothAgentInterface,
89        bluetooth_agent::kRequestAuthorization,
90        base::Bind(&BluetoothAgentServiceProviderImpl::RequestAuthorization,
91                   weak_ptr_factory_.GetWeakPtr()),
92        base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
93                   weak_ptr_factory_.GetWeakPtr()));
94
95    exported_object_->ExportMethod(
96        bluetooth_agent::kBluetoothAgentInterface,
97        bluetooth_agent::kAuthorizeService,
98        base::Bind(&BluetoothAgentServiceProviderImpl::AuthorizeService,
99                   weak_ptr_factory_.GetWeakPtr()),
100        base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
101                   weak_ptr_factory_.GetWeakPtr()));
102
103    exported_object_->ExportMethod(
104        bluetooth_agent::kBluetoothAgentInterface,
105        bluetooth_agent::kCancel,
106        base::Bind(&BluetoothAgentServiceProviderImpl::Cancel,
107                   weak_ptr_factory_.GetWeakPtr()),
108        base::Bind(&BluetoothAgentServiceProviderImpl::OnExported,
109                   weak_ptr_factory_.GetWeakPtr()));
110  }
111
112  virtual ~BluetoothAgentServiceProviderImpl() {
113    VLOG(1) << "Cleaning up Bluetooth Agent: " << object_path_.value();
114
115    // Unregister the object path so we can reuse with a new agent.
116    bus_->UnregisterExportedObject(object_path_);
117  }
118
119 private:
120  // Returns true if the current thread is on the origin thread.
121  bool OnOriginThread() {
122    return base::PlatformThread::CurrentId() == origin_thread_id_;
123  }
124
125  // Called by dbus:: when the agent is unregistered from the Bluetooth
126  // daemon, generally at the end of a pairing request.
127  void Release(dbus::MethodCall* method_call,
128               dbus::ExportedObject::ResponseSender response_sender) {
129    DCHECK(OnOriginThread());
130    DCHECK(delegate_);
131
132    delegate_->Released();
133
134    response_sender.Run(dbus::Response::FromMethodCall(method_call));
135  }
136
137  // Called by dbus:: when the Bluetooth daemon requires a PIN Code for
138  // device authentication.
139  void RequestPinCode(dbus::MethodCall* method_call,
140                      dbus::ExportedObject::ResponseSender response_sender) {
141    DCHECK(OnOriginThread());
142    DCHECK(delegate_);
143
144    dbus::MessageReader reader(method_call);
145    dbus::ObjectPath device_path;
146    if (!reader.PopObjectPath(&device_path)) {
147      LOG(WARNING) << "RequestPinCode called with incorrect paramters: "
148                   << method_call->ToString();
149      return;
150    }
151
152    Delegate::PinCodeCallback callback = base::Bind(
153        &BluetoothAgentServiceProviderImpl::OnPinCode,
154        weak_ptr_factory_.GetWeakPtr(),
155        method_call,
156        response_sender);
157
158    delegate_->RequestPinCode(device_path, callback);
159  }
160
161  // Called by dbus:: when the Bluetooth daemon requires that the user
162  // enter a PIN Code into the remote device so that it may be
163  // authenticated.
164  void DisplayPinCode(dbus::MethodCall* method_call,
165                      dbus::ExportedObject::ResponseSender response_sender) {
166    DCHECK(OnOriginThread());
167    DCHECK(delegate_);
168
169    dbus::MessageReader reader(method_call);
170    dbus::ObjectPath device_path;
171    std::string pincode;
172    if (!reader.PopObjectPath(&device_path) ||
173        !reader.PopString(&pincode)) {
174      LOG(WARNING) << "DisplayPinCode called with incorrect paramters: "
175                   << method_call->ToString();
176      return;
177    }
178
179    delegate_->DisplayPinCode(device_path, pincode);
180
181    response_sender.Run(dbus::Response::FromMethodCall(method_call));
182  }
183
184  // Called by dbus:: when the Bluetooth daemon requires a Passkey for
185  // device authentication.
186  void RequestPasskey(dbus::MethodCall* method_call,
187                      dbus::ExportedObject::ResponseSender response_sender) {
188    DCHECK(OnOriginThread());
189    DCHECK(delegate_);
190
191    dbus::MessageReader reader(method_call);
192    dbus::ObjectPath device_path;
193    if (!reader.PopObjectPath(&device_path)) {
194      LOG(WARNING) << "RequestPasskey called with incorrect paramters: "
195                   << method_call->ToString();
196      return;
197    }
198
199    Delegate::PasskeyCallback callback = base::Bind(
200        &BluetoothAgentServiceProviderImpl::OnPasskey,
201        weak_ptr_factory_.GetWeakPtr(),
202        method_call,
203        response_sender);
204
205    delegate_->RequestPasskey(device_path, callback);
206  }
207
208  // Called by dbus:: when the Bluetooth daemon requires that the user
209  // enter a Passkey into the remote device so that it may be
210  // authenticated.
211  void DisplayPasskey(dbus::MethodCall* method_call,
212                      dbus::ExportedObject::ResponseSender response_sender) {
213    DCHECK(OnOriginThread());
214    DCHECK(delegate_);
215
216    dbus::MessageReader reader(method_call);
217    dbus::ObjectPath device_path;
218    uint32 passkey;
219    uint16 entered;
220    if (!reader.PopObjectPath(&device_path) ||
221        !reader.PopUint32(&passkey) ||
222        !reader.PopUint16(&entered)) {
223      LOG(WARNING) << "DisplayPasskey called with incorrect paramters: "
224                   << method_call->ToString();
225      return;
226    }
227
228    delegate_->DisplayPasskey(device_path, passkey, entered);
229
230    response_sender.Run(dbus::Response::FromMethodCall(method_call));
231  }
232
233  // Called by dbus:: when the Bluetooth daemon requires that the user
234  // confirm that a Passkey is displayed on the screen of the remote
235  // device so that it may be authenticated.
236  void RequestConfirmation(
237      dbus::MethodCall* method_call,
238      dbus::ExportedObject::ResponseSender response_sender) {
239    DCHECK(OnOriginThread());
240    DCHECK(delegate_);
241
242    dbus::MessageReader reader(method_call);
243    dbus::ObjectPath device_path;
244    uint32 passkey;
245    if (!reader.PopObjectPath(&device_path) ||
246        !reader.PopUint32(&passkey)) {
247      LOG(WARNING) << "RequestConfirmation called with incorrect paramters: "
248                   << method_call->ToString();
249      return;
250    }
251
252    Delegate::ConfirmationCallback callback = base::Bind(
253        &BluetoothAgentServiceProviderImpl::OnConfirmation,
254        weak_ptr_factory_.GetWeakPtr(),
255        method_call,
256        response_sender);
257
258    delegate_->RequestConfirmation(device_path, passkey, callback);
259  }
260
261  // Called by dbus:: when the Bluetooth daemon requires that the user
262  // confirm an incoming just-works pairing.
263  void RequestAuthorization(
264        dbus::MethodCall* method_call,
265        dbus::ExportedObject::ResponseSender response_sender) {
266    DCHECK(OnOriginThread());
267    DCHECK(delegate_);
268
269    dbus::MessageReader reader(method_call);
270    dbus::ObjectPath device_path;
271    if (!reader.PopObjectPath(&device_path)) {
272      LOG(WARNING) << "RequestAuthorization called with incorrect paramters: "
273                   << method_call->ToString();
274      return;
275    }
276
277    Delegate::ConfirmationCallback callback = base::Bind(
278        &BluetoothAgentServiceProviderImpl::OnConfirmation,
279        weak_ptr_factory_.GetWeakPtr(),
280        method_call,
281        response_sender);
282
283    delegate_->RequestAuthorization(device_path, callback);
284  }
285
286  // Called by dbus:: when the Bluetooth daemon requires that the user
287  // confirm that that a remote device is authorized to connect to a service
288  // UUID.
289  void AuthorizeService(dbus::MethodCall* method_call,
290                        dbus::ExportedObject::ResponseSender response_sender) {
291    DCHECK(OnOriginThread());
292    DCHECK(delegate_);
293
294    dbus::MessageReader reader(method_call);
295    dbus::ObjectPath device_path;
296    std::string uuid;
297    if (!reader.PopObjectPath(&device_path) ||
298        !reader.PopString(&uuid)) {
299      LOG(WARNING) << "AuthorizeService called with incorrect paramters: "
300                   << method_call->ToString();
301      return;
302    }
303
304    Delegate::ConfirmationCallback callback = base::Bind(
305        &BluetoothAgentServiceProviderImpl::OnConfirmation,
306        weak_ptr_factory_.GetWeakPtr(),
307        method_call,
308        response_sender);
309
310    delegate_->AuthorizeService(device_path, uuid, callback);
311  }
312
313  // Called by dbus:: when the request failed before a reply was returned
314  // from the device.
315  void Cancel(dbus::MethodCall* method_call,
316              dbus::ExportedObject::ResponseSender response_sender) {
317    DCHECK(OnOriginThread());
318    DCHECK(delegate_);
319
320    delegate_->Cancel();
321
322    response_sender.Run(dbus::Response::FromMethodCall(method_call));
323  }
324
325  // Called by dbus:: when a method is exported.
326  void OnExported(const std::string& interface_name,
327                  const std::string& method_name,
328                  bool success) {
329    LOG_IF(WARNING, !success) << "Failed to export "
330                              << interface_name << "." << method_name;
331  }
332
333  // Called by the Delegate to response to a method requesting a PIN code.
334  void OnPinCode(dbus::MethodCall* method_call,
335                 dbus::ExportedObject::ResponseSender response_sender,
336                 Delegate::Status status,
337                 const std::string& pincode) {
338    DCHECK(OnOriginThread());
339
340    switch (status) {
341      case Delegate::SUCCESS: {
342        scoped_ptr<dbus::Response> response(
343            dbus::Response::FromMethodCall(method_call));
344        dbus::MessageWriter writer(response.get());
345        writer.AppendString(pincode);
346        response_sender.Run(response.Pass());
347        break;
348      }
349      case Delegate::REJECTED: {
350        response_sender.Run(
351            dbus::ErrorResponse::FromMethodCall(
352                method_call, bluetooth_agent::kErrorRejected, "rejected")
353            .PassAs<dbus::Response>());
354        break;
355      }
356      case Delegate::CANCELLED: {
357        response_sender.Run(
358            dbus::ErrorResponse::FromMethodCall(
359                method_call, bluetooth_agent::kErrorCanceled, "canceled")
360            .PassAs<dbus::Response>());
361        break;
362      }
363      default:
364        NOTREACHED() << "Unexpected status code from delegate: " << status;
365    }
366  }
367
368  // Called by the Delegate to response to a method requesting a Passkey.
369  void OnPasskey(dbus::MethodCall* method_call,
370                 dbus::ExportedObject::ResponseSender response_sender,
371                 Delegate::Status status,
372                 uint32 passkey) {
373    DCHECK(OnOriginThread());
374
375    switch (status) {
376      case Delegate::SUCCESS: {
377        scoped_ptr<dbus::Response> response(
378            dbus::Response::FromMethodCall(method_call));
379        dbus::MessageWriter writer(response.get());
380        writer.AppendUint32(passkey);
381        response_sender.Run(response.Pass());
382        break;
383      }
384      case Delegate::REJECTED: {
385        response_sender.Run(
386            dbus::ErrorResponse::FromMethodCall(
387                method_call, bluetooth_agent::kErrorRejected, "rejected")
388            .PassAs<dbus::Response>());
389        break;
390      }
391      case Delegate::CANCELLED: {
392        response_sender.Run(
393            dbus::ErrorResponse::FromMethodCall(
394                method_call, bluetooth_agent::kErrorCanceled, "canceled")
395            .PassAs<dbus::Response>());
396        break;
397      }
398      default:
399        NOTREACHED() << "Unexpected status code from delegate: " << status;
400    }
401  }
402
403  // Called by the Delegate in response to a method requiring confirmation.
404  void OnConfirmation(dbus::MethodCall* method_call,
405                      dbus::ExportedObject::ResponseSender response_sender,
406                      Delegate::Status status) {
407    DCHECK(OnOriginThread());
408
409    switch (status) {
410      case Delegate::SUCCESS: {
411        response_sender.Run(dbus::Response::FromMethodCall(method_call));
412        break;
413      }
414      case Delegate::REJECTED: {
415        response_sender.Run(
416            dbus::ErrorResponse::FromMethodCall(
417                method_call, bluetooth_agent::kErrorRejected, "rejected")
418            .PassAs<dbus::Response>());
419        break;
420      }
421      case Delegate::CANCELLED: {
422        response_sender.Run(
423            dbus::ErrorResponse::FromMethodCall(
424                method_call, bluetooth_agent::kErrorCanceled, "canceled")
425            .PassAs<dbus::Response>());
426        break;
427      }
428      default:
429        NOTREACHED() << "Unexpected status code from delegate: " << status;
430    }
431  }
432
433  // Origin thread (i.e. the UI thread in production).
434  base::PlatformThreadId origin_thread_id_;
435
436  // D-Bus bus object is exported on, not owned by this object and must
437  // outlive it.
438  dbus::Bus* bus_;
439
440  // All incoming method calls are passed on to the Delegate and a callback
441  // passed to generate the reply. |delegate_| is generally the object that
442  // owns this one, and must outlive it.
443  Delegate* delegate_;
444
445  // D-Bus object path of object we are exporting, kept so we can unregister
446  // again in our destructor.
447  dbus::ObjectPath object_path_;
448
449  // D-Bus object we are exporting, owned by this object.
450  scoped_refptr<dbus::ExportedObject> exported_object_;
451
452  // Weak pointer factory for generating 'this' pointers that might live longer
453  // than we do.
454  // Note: This should remain the last member so it'll be destroyed and
455  // invalidate its weak pointers before any other members are destroyed.
456  base::WeakPtrFactory<BluetoothAgentServiceProviderImpl> weak_ptr_factory_;
457
458  DISALLOW_COPY_AND_ASSIGN(BluetoothAgentServiceProviderImpl);
459};
460
461BluetoothAgentServiceProvider::BluetoothAgentServiceProvider() {
462}
463
464BluetoothAgentServiceProvider::~BluetoothAgentServiceProvider() {
465}
466
467// static
468BluetoothAgentServiceProvider* BluetoothAgentServiceProvider::Create(
469    dbus::Bus* bus,
470    const dbus::ObjectPath& object_path,
471    Delegate* delegate) {
472  if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH)) {
473    return new BluetoothAgentServiceProviderImpl(bus, object_path, delegate);
474  } else {
475    return new FakeBluetoothAgentServiceProvider(object_path, delegate);
476  }
477}
478
479}  // namespace chromeos
480