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_service_service_provider.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/memory/ref_counted.h"
10#include "base/memory/weak_ptr.h"
11#include "base/threading/platform_thread.h"
12#include "chromeos/dbus/dbus_thread_manager.h"
13#include "chromeos/dbus/fake_bluetooth_gatt_service_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";
24}  // namespace
25
26// The BluetoothGattServiceServiceProvider implementation used in production.
27class BluetoothGattServiceServiceProviderImpl
28    : public BluetoothGattServiceServiceProvider {
29 public:
30  BluetoothGattServiceServiceProviderImpl(
31      dbus::Bus* bus,
32      const dbus::ObjectPath& object_path,
33      const std::string& uuid,
34      const std::vector<dbus::ObjectPath>& includes)
35      : origin_thread_id_(base::PlatformThread::CurrentId()),
36        uuid_(uuid),
37        includes_(includes),
38        bus_(bus),
39        object_path_(object_path),
40        weak_ptr_factory_(this) {
41    VLOG(1) << "Creating Bluetooth GATT service: " << object_path_.value()
42            << " UUID: " << uuid;
43    DCHECK(!uuid_.empty());
44    DCHECK(object_path_.IsValid());
45    DCHECK(bus_);
46
47    exported_object_ = bus_->GetExportedObject(object_path_);
48
49    exported_object_->ExportMethod(
50        dbus::kDBusPropertiesInterface,
51        dbus::kDBusPropertiesGet,
52        base::Bind(&BluetoothGattServiceServiceProviderImpl::Get,
53                   weak_ptr_factory_.GetWeakPtr()),
54        base::Bind(&BluetoothGattServiceServiceProviderImpl::OnExported,
55                   weak_ptr_factory_.GetWeakPtr()));
56
57    exported_object_->ExportMethod(
58        dbus::kDBusPropertiesInterface,
59        dbus::kDBusPropertiesSet,
60        base::Bind(&BluetoothGattServiceServiceProviderImpl::Set,
61                   weak_ptr_factory_.GetWeakPtr()),
62        base::Bind(&BluetoothGattServiceServiceProviderImpl::OnExported,
63                   weak_ptr_factory_.GetWeakPtr()));
64
65    exported_object_->ExportMethod(
66        dbus::kDBusPropertiesInterface,
67        dbus::kDBusPropertiesGetAll,
68        base::Bind(&BluetoothGattServiceServiceProviderImpl::GetAll,
69                   weak_ptr_factory_.GetWeakPtr()),
70        base::Bind(&BluetoothGattServiceServiceProviderImpl::OnExported,
71                   weak_ptr_factory_.GetWeakPtr()));
72  }
73
74  virtual ~BluetoothGattServiceServiceProviderImpl() {
75    VLOG(1) << "Cleaning up Bluetooth GATT service: " << object_path_.value();
76    bus_->UnregisterExportedObject(object_path_);
77  }
78
79 private:
80  // Returns true if the current thread is on the origin thread.
81  bool OnOriginThread() {
82    return base::PlatformThread::CurrentId() == origin_thread_id_;
83  }
84
85  // Called by dbus:: when the Bluetooth daemon fetches a single property of
86  // the service.
87  void Get(dbus::MethodCall* method_call,
88           dbus::ExportedObject::ResponseSender response_sender) {
89    VLOG(2) << "BluetoothGattServiceServiceProvider::Get: "
90            << object_path_.value();
91    DCHECK(OnOriginThread());
92
93    dbus::MessageReader reader(method_call);
94
95    std::string interface_name;
96    std::string property_name;
97    if (!reader.PopString(&interface_name) ||
98        !reader.PopString(&property_name) ||
99        reader.HasMoreData()) {
100      scoped_ptr<dbus::ErrorResponse> error_response =
101          dbus::ErrorResponse::FromMethodCall(
102              method_call, kErrorInvalidArgs, "Expected 'ss'.");
103      response_sender.Run(error_response.PassAs<dbus::Response>());
104      return;
105    }
106
107    // Only the GATT service interface is allowed.
108    if (interface_name !=
109        bluetooth_gatt_service::kBluetoothGattServiceInterface) {
110      scoped_ptr<dbus::ErrorResponse> error_response =
111          dbus::ErrorResponse::FromMethodCall(
112              method_call, kErrorInvalidArgs,
113              "No such interface: '" + interface_name + "'.");
114      response_sender.Run(error_response.PassAs<dbus::Response>());
115      return;
116    }
117
118    // Return error if |property_name| is unknown.
119    if (property_name != bluetooth_gatt_service::kUUIDProperty &&
120        property_name != bluetooth_gatt_service::kIncludesProperty) {
121      scoped_ptr<dbus::ErrorResponse> error_response =
122          dbus::ErrorResponse::FromMethodCall(
123              method_call, kErrorInvalidArgs,
124              "No such property: '" + property_name + "'.");
125      response_sender.Run(error_response.PassAs<dbus::Response>());
126      return;
127    }
128
129    scoped_ptr<dbus::Response> response =
130        dbus::Response::FromMethodCall(method_call);
131    dbus::MessageWriter writer(response.get());
132    dbus::MessageWriter variant_writer(NULL);
133
134    if (property_name == bluetooth_gatt_service::kUUIDProperty) {
135      writer.OpenVariant("s", &variant_writer);
136      variant_writer.AppendString(uuid_);
137      writer.CloseContainer(&variant_writer);
138    } else {
139      writer.OpenVariant("ao", &variant_writer);
140      variant_writer.AppendArrayOfObjectPaths(includes_);
141      writer.CloseContainer(&variant_writer);
142    }
143
144    response_sender.Run(response.Pass());
145  }
146
147  // Called by dbus:: when the Bluetooth daemon sets a single property of the
148  // service.
149  void Set(dbus::MethodCall* method_call,
150           dbus::ExportedObject::ResponseSender response_sender) {
151    VLOG(2) << "BluetoothGattServiceServiceProvider::Set: "
152            << object_path_.value();
153    DCHECK(OnOriginThread());
154
155    // All of the properties on this interface are read-only, so just return
156    // error.
157    scoped_ptr<dbus::ErrorResponse> error_response =
158        dbus::ErrorResponse::FromMethodCall(
159            method_call, kErrorPropertyReadOnly,
160            "All properties are read-only.");
161    response_sender.Run(error_response.PassAs<dbus::Response>());
162  }
163
164  // Called by dbus:: when the Bluetooth daemon fetches all properties of the
165  // service.
166  void GetAll(dbus::MethodCall* method_call,
167              dbus::ExportedObject::ResponseSender response_sender) {
168    VLOG(2) << "BluetoothGattServiceServiceProvider::GetAll: "
169            << object_path_.value();
170    DCHECK(OnOriginThread());
171
172    dbus::MessageReader reader(method_call);
173
174    std::string interface_name;
175    if (!reader.PopString(&interface_name) || reader.HasMoreData()) {
176      scoped_ptr<dbus::ErrorResponse> error_response =
177          dbus::ErrorResponse::FromMethodCall(
178              method_call, kErrorInvalidArgs, "Expected 's'.");
179      response_sender.Run(error_response.PassAs<dbus::Response>());
180      return;
181    }
182
183    // Only the GATT service interface is allowed.
184    if (interface_name !=
185        bluetooth_gatt_service::kBluetoothGattServiceInterface) {
186      scoped_ptr<dbus::ErrorResponse> error_response =
187          dbus::ErrorResponse::FromMethodCall(
188              method_call, kErrorInvalidArgs,
189              "No such interface: '" + interface_name + "'.");
190      response_sender.Run(error_response.PassAs<dbus::Response>());
191      return;
192    }
193
194    scoped_ptr<dbus::Response> response =
195        dbus::Response::FromMethodCall(method_call);
196    dbus::MessageWriter writer(response.get());
197    dbus::MessageWriter array_writer(NULL);
198    dbus::MessageWriter dict_entry_writer(NULL);
199    dbus::MessageWriter variant_writer(NULL);
200
201    writer.OpenArray("{sv}", &array_writer);
202
203    array_writer.OpenDictEntry(&dict_entry_writer);
204    dict_entry_writer.AppendString(bluetooth_gatt_service::kUUIDProperty);
205    dict_entry_writer.AppendVariantOfString(uuid_);
206    array_writer.CloseContainer(&dict_entry_writer);
207
208    array_writer.OpenDictEntry(&dict_entry_writer);
209    dict_entry_writer.AppendString(bluetooth_gatt_service::kIncludesProperty);
210    dict_entry_writer.OpenVariant("ao", &variant_writer);
211    variant_writer.AppendArrayOfObjectPaths(includes_);
212    dict_entry_writer.CloseContainer(&variant_writer);
213    array_writer.CloseContainer(&dict_entry_writer);
214
215    writer.CloseContainer(&array_writer);
216
217    response_sender.Run(response.Pass());
218  }
219
220  // Called by dbus:: when a method is exported.
221  void OnExported(const std::string& interface_name,
222                  const std::string& method_name,
223                  bool success) {
224    LOG_IF(WARNING, !success) << "Failed to export "
225                              << interface_name << "." << method_name;
226  }
227
228  // Origin thread (i.e. the UI thread in production).
229  base::PlatformThreadId origin_thread_id_;
230
231  // 128-bit service UUID of this object.
232  std::string uuid_;
233
234  // List of object paths that represent other exported GATT services that are
235  // included from this service.
236  std::vector<dbus::ObjectPath> includes_;
237
238  // D-Bus bus object is exported on, not owned by this object and must
239  // outlive it.
240  dbus::Bus* bus_;
241
242  // D-Bus object path of object we are exporting, kept so we can unregister
243  // again in our destructor.
244  dbus::ObjectPath object_path_;
245
246  // D-Bus object we are exporting, owned by this object.
247  scoped_refptr<dbus::ExportedObject> exported_object_;
248
249  // Weak pointer factory for generating 'this' pointers that might live longer
250  // than we do.
251  // Note: This should remain the last member so it'll be destroyed and
252  // invalidate its weak pointers before any other members are destroyed.
253  base::WeakPtrFactory<BluetoothGattServiceServiceProviderImpl>
254      weak_ptr_factory_;
255
256  DISALLOW_COPY_AND_ASSIGN(BluetoothGattServiceServiceProviderImpl);
257};
258
259BluetoothGattServiceServiceProvider::BluetoothGattServiceServiceProvider() {
260}
261
262BluetoothGattServiceServiceProvider::~BluetoothGattServiceServiceProvider() {
263}
264
265// static
266BluetoothGattServiceServiceProvider*
267BluetoothGattServiceServiceProvider::Create(
268    dbus::Bus* bus,
269    const dbus::ObjectPath& object_path,
270    const std::string& uuid,
271    const std::vector<dbus::ObjectPath>& includes) {
272  if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH)) {
273    return new BluetoothGattServiceServiceProviderImpl(
274        bus, object_path, uuid, includes);
275  }
276  return new FakeBluetoothGattServiceServiceProvider(
277      object_path, uuid, includes);
278}
279
280}  // namespace chromeos
281