OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "chromeos/dbus/bluetooth_le_advertisement_service_provider.h" | |
6 | |
7 #include "base/bind.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" | |
17 | |
18 namespace chromeos { | |
19 | |
20 namespace { | |
21 const char kErrorInvalidArgs[] = "org.freedesktop.DBus.Error.InvalidArgs"; | |
22 } // namespace | |
23 | |
24 // The BluetoothAdvertisementServiceProvider implementation used in production. | |
25 class BluetoothAdvertisementServiceProviderImpl | |
26 : public BluetoothLEAdvertisementServiceProvider { | |
27 public: | |
28 BluetoothAdvertisementServiceProviderImpl( | |
29 dbus::Bus* bus, | |
30 const dbus::ObjectPath& object_path, | |
31 Delegate* delegate, | |
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()), | |
38 bus_(bus), | |
39 delegate_(delegate), | |
40 type_(type), | |
41 service_uuids_(service_uuids.Pass()), | |
42 manufacturer_data_(manufacturer_data.Pass()), | |
43 solicit_uuids_(solicit_uuids.Pass()), | |
44 service_data_(service_data.Pass()), | |
45 weak_ptr_factory_(this) { | |
46 DCHECK(bus); | |
47 DCHECK(delegate); | |
48 | |
49 VLOG(1) << "Creating Bluetooth Advertisement: " << object_path_.value(); | |
50 | |
51 object_path_ = object_path; | |
52 exported_object_ = bus_->GetExportedObject(object_path_); | |
53 | |
54 // Export Bluetooth Advertisement interface methods. | |
55 exported_object_->ExportMethod( | |
56 bluetooth_advertisement::kBluetoothAdvertisementInterface, | |
57 bluetooth_advertisement::kRelease, | |
58 base::Bind(&BluetoothAdvertisementServiceProviderImpl::Release, | |
59 weak_ptr_factory_.GetWeakPtr()), | |
60 base::Bind(&BluetoothAdvertisementServiceProviderImpl::OnExported, | |
61 weak_ptr_factory_.GetWeakPtr())); | |
62 | |
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())); | |
70 | |
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())); | |
77 } | |
78 | |
79 ~BluetoothAdvertisementServiceProviderImpl() override { | |
80 VLOG(1) << "Cleaning up Bluetooth Advertisement: " << object_path_.value(); | |
81 | |
82 // Unregister the object path so we can reuse with a new agent. | |
83 bus_->UnregisterExportedObject(object_path_); | |
84 } | |
85 | |
86 private: | |
87 // Returns true if the current thread is on the origin thread. | |
88 bool OnOriginThread() { | |
89 return base::PlatformThread::CurrentId() == origin_thread_id_; | |
90 } | |
91 | |
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()); | |
97 DCHECK(delegate_); | |
98 | |
99 delegate_->Released(); | |
100 } | |
101 | |
102 // Called by dbus:: when the Bluetooth daemon fetches a single property of | |
103 // the descriptor. | |
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()); | |
109 | |
110 dbus::MessageReader reader(method_call); | |
111 | |
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, | |
118 "Expected 'ss'."); | |
119 response_sender.Run(error_response.Pass()); | |
120 return; | |
121 } | |
122 | |
123 // Only the advertisement interface is supported. | |
124 if (interface_name != | |
125 bluetooth_advertisement::kBluetoothAdvertisementInterface) { | |
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()); | |
131 return; | |
132 } | |
133 | |
134 scoped_ptr<dbus::Response> response = | |
135 dbus::Response::FromMethodCall(method_call); | |
136 dbus::MessageWriter writer(response.get()); | |
137 dbus::MessageWriter variant_writer(NULL); | |
138 | |
139 if (property_name == bluetooth_advertisement::kTypeProperty) { | |
140 writer.OpenVariant("s", &variant_writer); | |
141 if (type_ == ADVERTISEMENT_TYPE_BROADCAST) { | |
142 variant_writer.AppendString("broadcast"); | |
143 } else { | |
144 variant_writer.AppendString("peripheral"); | |
145 } | |
146 } else if ((property_name == | |
147 bluetooth_advertisement::kServiceUUIDsProperty) && | |
148 service_uuids_) { | |
149 writer.OpenVariant("as", &variant_writer); | |
150 variant_writer.AppendArrayOfStrings(*service_uuids_); | |
151 } else if ((property_name == | |
152 bluetooth_advertisement::kSolicitUUIDsProperty) && | |
153 solicit_uuids_) { | |
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) && | |
163 service_data_) { | |
164 writer.OpenVariant("o", &variant_writer); | |
165 AppendServiceDataVariant(&variant_writer); | |
166 } else { | |
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()); | |
172 } | |
173 | |
174 writer.CloseContainer(&variant_writer); | |
175 response_sender.Run(response.Pass()); | |
176 } | |
177 | |
178 // Called by dbus:: when the Bluetooth daemon fetches all properties of the | |
179 // descriptor. | |
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()); | |
185 | |
186 dbus::MessageReader reader(method_call); | |
187 | |
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, | |
192 "Expected 's'."); | |
193 response_sender.Run(error_response.Pass()); | |
194 return; | |
195 } | |
196 | |
197 // Only the advertisement interface is supported. | |
198 if (interface_name != | |
199 bluetooth_advertisement::kBluetoothAdvertisementInterface) { | |
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()); | |
205 return; | |
206 } | |
207 | |
208 response_sender.Run(CreateGetAllResponse(method_call).Pass()); | |
209 } | |
210 | |
211 // Called by dbus:: when a method is exported. | |
212 void OnExported(const std::string& interface_name, | |
213 const std::string& method_name, | |
214 bool success) { | |
215 LOG_IF(WARNING, !success) << "Failed to export " << interface_name << "." | |
216 << method_name; | |
217 } | |
218 | |
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 " | |
223 << "GetAll."; | |
224 | |
225 scoped_ptr<dbus::Response> response = | |
226 dbus::Response::FromMethodCall(method_call); | |
227 | |
228 dbus::MessageWriter writer(response.get()); | |
229 dbus::MessageWriter array_writer(NULL); | |
230 | |
231 writer.OpenArray("{sv}", &array_writer); | |
232 | |
233 AppendType(&array_writer); | |
234 AppendServiceUUIDs(&array_writer); | |
235 AppendManufacturerData(&array_writer); | |
236 AppendSolicitUUIDs(&array_writer); | |
237 AppendServiceData(&array_writer); | |
238 | |
239 writer.CloseContainer(&array_writer); | |
240 return response; | |
241 } | |
242 | |
243 // Called by the Delegate in response to a successful method call to get the | |
244 // descriptor value. | |
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); | |
253 | |
254 writer.OpenVariant("ay", &variant_writer); | |
255 variant_writer.AppendArrayOfBytes(value.data(), value.size()); | |
256 writer.CloseContainer(&variant_writer); | |
257 | |
258 response_sender.Run(response.Pass()); | |
259 } | |
260 | |
261 void AppendArrayVariantOfStrings(dbus::MessageWriter* dict_writer, | |
262 const UUIDList& strings) { | |
263 dbus::MessageWriter strings_array_variant(nullptr); | |
264 dict_writer->OpenVariant("as", &strings_array_variant); | |
265 strings_array_variant.AppendArrayOfStrings(strings); | |
266 dict_writer->CloseContainer(&strings_array_variant); | |
267 } | |
268 | |
269 void AppendType(dbus::MessageWriter* array_writer) { | |
270 dbus::MessageWriter dict_entry_writer(NULL); | |
271 array_writer->OpenDictEntry(&dict_entry_writer); | |
272 dict_entry_writer.AppendString(bluetooth_advertisement::kTypeProperty); | |
273 if (type_ == ADVERTISEMENT_TYPE_BROADCAST) { | |
274 dict_entry_writer.AppendVariantOfString("broadcast"); | |
275 } else { | |
276 dict_entry_writer.AppendVariantOfString("peripheral"); | |
277 } | |
278 array_writer->CloseContainer(&dict_entry_writer); | |
279 } | |
280 | |
281 void AppendServiceUUIDs(dbus::MessageWriter* array_writer) { | |
282 if (!service_uuids_) | |
283 return; | |
284 dbus::MessageWriter dict_entry_writer(NULL); | |
285 array_writer->OpenDictEntry(&dict_entry_writer); | |
286 dict_entry_writer.AppendString( | |
287 bluetooth_advertisement::kServiceUUIDsProperty); | |
288 AppendArrayVariantOfStrings(&dict_entry_writer, *service_uuids_); | |
289 array_writer->CloseContainer(&dict_entry_writer); | |
290 } | |
291 | |
292 void AppendManufacturerData(dbus::MessageWriter* array_writer) { | |
293 if (!manufacturer_data_) | |
294 return; | |
295 dbus::MessageWriter dict_entry_writer(NULL); | |
296 array_writer->OpenDictEntry(&dict_entry_writer); | |
297 dict_entry_writer.AppendString( | |
298 bluetooth_advertisement::kManufacturerDataProperty); | |
299 dbus::MessageWriter variant_writer(NULL); | |
300 dict_entry_writer.OpenVariant("a{qay}", &variant_writer); | |
301 AppendManufacturerDataVariant(&variant_writer); | |
302 dict_entry_writer.CloseContainer(&variant_writer); | |
303 array_writer->CloseContainer(&dict_entry_writer); | |
304 } | |
305 | |
306 void AppendSolicitUUIDs(dbus::MessageWriter* array_writer) { | |
307 if (!solicit_uuids_) | |
308 return; | |
309 dbus::MessageWriter dict_entry_writer(NULL); | |
310 array_writer->OpenDictEntry(&dict_entry_writer); | |
311 dict_entry_writer.AppendString( | |
312 bluetooth_advertisement::kSolicitUUIDsProperty); | |
313 AppendArrayVariantOfStrings(&dict_entry_writer, *solicit_uuids_); | |
314 array_writer->CloseContainer(&dict_entry_writer); | |
315 } | |
316 | |
317 void AppendServiceData(dbus::MessageWriter* array_writer) { | |
318 if (!service_data_) | |
319 return; | |
320 dbus::MessageWriter dict_entry_writer(NULL); | |
321 array_writer->OpenDictEntry(&dict_entry_writer); | |
322 dict_entry_writer.AppendString( | |
323 bluetooth_advertisement::kServiceDataProperty); | |
324 dbus::MessageWriter variant_writer(NULL); | |
325 dict_entry_writer.OpenVariant("a{say}", &variant_writer); | |
326 AppendServiceDataVariant(&variant_writer); | |
327 dict_entry_writer.CloseContainer(&variant_writer); | |
328 array_writer->CloseContainer(&dict_entry_writer); | |
329 } | |
330 | |
331 void AppendManufacturerDataVariant(dbus::MessageWriter* writer) { | |
332 DCHECK(manufacturer_data_); | |
333 dbus::MessageWriter array_writer(NULL); | |
334 writer->OpenArray("{qay}", &array_writer); | |
335 for (const auto& m : *manufacturer_data_) { | |
336 dbus::MessageWriter entry_writer(NULL); | |
337 | |
338 array_writer.OpenDictEntry(&entry_writer); | |
339 | |
340 entry_writer.AppendUint32(m.first); | |
341 entry_writer.AppendArrayOfBytes(vector_as_array(&m.second), | |
342 m.second.size()); | |
343 | |
344 array_writer.CloseContainer(&entry_writer); | |
345 } | |
346 writer->CloseContainer(&array_writer); | |
347 } | |
348 | |
349 void AppendServiceDataVariant(dbus::MessageWriter* writer) { | |
350 DCHECK(service_data_); | |
351 dbus::MessageWriter array_writer(NULL); | |
352 writer->OpenArray("{say}", &array_writer); | |
353 for (const auto& m : *service_data_) { | |
354 dbus::MessageWriter entry_writer(NULL); | |
355 | |
356 array_writer.OpenDictEntry(&entry_writer); | |
357 | |
358 entry_writer.AppendString(m.first); | |
359 entry_writer.AppendArrayOfBytes(vector_as_array(&m.second), | |
360 m.second.size()); | |
361 | |
362 array_writer.CloseContainer(&entry_writer); | |
363 } | |
364 writer->CloseContainer(&array_writer); | |
365 } | |
366 | |
367 // Origin thread (i.e. the UI thread in production). | |
368 base::PlatformThreadId origin_thread_id_; | |
369 | |
370 // D-Bus bus object is exported on, not owned by this object and must | |
371 // outlive it. | |
372 dbus::Bus* bus_; | |
373 | |
374 // All incoming method calls are passed on to the Delegate and a callback | |
375 // passed to generate the reply. |delegate_| is generally the object that | |
376 // owns this one, and must outlive it. | |
377 Delegate* delegate_; | |
378 | |
379 // Advertisement data that needs to be provided to BlueZ when requested. | |
380 AdvertisementType type_; | |
381 scoped_ptr<UUIDList> service_uuids_; | |
382 scoped_ptr<ManufacturerData> manufacturer_data_; | |
383 scoped_ptr<UUIDList> solicit_uuids_; | |
384 scoped_ptr<ServiceData> service_data_; | |
385 | |
386 // D-Bus object we are exporting, owned by this object. | |
387 scoped_refptr<dbus::ExportedObject> exported_object_; | |
388 | |
389 // Weak pointer factory for generating 'this' pointers that might live longer | |
390 // than we do. | |
391 // Note: This should remain the last member so it'll be destroyed and | |
392 // invalidate its weak pointers before any other members are destroyed. | |
393 base::WeakPtrFactory<BluetoothAdvertisementServiceProviderImpl> | |
394 weak_ptr_factory_; | |
395 | |
396 DISALLOW_COPY_AND_ASSIGN(BluetoothAdvertisementServiceProviderImpl); | |
397 }; | |
398 | |
399 BluetoothLEAdvertisementServiceProvider:: | |
400 BluetoothLEAdvertisementServiceProvider() { | |
401 } | |
402 | |
403 BluetoothLEAdvertisementServiceProvider:: | |
404 ~BluetoothLEAdvertisementServiceProvider() { | |
405 } | |
406 | |
407 // static | |
408 scoped_ptr<BluetoothLEAdvertisementServiceProvider> | |
409 BluetoothLEAdvertisementServiceProvider::Create( | |
410 dbus::Bus* bus, | |
411 const dbus::ObjectPath& object_path, | |
412 Delegate* delegate, | |
413 AdvertisementType type, | |
414 scoped_ptr<UUIDList> service_uuids, | |
415 scoped_ptr<ManufacturerData> manufacturer_data, | |
416 scoped_ptr<UUIDList> solicit_uuids, | |
417 scoped_ptr<ServiceData> service_data) { | |
418 if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH)) { | |
419 return make_scoped_ptr(new BluetoothAdvertisementServiceProviderImpl( | |
420 bus, object_path, delegate, type, service_uuids.Pass(), | |
421 manufacturer_data.Pass(), solicit_uuids.Pass(), service_data.Pass())); | |
422 } else { | |
423 return make_scoped_ptr( | |
424 new FakeBluetoothLEAdvertisementServiceProvider(object_path, delegate)); | |
425 } | |
426 } | |
427 | |
428 } // namespace chromeos | |
OLD | NEW |