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.
5 #include "chromeos/dbus/bluetooth_gatt_characteristic_service_provider.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"
20 const char kErrorInvalidArgs
[] =
21 "org.freedesktop.DBus.Error.InvalidArgs";
22 const char kErrorPropertyReadOnly
[] =
23 "org.freedesktop.DBus.Error.PropertyReadOnly";
24 const char kErrorFailed
[] =
25 "org.freedesktop.DBus.Error.Failed";
28 // The BluetoothGattCharacteristicServiceProvider implementation used in
30 class BluetoothGattCharacteristicServiceProviderImpl
31 : public BluetoothGattCharacteristicServiceProvider
{
33 BluetoothGattCharacteristicServiceProviderImpl(
35 const dbus::ObjectPath
& object_path
,
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()),
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()
52 DCHECK(!uuid_
.empty());
53 DCHECK(object_path_
.IsValid());
54 DCHECK(service_path_
.IsValid());
55 DCHECK(base::StartsWithASCII(object_path_
.value(),
56 service_path_
.value() + "/", true));
58 exported_object_
= bus_
->GetExportedObject(object_path_
);
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()));
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()));
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()));
85 ~BluetoothGattCharacteristicServiceProviderImpl() override
{
86 VLOG(1) << "Cleaning up Bluetooth GATT characteristic: "
87 << object_path_
.value();
88 bus_
->UnregisterExportedObject(object_path_
);
91 // BluetoothGattCharacteristicServiceProvider override.
92 void SendValueChanged(const std::vector
<uint8
>& value
) override
{
93 VLOG(2) << "Emitting a PropertiesChanged signal for characteristic value.";
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
);
104 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface
);
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
);
117 // invalidated_properties.
118 writer
.OpenArray("s", &array_writer
);
119 writer
.CloseContainer(&array_writer
);
121 exported_object_
->SendSignal(&signal
);
125 // Returns true if the current thread is on the origin thread.
126 bool OnOriginThread() {
127 return base::PlatformThread::CurrentId() == origin_thread_id_
;
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());
138 dbus::MessageReader
reader(method_call
);
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
.Pass());
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
.Pass());
163 // If getting the "Value" property, obtain the value from the delegate.
164 if (property_name
== bluetooth_gatt_characteristic::kValueProperty
) {
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
));
176 scoped_ptr
<dbus::Response
> response
=
177 dbus::Response::FromMethodCall(method_call
);
178 dbus::MessageWriter
writer(response
.get());
179 dbus::MessageWriter
variant_writer(NULL
);
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
);
192 response
= dbus::ErrorResponse::FromMethodCall(
195 "No such property: '" + property_name
+ "'.");
198 response_sender
.Run(response
.Pass());
201 // Called by dbus:: when the Bluetooth daemon sets a single property of the
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());
209 dbus::MessageReader
reader(method_call
);
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
.Pass());
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
.Pass());
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
+ "'.";
245 error_name
= kErrorInvalidArgs
;
246 error_message
= "No such property: '" + property_name
+ "'.";
248 scoped_ptr
<dbus::ErrorResponse
> error_response
=
249 dbus::ErrorResponse::FromMethodCall(
250 method_call
, error_name
, error_message
);
251 response_sender
.Run(error_response
.Pass());
256 const uint8
* bytes
= NULL
;
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
.Pass());
267 // Pass the set request onto the delegate.
268 std::vector
<uint8
> value(bytes
, bytes
+ length
);
270 delegate_
->SetCharacteristicValue(
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
));
280 // Called by dbus:: when the Bluetooth daemon fetches all properties of the
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());
288 dbus::MessageReader
reader(method_call
);
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
.Pass());
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
.Pass());
310 // Try to obtain the value from the delegate. We will construct the
311 // response in the success callback.
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
));
322 // Called by dbus:: when a method is exported.
323 void OnExported(const std::string
& interface_name
,
324 const std::string
& method_name
,
326 LOG_IF(WARNING
, !success
) << "Failed to export "
327 << interface_name
<< "." << method_name
;
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 "
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
);
346 writer
.OpenArray("{sv}", &array_writer
);
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
);
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
);
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
);
368 // TODO(armansito): Process Flags & Permissions properties.
370 writer
.CloseContainer(&array_writer
);
372 response_sender
.Run(response
.Pass());
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
);
386 writer
.OpenVariant("ay", &variant_writer
);
387 variant_writer
.AppendArrayOfBytes(value
.data(), value
.size());
388 writer
.CloseContainer(&variant_writer
);
390 response_sender
.Run(response
.Pass());
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
));
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
.Pass());
413 // Origin thread (i.e. the UI thread in production).
414 base::PlatformThreadId origin_thread_id_
;
416 // 128-bit characteristic UUID of this object.
419 // D-Bus bus object is exported on, not owned by this object and must
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.
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_
;
432 // Object path of the GATT service that the exported characteristic belongs
434 dbus::ObjectPath service_path_
;
436 // D-Bus object we are exporting, owned by this object.
437 scoped_refptr
<dbus::ExportedObject
> exported_object_
;
439 // Weak pointer factory for generating 'this' pointers that might live longer
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
>
446 DISALLOW_COPY_AND_ASSIGN(BluetoothGattCharacteristicServiceProviderImpl
);
449 BluetoothGattCharacteristicServiceProvider::
450 BluetoothGattCharacteristicServiceProvider() {
453 BluetoothGattCharacteristicServiceProvider::
454 ~BluetoothGattCharacteristicServiceProvider() {
458 BluetoothGattCharacteristicServiceProvider
*
459 BluetoothGattCharacteristicServiceProvider::Create(
461 const dbus::ObjectPath
& object_path
,
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
);
471 return new FakeBluetoothGattCharacteristicServiceProvider(
472 object_path
, delegate
, uuid
, flags
, permissions
, service_path
);
475 } // namespace chromeos