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
18 namespace chromeos {
19 namespace {
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";
26 } // namespace
27
28 // The BluetoothGattCharacteristicServiceProvider implementation used in
29 // production.
30 class BluetoothGattCharacteristicServiceProviderImpl
31 : public BluetoothGattCharacteristicServiceProvider {
32 public:
BluetoothGattCharacteristicServiceProviderImpl(dbus::Bus * bus,const dbus::ObjectPath & object_path,Delegate * delegate,const std::string & uuid,const std::vector<std::string> & flags,const std::vector<std::string> & permissions,const dbus::ObjectPath & service_path)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
~BluetoothGattCharacteristicServiceProviderImpl()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.
SendValueChanged(const std::vector<uint8> & value)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.
OnOriginThread()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.
Get(dbus::MethodCall * method_call,dbus::ExportedObject::ResponseSender response_sender)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.
Set(dbus::MethodCall * method_call,dbus::ExportedObject::ResponseSender response_sender)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.
GetAll(dbus::MethodCall * method_call,dbus::ExportedObject::ResponseSender response_sender)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.
OnExported(const std::string & interface_name,const std::string & method_name,bool success)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.
OnGetAll(dbus::MethodCall * method_call,dbus::ExportedObject::ResponseSender response_sender,const std::vector<uint8> & 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.
OnGet(dbus::MethodCall * method_call,dbus::ExportedObject::ResponseSender response_sender,const std::vector<uint8> & 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.
OnSet(dbus::MethodCall * method_call,dbus::ExportedObject::ResponseSender response_sender)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.
OnFailure(dbus::MethodCall * method_call,dbus::ExportedObject::ResponseSender response_sender)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
449 BluetoothGattCharacteristicServiceProvider::
BluetoothGattCharacteristicServiceProvider()450 BluetoothGattCharacteristicServiceProvider() {
451 }
452
453 BluetoothGattCharacteristicServiceProvider::
~BluetoothGattCharacteristicServiceProvider()454 ~BluetoothGattCharacteristicServiceProvider() {
455 }
456
457 // static
458 BluetoothGattCharacteristicServiceProvider*
Create(dbus::Bus * bus,const dbus::ObjectPath & object_path,Delegate * delegate,const std::string & uuid,const std::vector<std::string> & flags,const std::vector<std::string> & permissions,const dbus::ObjectPath & service_path)459 BluetoothGattCharacteristicServiceProvider::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