1// Copyright 2014 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_gatt_characteristic_service_provider.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/memory/weak_ptr.h"
10#include "base/strings/string_util.h"
11#include "base/threading/platform_thread.h"
12#include "chromeos/dbus/dbus_thread_manager.h"
13#include "chromeos/dbus/fake_bluetooth_gatt_characteristic_service_provider.h"
14#include "dbus/exported_object.h"
15#include "dbus/message.h"
16#include "third_party/cros_system_api/dbus/service_constants.h"
17
18namespace chromeos {
19namespace {
20const char kErrorInvalidArgs[] =
21    "org.freedesktop.DBus.Error.InvalidArgs";
22const char kErrorPropertyReadOnly[] =
23    "org.freedesktop.DBus.Error.PropertyReadOnly";
24const char kErrorFailed[] =
25    "org.freedesktop.DBus.Error.Failed";
26}  // namespace
27
28// The BluetoothGattCharacteristicServiceProvider implementation used in
29// production.
30class BluetoothGattCharacteristicServiceProviderImpl
31    : public BluetoothGattCharacteristicServiceProvider {
32 public:
33  BluetoothGattCharacteristicServiceProviderImpl(
34      dbus::Bus* bus,
35      const dbus::ObjectPath& object_path,
36      Delegate* delegate,
37      const std::string& uuid,
38      const std::vector<std::string>& flags,
39      const std::vector<std::string>& permissions,
40      const dbus::ObjectPath& service_path)
41      : origin_thread_id_(base::PlatformThread::CurrentId()),
42        uuid_(uuid),
43        bus_(bus),
44        delegate_(delegate),
45        object_path_(object_path),
46        service_path_(service_path),
47        weak_ptr_factory_(this) {
48    VLOG(1) << "Created Bluetooth GATT characteristic: " << object_path.value()
49            << " UUID: " << uuid;
50    DCHECK(bus_);
51    DCHECK(delegate_);
52    DCHECK(!uuid_.empty());
53    DCHECK(object_path_.IsValid());
54    DCHECK(service_path_.IsValid());
55    DCHECK(StartsWithASCII(
56        object_path_.value(), service_path_.value() + "/", true));
57
58    exported_object_ = bus_->GetExportedObject(object_path_);
59
60    exported_object_->ExportMethod(
61        dbus::kDBusPropertiesInterface,
62        dbus::kDBusPropertiesGet,
63        base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::Get,
64                   weak_ptr_factory_.GetWeakPtr()),
65        base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported,
66                   weak_ptr_factory_.GetWeakPtr()));
67
68    exported_object_->ExportMethod(
69        dbus::kDBusPropertiesInterface,
70        dbus::kDBusPropertiesSet,
71        base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::Set,
72                   weak_ptr_factory_.GetWeakPtr()),
73        base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported,
74                   weak_ptr_factory_.GetWeakPtr()));
75
76    exported_object_->ExportMethod(
77        dbus::kDBusPropertiesInterface,
78        dbus::kDBusPropertiesGetAll,
79        base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::GetAll,
80                   weak_ptr_factory_.GetWeakPtr()),
81        base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported,
82                   weak_ptr_factory_.GetWeakPtr()));
83  }
84
85  virtual ~BluetoothGattCharacteristicServiceProviderImpl() {
86    VLOG(1) << "Cleaning up Bluetooth GATT characteristic: "
87            << object_path_.value();
88    bus_->UnregisterExportedObject(object_path_);
89  }
90
91  // BluetoothGattCharacteristicServiceProvider override.
92  virtual void SendValueChanged(const std::vector<uint8>& value) OVERRIDE {
93    VLOG(2) << "Emitting a PropertiesChanged signal for characteristic value.";
94    dbus::Signal signal(
95        dbus::kDBusPropertiesInterface,
96        dbus::kDBusPropertiesChangedSignal);
97    dbus::MessageWriter writer(&signal);
98    dbus::MessageWriter array_writer(NULL);
99    dbus::MessageWriter dict_entry_writer(NULL);
100    dbus::MessageWriter variant_writer(NULL);
101
102    // interface_name
103    writer.AppendString(
104        bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface);
105
106    // changed_properties
107    writer.OpenArray("{sv}", &array_writer);
108    array_writer.OpenDictEntry(&dict_entry_writer);
109    dict_entry_writer.AppendString(
110        bluetooth_gatt_characteristic::kValueProperty);
111    dict_entry_writer.OpenVariant("ay", &variant_writer);
112    variant_writer.AppendArrayOfBytes(value.data(), value.size());
113    dict_entry_writer.CloseContainer(&variant_writer);
114    array_writer.CloseContainer(&dict_entry_writer);
115    writer.CloseContainer(&array_writer);
116
117    // invalidated_properties.
118    writer.OpenArray("s", &array_writer);
119    writer.CloseContainer(&array_writer);
120
121    exported_object_->SendSignal(&signal);
122  }
123
124 private:
125  // Returns true if the current thread is on the origin thread.
126  bool OnOriginThread() {
127    return base::PlatformThread::CurrentId() == origin_thread_id_;
128  }
129
130  // Called by dbus:: when the Bluetooth daemon fetches a single property of
131  // the characteristic.
132  void Get(dbus::MethodCall* method_call,
133           dbus::ExportedObject::ResponseSender response_sender) {
134    VLOG(2) << "BluetoothGattCharacteristicServiceProvider::Get: "
135            << object_path_.value();
136    DCHECK(OnOriginThread());
137
138    dbus::MessageReader reader(method_call);
139
140    std::string interface_name;
141    std::string property_name;
142    if (!reader.PopString(&interface_name) ||
143        !reader.PopString(&property_name) ||
144        reader.HasMoreData()) {
145      scoped_ptr<dbus::ErrorResponse> error_response =
146          dbus::ErrorResponse::FromMethodCall(
147              method_call, kErrorInvalidArgs, "Expected 'ss'.");
148      response_sender.Run(error_response.PassAs<dbus::Response>());
149      return;
150    }
151
152    // Only the GATT characteristic interface is supported.
153    if (interface_name !=
154        bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface) {
155      scoped_ptr<dbus::ErrorResponse> error_response =
156          dbus::ErrorResponse::FromMethodCall(
157              method_call, kErrorInvalidArgs,
158              "No such interface: '" + interface_name + "'.");
159      response_sender.Run(error_response.PassAs<dbus::Response>());
160      return;
161    }
162
163    // If getting the "Value" property, obtain the value from the delegate.
164    if (property_name == bluetooth_gatt_characteristic::kValueProperty) {
165      DCHECK(delegate_);
166      delegate_->GetCharacteristicValue(
167          base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnGet,
168                     weak_ptr_factory_.GetWeakPtr(),
169                     method_call, response_sender),
170          base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure,
171                     weak_ptr_factory_.GetWeakPtr(),
172                     method_call, response_sender));
173      return;
174    }
175
176    scoped_ptr<dbus::Response> response =
177        dbus::Response::FromMethodCall(method_call);
178    dbus::MessageWriter writer(response.get());
179    dbus::MessageWriter variant_writer(NULL);
180
181    // TODO(armansito): Process the "Flags" and "Permissions" properties below.
182    if (property_name == bluetooth_gatt_characteristic::kUUIDProperty) {
183      writer.OpenVariant("s", &variant_writer);
184      variant_writer.AppendString(uuid_);
185      writer.CloseContainer(&variant_writer);
186    } else if (property_name ==
187               bluetooth_gatt_characteristic::kServiceProperty) {
188      writer.OpenVariant("o", &variant_writer);
189      variant_writer.AppendObjectPath(service_path_);
190      writer.CloseContainer(&variant_writer);
191    } else {
192      response = dbus::ErrorResponse::FromMethodCall(
193          method_call, kErrorInvalidArgs,
194          "No such property: '" + property_name + "'.")
195          .PassAs<dbus::Response>();
196    }
197
198    response_sender.Run(response.Pass());
199  }
200
201  // Called by dbus:: when the Bluetooth daemon sets a single property of the
202  // characteristic.
203  void Set(dbus::MethodCall* method_call,
204           dbus::ExportedObject::ResponseSender response_sender) {
205    VLOG(2) << "BluetoothGattCharacteristicServiceProvider::Set: "
206            << object_path_.value();
207    DCHECK(OnOriginThread());
208
209    dbus::MessageReader reader(method_call);
210
211    std::string interface_name;
212    std::string property_name;
213    dbus::MessageReader variant_reader(NULL);
214    if (!reader.PopString(&interface_name) ||
215        !reader.PopString(&property_name) ||
216        !reader.PopVariant(&variant_reader) ||
217        reader.HasMoreData()) {
218      scoped_ptr<dbus::ErrorResponse> error_response =
219          dbus::ErrorResponse::FromMethodCall(
220              method_call, kErrorInvalidArgs, "Expected 'ssv'.");
221      response_sender.Run(error_response.PassAs<dbus::Response>());
222      return;
223    }
224
225    // Only the GATT characteristic interface is allowed.
226    if (interface_name !=
227        bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface) {
228      scoped_ptr<dbus::ErrorResponse> error_response =
229          dbus::ErrorResponse::FromMethodCall(
230              method_call, kErrorInvalidArgs,
231              "No such interface: '" + interface_name + "'.");
232      response_sender.Run(error_response.PassAs<dbus::Response>());
233      return;
234    }
235
236    // Only the "Value" property is writeable.
237    if (property_name != bluetooth_gatt_characteristic::kValueProperty) {
238      std::string error_name;
239      std::string error_message;
240      if (property_name == bluetooth_gatt_characteristic::kUUIDProperty ||
241          property_name == bluetooth_gatt_characteristic::kServiceProperty) {
242        error_name = kErrorPropertyReadOnly;
243        error_message = "Read-only property: '" + property_name + "'.";
244      } else {
245        error_name = kErrorInvalidArgs;
246        error_message = "No such property: '" + property_name + "'.";
247      }
248      scoped_ptr<dbus::ErrorResponse> error_response =
249          dbus::ErrorResponse::FromMethodCall(
250              method_call, error_name, error_message);
251      response_sender.Run(error_response.PassAs<dbus::Response>());
252      return;
253    }
254
255    // Obtain the value.
256    const uint8* bytes = NULL;
257    size_t length = 0;
258    if (!variant_reader.PopArrayOfBytes(&bytes, &length)) {
259      scoped_ptr<dbus::ErrorResponse> error_response =
260          dbus::ErrorResponse::FromMethodCall(
261              method_call, kErrorInvalidArgs,
262              "Property '" + property_name + "' has type 'ay'.");
263      response_sender.Run(error_response.PassAs<dbus::Response>());
264      return;
265    }
266
267    // Pass the set request onto the delegate.
268    std::vector<uint8> value(bytes, bytes + length);
269    DCHECK(delegate_);
270    delegate_->SetCharacteristicValue(
271        value,
272        base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnSet,
273                   weak_ptr_factory_.GetWeakPtr(),
274                   method_call, response_sender),
275        base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure,
276                   weak_ptr_factory_.GetWeakPtr(),
277                   method_call, response_sender));
278  }
279
280  // Called by dbus:: when the Bluetooth daemon fetches all properties of the
281  // characteristic.
282  void GetAll(dbus::MethodCall* method_call,
283              dbus::ExportedObject::ResponseSender response_sender) {
284    VLOG(2) << "BluetoothGattCharacteristicServiceProvider::GetAll: "
285            << object_path_.value();
286    DCHECK(OnOriginThread());
287
288    dbus::MessageReader reader(method_call);
289
290    std::string interface_name;
291    if (!reader.PopString(&interface_name) || reader.HasMoreData()) {
292      scoped_ptr<dbus::ErrorResponse> error_response =
293          dbus::ErrorResponse::FromMethodCall(
294              method_call, kErrorInvalidArgs, "Expected 's'.");
295      response_sender.Run(error_response.PassAs<dbus::Response>());
296      return;
297    }
298
299    // Only the GATT characteristic interface is supported.
300    if (interface_name !=
301        bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface) {
302      scoped_ptr<dbus::ErrorResponse> error_response =
303          dbus::ErrorResponse::FromMethodCall(
304              method_call, kErrorInvalidArgs,
305              "No such interface: '" + interface_name + "'.");
306      response_sender.Run(error_response.PassAs<dbus::Response>());
307      return;
308    }
309
310    // Try to obtain the value from the delegate. We will construct the
311    // response in the success callback.
312    DCHECK(delegate_);
313    delegate_->GetCharacteristicValue(
314        base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnGetAll,
315                   weak_ptr_factory_.GetWeakPtr(),
316                   method_call, response_sender),
317        base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure,
318                   weak_ptr_factory_.GetWeakPtr(),
319                   method_call, response_sender));
320  }
321
322  // Called by dbus:: when a method is exported.
323  void OnExported(const std::string& interface_name,
324                  const std::string& method_name,
325                  bool success) {
326    LOG_IF(WARNING, !success) << "Failed to export "
327                              << interface_name << "." << method_name;
328  }
329
330  // Called by the Delegate in response to a method to call to get all
331  // properties, in which the delegate has successfully returned the
332  // characteristic value.
333  void OnGetAll(dbus::MethodCall* method_call,
334                dbus::ExportedObject::ResponseSender response_sender,
335                const std::vector<uint8>& value) {
336    VLOG(2) << "Characteristic value obtained from delegate. Responding to "
337            << "GetAll.";
338
339    scoped_ptr<dbus::Response> response =
340        dbus::Response::FromMethodCall(method_call);
341    dbus::MessageWriter writer(response.get());
342    dbus::MessageWriter array_writer(NULL);
343    dbus::MessageWriter dict_entry_writer(NULL);
344    dbus::MessageWriter variant_writer(NULL);
345
346    writer.OpenArray("{sv}", &array_writer);
347
348    array_writer.OpenDictEntry(&dict_entry_writer);
349    dict_entry_writer.AppendString(
350        bluetooth_gatt_characteristic::kUUIDProperty);
351    dict_entry_writer.AppendVariantOfString(uuid_);
352    array_writer.CloseContainer(&dict_entry_writer);
353
354    array_writer.OpenDictEntry(&dict_entry_writer);
355    dict_entry_writer.AppendString(
356        bluetooth_gatt_characteristic::kServiceProperty);
357    dict_entry_writer.AppendVariantOfObjectPath(service_path_);
358    array_writer.CloseContainer(&dict_entry_writer);
359
360    array_writer.OpenDictEntry(&dict_entry_writer);
361    dict_entry_writer.AppendString(
362        bluetooth_gatt_characteristic::kValueProperty);
363    dict_entry_writer.OpenVariant("ay", &variant_writer);
364    variant_writer.AppendArrayOfBytes(value.data(), value.size());
365    dict_entry_writer.CloseContainer(&variant_writer);
366    array_writer.CloseContainer(&dict_entry_writer);
367
368    // TODO(armansito): Process Flags & Permissions properties.
369
370    writer.CloseContainer(&array_writer);
371
372    response_sender.Run(response.Pass());
373  }
374
375  // Called by the Delegate in response to a successful method call to get the
376  // characteristic value.
377  void OnGet(dbus::MethodCall* method_call,
378             dbus::ExportedObject::ResponseSender response_sender,
379             const std::vector<uint8>& value) {
380    VLOG(2) << "Returning characteristic value obtained from delegate.";
381    scoped_ptr<dbus::Response> response =
382        dbus::Response::FromMethodCall(method_call);
383    dbus::MessageWriter writer(response.get());
384    dbus::MessageWriter variant_writer(NULL);
385
386    writer.OpenVariant("ay", &variant_writer);
387    variant_writer.AppendArrayOfBytes(value.data(), value.size());
388    writer.CloseContainer(&variant_writer);
389
390    response_sender.Run(response.Pass());
391  }
392
393  // Called by the Delegate in response to a successful method call to set the
394  // characteristic value.
395  void OnSet(dbus::MethodCall* method_call,
396             dbus::ExportedObject::ResponseSender response_sender) {
397    VLOG(2) << "Successfully set characteristic value. Return success.";
398    response_sender.Run(dbus::Response::FromMethodCall(method_call));
399  }
400
401  // Called by the Delegate in response to a failed method call to get or set
402  // the characteristic value.
403  void OnFailure(dbus::MethodCall* method_call,
404                 dbus::ExportedObject::ResponseSender response_sender) {
405    VLOG(2) << "Failed to get/set characteristic value. Report error.";
406    scoped_ptr<dbus::ErrorResponse> error_response =
407        dbus::ErrorResponse::FromMethodCall(
408            method_call, kErrorFailed,
409            "Failed to get/set characteristic value.");
410    response_sender.Run(error_response.PassAs<dbus::Response>());
411  }
412
413  // Origin thread (i.e. the UI thread in production).
414  base::PlatformThreadId origin_thread_id_;
415
416  // 128-bit characteristic UUID of this object.
417  std::string uuid_;
418
419  // D-Bus bus object is exported on, not owned by this object and must
420  // outlive it.
421  dbus::Bus* bus_;
422
423  // Incoming methods to get and set the "Value" property are passed on to the
424  // delegate and callbacks passed to generate a reply. |delegate_| is generally
425  // the object that owns this one and must outlive it.
426  Delegate* delegate_;
427
428  // D-Bus object path of object we are exporting, kept so we can unregister
429  // again in our destructor.
430  dbus::ObjectPath object_path_;
431
432  // Object path of the GATT service that the exported characteristic belongs
433  // to.
434  dbus::ObjectPath service_path_;
435
436  // D-Bus object we are exporting, owned by this object.
437  scoped_refptr<dbus::ExportedObject> exported_object_;
438
439  // Weak pointer factory for generating 'this' pointers that might live longer
440  // than we do.
441  // Note: This should remain the last member so it'll be destroyed and
442  // invalidate its weak pointers before any other members are destroyed.
443  base::WeakPtrFactory<BluetoothGattCharacteristicServiceProviderImpl>
444      weak_ptr_factory_;
445
446  DISALLOW_COPY_AND_ASSIGN(BluetoothGattCharacteristicServiceProviderImpl);
447};
448
449BluetoothGattCharacteristicServiceProvider::
450    BluetoothGattCharacteristicServiceProvider() {
451}
452
453BluetoothGattCharacteristicServiceProvider::
454    ~BluetoothGattCharacteristicServiceProvider() {
455}
456
457// static
458BluetoothGattCharacteristicServiceProvider*
459BluetoothGattCharacteristicServiceProvider::Create(
460      dbus::Bus* bus,
461      const dbus::ObjectPath& object_path,
462      Delegate* delegate,
463      const std::string& uuid,
464      const std::vector<std::string>& flags,
465      const std::vector<std::string>& permissions,
466      const dbus::ObjectPath& service_path) {
467  if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH)) {
468    return new BluetoothGattCharacteristicServiceProviderImpl(
469        bus, object_path, delegate, uuid, flags, permissions, service_path);
470  }
471  return new FakeBluetoothGattCharacteristicServiceProvider(
472      object_path, delegate, uuid, flags, permissions, service_path);
473}
474
475}  // namespace chromeos
476