1 // Copyright 2015 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_le_advertisement_service_provider.h"
8 #include "base/logging.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/stl_util.h"
11 #include "base/threading/platform_thread.h"
12 #include "chromeos/dbus/dbus_thread_manager.h"
13 #include "chromeos/dbus/fake_bluetooth_le_advertisement_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"
21 const char kErrorInvalidArgs
[] = "org.freedesktop.DBus.Error.InvalidArgs";
24 // The BluetoothAdvertisementServiceProvider implementation used in production.
25 class BluetoothAdvertisementServiceProviderImpl
26 : public BluetoothLEAdvertisementServiceProvider
{
28 BluetoothAdvertisementServiceProviderImpl(
30 const dbus::ObjectPath
& object_path
,
32 AdvertisementType type
,
33 scoped_ptr
<UUIDList
> service_uuids
,
34 scoped_ptr
<ManufacturerData
> manufacturer_data
,
35 scoped_ptr
<UUIDList
> solicit_uuids
,
36 scoped_ptr
<ServiceData
> service_data
)
37 : origin_thread_id_(base::PlatformThread::CurrentId()),
40 object_path_(object_path
),
42 service_uuids_(service_uuids
.Pass()),
43 manufacturer_data_(manufacturer_data
.Pass()),
44 solicit_uuids_(solicit_uuids
.Pass()),
45 service_data_(service_data
.Pass()),
46 weak_ptr_factory_(this) {
50 VLOG(1) << "Creating Bluetooth Advertisement: " << object_path_
.value();
52 exported_object_
= bus_
->GetExportedObject(object_path_
);
54 // Export Bluetooth Advertisement interface methods.
55 exported_object_
->ExportMethod(
56 bluetooth_advertisement::kBluetoothAdvertisementIntervace
,
57 bluetooth_advertisement::kRelease
,
58 base::Bind(&BluetoothAdvertisementServiceProviderImpl::Release
,
59 weak_ptr_factory_
.GetWeakPtr()),
60 base::Bind(&BluetoothAdvertisementServiceProviderImpl::OnExported
,
61 weak_ptr_factory_
.GetWeakPtr()));
63 // Export dbus property methods.
64 exported_object_
->ExportMethod(
65 dbus::kDBusPropertiesInterface
, dbus::kDBusPropertiesGet
,
66 base::Bind(&BluetoothAdvertisementServiceProviderImpl::Get
,
67 weak_ptr_factory_
.GetWeakPtr()),
68 base::Bind(&BluetoothAdvertisementServiceProviderImpl::OnExported
,
69 weak_ptr_factory_
.GetWeakPtr()));
71 exported_object_
->ExportMethod(
72 dbus::kDBusPropertiesInterface
, dbus::kDBusPropertiesGetAll
,
73 base::Bind(&BluetoothAdvertisementServiceProviderImpl::GetAll
,
74 weak_ptr_factory_
.GetWeakPtr()),
75 base::Bind(&BluetoothAdvertisementServiceProviderImpl::OnExported
,
76 weak_ptr_factory_
.GetWeakPtr()));
79 ~BluetoothAdvertisementServiceProviderImpl() override
{
80 VLOG(1) << "Cleaning up Bluetooth Advertisement: " << object_path_
.value();
82 // Unregister the object path so we can reuse with a new agent.
83 bus_
->UnregisterExportedObject(object_path_
);
87 // Returns true if the current thread is on the origin thread.
88 bool OnOriginThread() {
89 return base::PlatformThread::CurrentId() == origin_thread_id_
;
92 // Called by dbus:: when this advertisement is unregistered from the Bluetooth
93 // daemon, generally by our request.
94 void Release(dbus::MethodCall
* method_call
,
95 dbus::ExportedObject::ResponseSender response_sender
) {
96 DCHECK(OnOriginThread());
99 delegate_
->Released();
102 // Called by dbus:: when the Bluetooth daemon fetches a single property of
104 void Get(dbus::MethodCall
* method_call
,
105 dbus::ExportedObject::ResponseSender response_sender
) {
106 VLOG(2) << "BluetoothAdvertisementServiceProvider::Get: "
107 << object_path_
.value();
108 DCHECK(OnOriginThread());
110 dbus::MessageReader
reader(method_call
);
112 std::string interface_name
;
113 std::string property_name
;
114 if (!reader
.PopString(&interface_name
) ||
115 !reader
.PopString(&property_name
) || reader
.HasMoreData()) {
116 scoped_ptr
<dbus::ErrorResponse
> error_response
=
117 dbus::ErrorResponse::FromMethodCall(method_call
, kErrorInvalidArgs
,
119 response_sender
.Run(error_response
.Pass());
123 // Only the advertisement interface is supported.
124 if (interface_name
!=
125 bluetooth_advertisement::kBluetoothAdvertisementIntervace
) {
126 scoped_ptr
<dbus::ErrorResponse
> error_response
=
127 dbus::ErrorResponse::FromMethodCall(
128 method_call
, kErrorInvalidArgs
,
129 "No such interface: '" + interface_name
+ "'.");
130 response_sender
.Run(error_response
.Pass());
134 scoped_ptr
<dbus::Response
> response
=
135 dbus::Response::FromMethodCall(method_call
);
136 dbus::MessageWriter
writer(response
.get());
137 dbus::MessageWriter
variant_writer(NULL
);
139 if (property_name
== bluetooth_advertisement::kTypeProperty
) {
140 writer
.OpenVariant("s", &variant_writer
);
141 if (type_
== ADVERTISEMENT_TYPE_BROADCAST
) {
142 variant_writer
.AppendString("broadcast");
144 variant_writer
.AppendString("peripheral");
146 } else if ((property_name
==
147 bluetooth_advertisement::kServiceUUIDsProperty
) &&
149 writer
.OpenVariant("as", &variant_writer
);
150 variant_writer
.AppendArrayOfStrings(*service_uuids_
);
151 } else if ((property_name
==
152 bluetooth_advertisement::kSolicitUUIDsProperty
) &&
154 writer
.OpenVariant("as", &variant_writer
);
155 variant_writer
.AppendArrayOfStrings(*solicit_uuids_
);
156 } else if ((property_name
==
157 bluetooth_advertisement::kManufacturerDataProperty
) &&
158 manufacturer_data_
) {
159 writer
.OpenVariant("o", &variant_writer
);
160 AppendManufacturerDataVariant(&variant_writer
);
161 } else if ((property_name
==
162 bluetooth_advertisement::kServiceDataProperty
) &&
164 writer
.OpenVariant("o", &variant_writer
);
165 AppendServiceDataVariant(&variant_writer
);
167 scoped_ptr
<dbus::ErrorResponse
> error_response
=
168 dbus::ErrorResponse::FromMethodCall(
169 method_call
, kErrorInvalidArgs
,
170 "No such property: '" + property_name
+ "'.");
171 response_sender
.Run(error_response
.Pass());
174 writer
.CloseContainer(&variant_writer
);
175 response_sender
.Run(response
.Pass());
178 // Called by dbus:: when the Bluetooth daemon fetches all properties of the
180 void GetAll(dbus::MethodCall
* method_call
,
181 dbus::ExportedObject::ResponseSender response_sender
) {
182 VLOG(2) << "BluetoothAdvertisementServiceProvider::GetAll: "
183 << object_path_
.value();
184 DCHECK(OnOriginThread());
186 dbus::MessageReader
reader(method_call
);
188 std::string interface_name
;
189 if (!reader
.PopString(&interface_name
) || reader
.HasMoreData()) {
190 scoped_ptr
<dbus::ErrorResponse
> error_response
=
191 dbus::ErrorResponse::FromMethodCall(method_call
, kErrorInvalidArgs
,
193 response_sender
.Run(error_response
.Pass());
197 // Only the advertisement interface is supported.
198 if (interface_name
!=
199 bluetooth_advertisement::kBluetoothAdvertisementIntervace
) {
200 scoped_ptr
<dbus::ErrorResponse
> error_response
=
201 dbus::ErrorResponse::FromMethodCall(
202 method_call
, kErrorInvalidArgs
,
203 "No such interface: '" + interface_name
+ "'.");
204 response_sender
.Run(error_response
.Pass());
208 response_sender
.Run(CreateGetAllResponse(method_call
).Pass());
211 // Called by dbus:: when a method is exported.
212 void OnExported(const std::string
& interface_name
,
213 const std::string
& method_name
,
215 LOG_IF(WARNING
, !success
) << "Failed to export " << interface_name
<< "."
219 // Helper for populating the DBus response with the advertisement data.
220 scoped_ptr
<dbus::Response
> CreateGetAllResponse(
221 dbus::MethodCall
* method_call
) {
222 VLOG(2) << "Descriptor value obtained from delegate. Responding to "
225 scoped_ptr
<dbus::Response
> response
=
226 dbus::Response::FromMethodCall(method_call
);
228 dbus::MessageWriter
writer(response
.get());
229 dbus::MessageWriter
array_writer(NULL
);
231 writer
.OpenArray("{sv}", &array_writer
);
233 AppendType(&array_writer
);
234 AppendServiceUUIDs(&array_writer
);
235 AppendManufacturerData(&array_writer
);
236 AppendSolicitUUIDs(&array_writer
);
237 AppendServiceData(&array_writer
);
239 writer
.CloseContainer(&array_writer
);
243 // Called by the Delegate in response to a successful method call to get the
245 void OnGet(dbus::MethodCall
* method_call
,
246 dbus::ExportedObject::ResponseSender response_sender
,
247 const std::vector
<uint8
>& value
) {
248 VLOG(2) << "Returning descriptor value obtained from delegate.";
249 scoped_ptr
<dbus::Response
> response
=
250 dbus::Response::FromMethodCall(method_call
);
251 dbus::MessageWriter
writer(response
.get());
252 dbus::MessageWriter
variant_writer(NULL
);
254 writer
.OpenVariant("ay", &variant_writer
);
255 variant_writer
.AppendArrayOfBytes(value
.data(), value
.size());
256 writer
.CloseContainer(&variant_writer
);
258 response_sender
.Run(response
.Pass());
261 void AppendType(dbus::MessageWriter
* array_writer
) {
262 dbus::MessageWriter
dict_entry_writer(NULL
);
263 array_writer
->OpenDictEntry(&dict_entry_writer
);
264 dict_entry_writer
.AppendString(bluetooth_advertisement::kTypeProperty
);
265 if (type_
== ADVERTISEMENT_TYPE_BROADCAST
) {
266 dict_entry_writer
.AppendString("broadcast");
268 dict_entry_writer
.AppendString("peripheral");
270 array_writer
->CloseContainer(&dict_entry_writer
);
273 void AppendServiceUUIDs(dbus::MessageWriter
* array_writer
) {
274 dbus::MessageWriter
dict_entry_writer(NULL
);
275 array_writer
->OpenDictEntry(&dict_entry_writer
);
276 dict_entry_writer
.AppendString(
277 bluetooth_advertisement::kServiceUUIDsProperty
);
278 dict_entry_writer
.AppendArrayOfStrings(*service_uuids_
);
279 array_writer
->CloseContainer(&dict_entry_writer
);
282 void AppendManufacturerData(dbus::MessageWriter
* array_writer
) {
283 dbus::MessageWriter
dict_entry_writer(NULL
);
284 array_writer
->OpenDictEntry(&dict_entry_writer
);
285 dict_entry_writer
.AppendString(
286 bluetooth_advertisement::kManufacturerDataProperty
);
287 dbus::MessageWriter
variant_writer(NULL
);
288 dict_entry_writer
.OpenVariant("a{qay}", &variant_writer
);
289 AppendManufacturerDataVariant(&variant_writer
);
290 dict_entry_writer
.CloseContainer(&variant_writer
);
291 array_writer
->CloseContainer(&dict_entry_writer
);
294 void AppendSolicitUUIDs(dbus::MessageWriter
* array_writer
) {
295 dbus::MessageWriter
dict_entry_writer(NULL
);
296 array_writer
->OpenDictEntry(&dict_entry_writer
);
297 dict_entry_writer
.AppendString(
298 bluetooth_advertisement::kSolicitUUIDsProperty
);
299 dict_entry_writer
.AppendArrayOfStrings(*solicit_uuids_
);
300 array_writer
->CloseContainer(&dict_entry_writer
);
303 void AppendServiceData(dbus::MessageWriter
* array_writer
) {
304 dbus::MessageWriter
dict_entry_writer(NULL
);
305 array_writer
->OpenDictEntry(&dict_entry_writer
);
306 dict_entry_writer
.AppendString(
307 bluetooth_advertisement::kServiceDataProperty
);
308 dbus::MessageWriter
variant_writer(NULL
);
309 dict_entry_writer
.OpenVariant("a{say}", &variant_writer
);
310 AppendServiceDataVariant(&variant_writer
);
311 dict_entry_writer
.CloseContainer(&variant_writer
);
312 array_writer
->CloseContainer(&dict_entry_writer
);
315 void AppendManufacturerDataVariant(dbus::MessageWriter
* writer
) {
316 dbus::MessageWriter
array_writer(NULL
);
317 writer
->OpenArray("{qay}", &array_writer
);
318 for (const auto& m
: *manufacturer_data_
) {
319 dbus::MessageWriter
entry_writer(NULL
);
321 array_writer
.OpenDictEntry(&entry_writer
);
323 entry_writer
.AppendUint32(m
.first
);
324 entry_writer
.AppendArrayOfBytes(vector_as_array(&m
.second
),
327 array_writer
.CloseContainer(&entry_writer
);
329 writer
->CloseContainer(&array_writer
);
332 void AppendServiceDataVariant(dbus::MessageWriter
* writer
) {
333 dbus::MessageWriter
array_writer(NULL
);
334 writer
->OpenArray("{say}", &array_writer
);
335 for (const auto& m
: *service_data_
) {
336 dbus::MessageWriter
entry_writer(NULL
);
338 array_writer
.OpenDictEntry(&entry_writer
);
340 entry_writer
.AppendString(m
.first
);
341 entry_writer
.AppendArrayOfBytes(vector_as_array(&m
.second
),
344 array_writer
.CloseContainer(&entry_writer
);
346 writer
->CloseContainer(&array_writer
);
349 // Origin thread (i.e. the UI thread in production).
350 base::PlatformThreadId origin_thread_id_
;
352 // D-Bus bus object is exported on, not owned by this object and must
356 // All incoming method calls are passed on to the Delegate and a callback
357 // passed to generate the reply. |delegate_| is generally the object that
358 // owns this one, and must outlive it.
361 // D-Bus object path of object we are exporting, kept so we can unregister
362 // again in our destructor.
363 dbus::ObjectPath object_path_
;
365 // Advertisement data that needs to be provided to BlueZ when requested.
366 AdvertisementType type_
;
367 scoped_ptr
<UUIDList
> service_uuids_
;
368 scoped_ptr
<ManufacturerData
> manufacturer_data_
;
369 scoped_ptr
<UUIDList
> solicit_uuids_
;
370 scoped_ptr
<ServiceData
> service_data_
;
372 // D-Bus object we are exporting, owned by this object.
373 scoped_refptr
<dbus::ExportedObject
> exported_object_
;
375 // Weak pointer factory for generating 'this' pointers that might live longer
377 // Note: This should remain the last member so it'll be destroyed and
378 // invalidate its weak pointers before any other members are destroyed.
379 base::WeakPtrFactory
<BluetoothAdvertisementServiceProviderImpl
>
382 DISALLOW_COPY_AND_ASSIGN(BluetoothAdvertisementServiceProviderImpl
);
385 BluetoothLEAdvertisementServiceProvider::
386 BluetoothLEAdvertisementServiceProvider() {
389 BluetoothLEAdvertisementServiceProvider::
390 ~BluetoothLEAdvertisementServiceProvider() {
394 BluetoothLEAdvertisementServiceProvider
*
395 BluetoothLEAdvertisementServiceProvider::Create(
397 const dbus::ObjectPath
& object_path
,
399 AdvertisementType type
,
400 scoped_ptr
<UUIDList
> service_uuids
,
401 scoped_ptr
<ManufacturerData
> manufacturer_data
,
402 scoped_ptr
<UUIDList
> solicit_uuids
,
403 scoped_ptr
<ServiceData
> service_data
) {
404 if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH
)) {
405 return new BluetoothAdvertisementServiceProviderImpl(
406 bus
, object_path
, delegate
, type
, service_uuids
.Pass(),
407 manufacturer_data
.Pass(), solicit_uuids
.Pass(), service_data
.Pass());
409 return new FakeBluetoothLEAdvertisementServiceProvider(object_path
,
414 } // namespace chromeos