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::StartsWith(object_path_
.value(),
56 service_path_
.value() + "/",
57 base::CompareCase::SENSITIVE
));
59 exported_object_
= bus_
->GetExportedObject(object_path_
);
61 exported_object_
->ExportMethod(
62 dbus::kDBusPropertiesInterface
,
63 dbus::kDBusPropertiesGet
,
64 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::Get
,
65 weak_ptr_factory_
.GetWeakPtr()),
66 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported
,
67 weak_ptr_factory_
.GetWeakPtr()));
69 exported_object_
->ExportMethod(
70 dbus::kDBusPropertiesInterface
,
71 dbus::kDBusPropertiesSet
,
72 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::Set
,
73 weak_ptr_factory_
.GetWeakPtr()),
74 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported
,
75 weak_ptr_factory_
.GetWeakPtr()));
77 exported_object_
->ExportMethod(
78 dbus::kDBusPropertiesInterface
,
79 dbus::kDBusPropertiesGetAll
,
80 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::GetAll
,
81 weak_ptr_factory_
.GetWeakPtr()),
82 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnExported
,
83 weak_ptr_factory_
.GetWeakPtr()));
86 ~BluetoothGattCharacteristicServiceProviderImpl() override
{
87 VLOG(1) << "Cleaning up Bluetooth GATT characteristic: "
88 << object_path_
.value();
89 bus_
->UnregisterExportedObject(object_path_
);
92 // BluetoothGattCharacteristicServiceProvider override.
93 void SendValueChanged(const std::vector
<uint8
>& value
) override
{
94 VLOG(2) << "Emitting a PropertiesChanged signal for characteristic value.";
96 dbus::kDBusPropertiesInterface
,
97 dbus::kDBusPropertiesChangedSignal
);
98 dbus::MessageWriter
writer(&signal
);
99 dbus::MessageWriter
array_writer(NULL
);
100 dbus::MessageWriter
dict_entry_writer(NULL
);
101 dbus::MessageWriter
variant_writer(NULL
);
105 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface
);
107 // changed_properties
108 writer
.OpenArray("{sv}", &array_writer
);
109 array_writer
.OpenDictEntry(&dict_entry_writer
);
110 dict_entry_writer
.AppendString(
111 bluetooth_gatt_characteristic::kValueProperty
);
112 dict_entry_writer
.OpenVariant("ay", &variant_writer
);
113 variant_writer
.AppendArrayOfBytes(value
.data(), value
.size());
114 dict_entry_writer
.CloseContainer(&variant_writer
);
115 array_writer
.CloseContainer(&dict_entry_writer
);
116 writer
.CloseContainer(&array_writer
);
118 // invalidated_properties.
119 writer
.OpenArray("s", &array_writer
);
120 writer
.CloseContainer(&array_writer
);
122 exported_object_
->SendSignal(&signal
);
126 // Returns true if the current thread is on the origin thread.
127 bool OnOriginThread() {
128 return base::PlatformThread::CurrentId() == origin_thread_id_
;
131 // Called by dbus:: when the Bluetooth daemon fetches a single property of
132 // the characteristic.
133 void Get(dbus::MethodCall
* method_call
,
134 dbus::ExportedObject::ResponseSender response_sender
) {
135 VLOG(2) << "BluetoothGattCharacteristicServiceProvider::Get: "
136 << object_path_
.value();
137 DCHECK(OnOriginThread());
139 dbus::MessageReader
reader(method_call
);
141 std::string interface_name
;
142 std::string property_name
;
143 if (!reader
.PopString(&interface_name
) ||
144 !reader
.PopString(&property_name
) ||
145 reader
.HasMoreData()) {
146 scoped_ptr
<dbus::ErrorResponse
> error_response
=
147 dbus::ErrorResponse::FromMethodCall(
148 method_call
, kErrorInvalidArgs
, "Expected 'ss'.");
149 response_sender
.Run(error_response
.Pass());
153 // Only the GATT characteristic interface is supported.
154 if (interface_name
!=
155 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface
) {
156 scoped_ptr
<dbus::ErrorResponse
> error_response
=
157 dbus::ErrorResponse::FromMethodCall(
158 method_call
, kErrorInvalidArgs
,
159 "No such interface: '" + interface_name
+ "'.");
160 response_sender
.Run(error_response
.Pass());
164 // If getting the "Value" property, obtain the value from the delegate.
165 if (property_name
== bluetooth_gatt_characteristic::kValueProperty
) {
167 delegate_
->GetCharacteristicValue(
168 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnGet
,
169 weak_ptr_factory_
.GetWeakPtr(),
170 method_call
, response_sender
),
171 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure
,
172 weak_ptr_factory_
.GetWeakPtr(),
173 method_call
, response_sender
));
177 scoped_ptr
<dbus::Response
> response
=
178 dbus::Response::FromMethodCall(method_call
);
179 dbus::MessageWriter
writer(response
.get());
180 dbus::MessageWriter
variant_writer(NULL
);
182 // TODO(armansito): Process the "Flags" and "Permissions" properties below.
183 if (property_name
== bluetooth_gatt_characteristic::kUUIDProperty
) {
184 writer
.OpenVariant("s", &variant_writer
);
185 variant_writer
.AppendString(uuid_
);
186 writer
.CloseContainer(&variant_writer
);
187 } else if (property_name
==
188 bluetooth_gatt_characteristic::kServiceProperty
) {
189 writer
.OpenVariant("o", &variant_writer
);
190 variant_writer
.AppendObjectPath(service_path_
);
191 writer
.CloseContainer(&variant_writer
);
193 response
= dbus::ErrorResponse::FromMethodCall(
196 "No such property: '" + property_name
+ "'.");
199 response_sender
.Run(response
.Pass());
202 // Called by dbus:: when the Bluetooth daemon sets a single property of the
204 void Set(dbus::MethodCall
* method_call
,
205 dbus::ExportedObject::ResponseSender response_sender
) {
206 VLOG(2) << "BluetoothGattCharacteristicServiceProvider::Set: "
207 << object_path_
.value();
208 DCHECK(OnOriginThread());
210 dbus::MessageReader
reader(method_call
);
212 std::string interface_name
;
213 std::string property_name
;
214 dbus::MessageReader
variant_reader(NULL
);
215 if (!reader
.PopString(&interface_name
) ||
216 !reader
.PopString(&property_name
) ||
217 !reader
.PopVariant(&variant_reader
) ||
218 reader
.HasMoreData()) {
219 scoped_ptr
<dbus::ErrorResponse
> error_response
=
220 dbus::ErrorResponse::FromMethodCall(
221 method_call
, kErrorInvalidArgs
, "Expected 'ssv'.");
222 response_sender
.Run(error_response
.Pass());
226 // Only the GATT characteristic interface is allowed.
227 if (interface_name
!=
228 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface
) {
229 scoped_ptr
<dbus::ErrorResponse
> error_response
=
230 dbus::ErrorResponse::FromMethodCall(
231 method_call
, kErrorInvalidArgs
,
232 "No such interface: '" + interface_name
+ "'.");
233 response_sender
.Run(error_response
.Pass());
237 // Only the "Value" property is writeable.
238 if (property_name
!= bluetooth_gatt_characteristic::kValueProperty
) {
239 std::string error_name
;
240 std::string error_message
;
241 if (property_name
== bluetooth_gatt_characteristic::kUUIDProperty
||
242 property_name
== bluetooth_gatt_characteristic::kServiceProperty
) {
243 error_name
= kErrorPropertyReadOnly
;
244 error_message
= "Read-only property: '" + property_name
+ "'.";
246 error_name
= kErrorInvalidArgs
;
247 error_message
= "No such property: '" + property_name
+ "'.";
249 scoped_ptr
<dbus::ErrorResponse
> error_response
=
250 dbus::ErrorResponse::FromMethodCall(
251 method_call
, error_name
, error_message
);
252 response_sender
.Run(error_response
.Pass());
257 const uint8
* bytes
= NULL
;
259 if (!variant_reader
.PopArrayOfBytes(&bytes
, &length
)) {
260 scoped_ptr
<dbus::ErrorResponse
> error_response
=
261 dbus::ErrorResponse::FromMethodCall(
262 method_call
, kErrorInvalidArgs
,
263 "Property '" + property_name
+ "' has type 'ay'.");
264 response_sender
.Run(error_response
.Pass());
268 // Pass the set request onto the delegate.
269 std::vector
<uint8
> value(bytes
, bytes
+ length
);
271 delegate_
->SetCharacteristicValue(
273 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnSet
,
274 weak_ptr_factory_
.GetWeakPtr(),
275 method_call
, response_sender
),
276 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure
,
277 weak_ptr_factory_
.GetWeakPtr(),
278 method_call
, response_sender
));
281 // Called by dbus:: when the Bluetooth daemon fetches all properties of the
283 void GetAll(dbus::MethodCall
* method_call
,
284 dbus::ExportedObject::ResponseSender response_sender
) {
285 VLOG(2) << "BluetoothGattCharacteristicServiceProvider::GetAll: "
286 << object_path_
.value();
287 DCHECK(OnOriginThread());
289 dbus::MessageReader
reader(method_call
);
291 std::string interface_name
;
292 if (!reader
.PopString(&interface_name
) || reader
.HasMoreData()) {
293 scoped_ptr
<dbus::ErrorResponse
> error_response
=
294 dbus::ErrorResponse::FromMethodCall(
295 method_call
, kErrorInvalidArgs
, "Expected 's'.");
296 response_sender
.Run(error_response
.Pass());
300 // Only the GATT characteristic interface is supported.
301 if (interface_name
!=
302 bluetooth_gatt_characteristic::kBluetoothGattCharacteristicInterface
) {
303 scoped_ptr
<dbus::ErrorResponse
> error_response
=
304 dbus::ErrorResponse::FromMethodCall(
305 method_call
, kErrorInvalidArgs
,
306 "No such interface: '" + interface_name
+ "'.");
307 response_sender
.Run(error_response
.Pass());
311 // Try to obtain the value from the delegate. We will construct the
312 // response in the success callback.
314 delegate_
->GetCharacteristicValue(
315 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnGetAll
,
316 weak_ptr_factory_
.GetWeakPtr(),
317 method_call
, response_sender
),
318 base::Bind(&BluetoothGattCharacteristicServiceProviderImpl::OnFailure
,
319 weak_ptr_factory_
.GetWeakPtr(),
320 method_call
, response_sender
));
323 // Called by dbus:: when a method is exported.
324 void OnExported(const std::string
& interface_name
,
325 const std::string
& method_name
,
327 LOG_IF(WARNING
, !success
) << "Failed to export "
328 << interface_name
<< "." << method_name
;
331 // Called by the Delegate in response to a method to call to get all
332 // properties, in which the delegate has successfully returned the
333 // characteristic value.
334 void OnGetAll(dbus::MethodCall
* method_call
,
335 dbus::ExportedObject::ResponseSender response_sender
,
336 const std::vector
<uint8
>& value
) {
337 VLOG(2) << "Characteristic value obtained from delegate. Responding to "
340 scoped_ptr
<dbus::Response
> response
=
341 dbus::Response::FromMethodCall(method_call
);
342 dbus::MessageWriter
writer(response
.get());
343 dbus::MessageWriter
array_writer(NULL
);
344 dbus::MessageWriter
dict_entry_writer(NULL
);
345 dbus::MessageWriter
variant_writer(NULL
);
347 writer
.OpenArray("{sv}", &array_writer
);
349 array_writer
.OpenDictEntry(&dict_entry_writer
);
350 dict_entry_writer
.AppendString(
351 bluetooth_gatt_characteristic::kUUIDProperty
);
352 dict_entry_writer
.AppendVariantOfString(uuid_
);
353 array_writer
.CloseContainer(&dict_entry_writer
);
355 array_writer
.OpenDictEntry(&dict_entry_writer
);
356 dict_entry_writer
.AppendString(
357 bluetooth_gatt_characteristic::kServiceProperty
);
358 dict_entry_writer
.AppendVariantOfObjectPath(service_path_
);
359 array_writer
.CloseContainer(&dict_entry_writer
);
361 array_writer
.OpenDictEntry(&dict_entry_writer
);
362 dict_entry_writer
.AppendString(
363 bluetooth_gatt_characteristic::kValueProperty
);
364 dict_entry_writer
.OpenVariant("ay", &variant_writer
);
365 variant_writer
.AppendArrayOfBytes(value
.data(), value
.size());
366 dict_entry_writer
.CloseContainer(&variant_writer
);
367 array_writer
.CloseContainer(&dict_entry_writer
);
369 // TODO(armansito): Process Flags & Permissions properties.
371 writer
.CloseContainer(&array_writer
);
373 response_sender
.Run(response
.Pass());
376 // Called by the Delegate in response to a successful method call to get the
377 // characteristic value.
378 void OnGet(dbus::MethodCall
* method_call
,
379 dbus::ExportedObject::ResponseSender response_sender
,
380 const std::vector
<uint8
>& value
) {
381 VLOG(2) << "Returning characteristic value obtained from delegate.";
382 scoped_ptr
<dbus::Response
> response
=
383 dbus::Response::FromMethodCall(method_call
);
384 dbus::MessageWriter
writer(response
.get());
385 dbus::MessageWriter
variant_writer(NULL
);
387 writer
.OpenVariant("ay", &variant_writer
);
388 variant_writer
.AppendArrayOfBytes(value
.data(), value
.size());
389 writer
.CloseContainer(&variant_writer
);
391 response_sender
.Run(response
.Pass());
394 // Called by the Delegate in response to a successful method call to set the
395 // characteristic value.
396 void OnSet(dbus::MethodCall
* method_call
,
397 dbus::ExportedObject::ResponseSender response_sender
) {
398 VLOG(2) << "Successfully set characteristic value. Return success.";
399 response_sender
.Run(dbus::Response::FromMethodCall(method_call
));
402 // Called by the Delegate in response to a failed method call to get or set
403 // the characteristic value.
404 void OnFailure(dbus::MethodCall
* method_call
,
405 dbus::ExportedObject::ResponseSender response_sender
) {
406 VLOG(2) << "Failed to get/set characteristic value. Report error.";
407 scoped_ptr
<dbus::ErrorResponse
> error_response
=
408 dbus::ErrorResponse::FromMethodCall(
409 method_call
, kErrorFailed
,
410 "Failed to get/set characteristic value.");
411 response_sender
.Run(error_response
.Pass());
414 // Origin thread (i.e. the UI thread in production).
415 base::PlatformThreadId origin_thread_id_
;
417 // 128-bit characteristic UUID of this object.
420 // D-Bus bus object is exported on, not owned by this object and must
424 // Incoming methods to get and set the "Value" property are passed on to the
425 // delegate and callbacks passed to generate a reply. |delegate_| is generally
426 // the object that owns this one and must outlive it.
429 // D-Bus object path of object we are exporting, kept so we can unregister
430 // again in our destructor.
431 dbus::ObjectPath object_path_
;
433 // Object path of the GATT service that the exported characteristic belongs
435 dbus::ObjectPath service_path_
;
437 // D-Bus object we are exporting, owned by this object.
438 scoped_refptr
<dbus::ExportedObject
> exported_object_
;
440 // Weak pointer factory for generating 'this' pointers that might live longer
442 // Note: This should remain the last member so it'll be destroyed and
443 // invalidate its weak pointers before any other members are destroyed.
444 base::WeakPtrFactory
<BluetoothGattCharacteristicServiceProviderImpl
>
447 DISALLOW_COPY_AND_ASSIGN(BluetoothGattCharacteristicServiceProviderImpl
);
450 BluetoothGattCharacteristicServiceProvider::
451 BluetoothGattCharacteristicServiceProvider() {
454 BluetoothGattCharacteristicServiceProvider::
455 ~BluetoothGattCharacteristicServiceProvider() {
459 BluetoothGattCharacteristicServiceProvider
*
460 BluetoothGattCharacteristicServiceProvider::Create(
462 const dbus::ObjectPath
& object_path
,
464 const std::string
& uuid
,
465 const std::vector
<std::string
>& flags
,
466 const std::vector
<std::string
>& permissions
,
467 const dbus::ObjectPath
& service_path
) {
468 if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH
)) {
469 return new BluetoothGattCharacteristicServiceProviderImpl(
470 bus
, object_path
, delegate
, uuid
, flags
, permissions
, service_path
);
472 return new FakeBluetoothGattCharacteristicServiceProvider(
473 object_path
, delegate
, uuid
, flags
, permissions
, service_path
);
476 } // namespace chromeos