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 "device/bluetooth/dbus/bluetooth_media_endpoint_service_provider.h"
6
7 #include <stddef.h>
8
9 #include <memory>
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/threading/platform_thread.h"
17 #include "dbus/exported_object.h"
18 #include "device/bluetooth/dbus/bluetooth_media_transport_client.h"
19 #include "device/bluetooth/dbus/bluez_dbus_manager.h"
20 #include "device/bluetooth/dbus/fake_bluetooth_media_endpoint_service_provider.h"
21
22 namespace {
23
24 // TODO(mcchou): Move these constants to dbus/service_constants.h.
25 // Bluetooth Media Endpoint service identifier.
26 const char kBluetoothMediaEndpointInterface[] = "org.bluez.MediaEndpoint1";
27
28 // Method names in Bluetooth Media Endpoint interface.
29 const char kSetConfiguration[] = "SetConfiguration";
30 const char kSelectConfiguration[] = "SelectConfiguration";
31 const char kClearConfiguration[] = "ClearConfiguration";
32 const char kRelease[] = "Release";
33
34 const uint8_t kInvalidCodec = 0xff;
35 const char kInvalidState[] = "unknown";
36
37 } // namespace
38
39 namespace bluez {
40
41 // The BluetoothMediaEndopintServiceProvider implementation used in production.
42 class DEVICE_BLUETOOTH_EXPORT BluetoothMediaEndpointServiceProviderImpl
43 : public BluetoothMediaEndpointServiceProvider {
44 public:
BluetoothMediaEndpointServiceProviderImpl(dbus::Bus * bus,const dbus::ObjectPath & object_path,Delegate * delegate)45 BluetoothMediaEndpointServiceProviderImpl(dbus::Bus* bus,
46 const dbus::ObjectPath& object_path,
47 Delegate* delegate)
48 : origin_thread_id_(base::PlatformThread::CurrentId()),
49 bus_(bus),
50 delegate_(delegate),
51 object_path_(object_path) {
52 DVLOG(1) << "Creating Bluetooth Media Endpoint: " << object_path_.value();
53 DCHECK(bus_);
54 DCHECK(delegate_);
55 DCHECK(object_path_.IsValid());
56
57 exported_object_ = bus_->GetExportedObject(object_path_);
58
59 exported_object_->ExportMethod(
60 kBluetoothMediaEndpointInterface, kSetConfiguration,
61 base::Bind(&BluetoothMediaEndpointServiceProviderImpl::SetConfiguration,
62 weak_ptr_factory_.GetWeakPtr()),
63 base::Bind(&BluetoothMediaEndpointServiceProviderImpl::OnExported,
64 weak_ptr_factory_.GetWeakPtr()));
65
66 exported_object_->ExportMethod(
67 kBluetoothMediaEndpointInterface, kSelectConfiguration,
68 base::Bind(
69 &BluetoothMediaEndpointServiceProviderImpl::SelectConfiguration,
70 weak_ptr_factory_.GetWeakPtr()),
71 base::Bind(&BluetoothMediaEndpointServiceProviderImpl::OnExported,
72 weak_ptr_factory_.GetWeakPtr()));
73
74 exported_object_->ExportMethod(
75 kBluetoothMediaEndpointInterface, kClearConfiguration,
76 base::Bind(
77 &BluetoothMediaEndpointServiceProviderImpl::ClearConfiguration,
78 weak_ptr_factory_.GetWeakPtr()),
79 base::Bind(&BluetoothMediaEndpointServiceProviderImpl::OnExported,
80 weak_ptr_factory_.GetWeakPtr()));
81
82 exported_object_->ExportMethod(
83 kBluetoothMediaEndpointInterface, kRelease,
84 base::Bind(&BluetoothMediaEndpointServiceProviderImpl::Release,
85 weak_ptr_factory_.GetWeakPtr()),
86 base::Bind(&BluetoothMediaEndpointServiceProviderImpl::OnExported,
87 weak_ptr_factory_.GetWeakPtr()));
88 }
89
~BluetoothMediaEndpointServiceProviderImpl()90 ~BluetoothMediaEndpointServiceProviderImpl() override {
91 DVLOG(1) << "Cleaning up Bluetooth Media Endpoint: "
92 << object_path_.value();
93
94 bus_->UnregisterExportedObject(object_path_);
95 }
96
97 private:
98 // Returns true if the current thread is on the origin thread, false
99 // otherwise.
OnOriginThread() const100 bool OnOriginThread() const {
101 return base::PlatformThread::CurrentId() == origin_thread_id_;
102 }
103
104 // Called by dbus:: when a method is exported.
OnExported(const std::string & interface_name,const std::string & method_name,bool success)105 void OnExported(const std::string& interface_name,
106 const std::string& method_name,
107 bool success) {
108 DVLOG_IF(1, !success) << "Failed to export " << interface_name << "."
109 << method_name;
110 }
111
112 // Called by dbus:: when the remote device connects to the Media Endpoint.
SetConfiguration(dbus::MethodCall * method_call,dbus::ExportedObject::ResponseSender response_sender)113 void SetConfiguration(dbus::MethodCall* method_call,
114 dbus::ExportedObject::ResponseSender response_sender) {
115 DVLOG(1) << "SetConfiguration";
116
117 DCHECK(OnOriginThread());
118 DCHECK(delegate_);
119
120 dbus::MessageReader reader(method_call);
121 dbus::ObjectPath transport_path;
122 dbus::MessageReader property_reader(method_call);
123 if (!reader.PopObjectPath(&transport_path) ||
124 !reader.PopArray(&property_reader)) {
125 LOG(ERROR) << "SetConfiguration called with incorrect parameters: "
126 << method_call->ToString();
127 return;
128 }
129
130 // Parses |properties| and passes the property set as a
131 // Delegate::TransportProperties structure to |delegate_|.
132 Delegate::TransportProperties properties;
133 while (property_reader.HasMoreData()) {
134 dbus::MessageReader dict_entry_reader(nullptr);
135 std::string key;
136 if (!property_reader.PopDictEntry(&dict_entry_reader) ||
137 !dict_entry_reader.PopString(&key)) {
138 LOG(ERROR) << "SetConfiguration called with incorrect parameters: "
139 << method_call->ToString();
140 } else if (key == BluetoothMediaTransportClient::kDeviceProperty) {
141 dict_entry_reader.PopVariantOfObjectPath(&properties.device);
142 } else if (key == BluetoothMediaTransportClient::kUUIDProperty) {
143 dict_entry_reader.PopVariantOfString(&properties.uuid);
144 } else if (key == BluetoothMediaTransportClient::kCodecProperty) {
145 dict_entry_reader.PopVariantOfByte(&properties.codec);
146 } else if (key == BluetoothMediaTransportClient::kConfigurationProperty) {
147 dbus::MessageReader variant_reader(nullptr);
148 const uint8_t* bytes = nullptr;
149 size_t length = 0;
150 dict_entry_reader.PopVariant(&variant_reader);
151 variant_reader.PopArrayOfBytes(&bytes, &length);
152 properties.configuration.assign(bytes, bytes + length);
153 } else if (key == BluetoothMediaTransportClient::kStateProperty) {
154 dict_entry_reader.PopVariantOfString(&properties.state);
155 } else if (key == BluetoothMediaTransportClient::kDelayProperty) {
156 properties.delay.reset(new uint16_t());
157 dict_entry_reader.PopVariantOfUint16(properties.delay.get());
158 } else if (key == BluetoothMediaTransportClient::kVolumeProperty) {
159 properties.volume.reset(new uint16_t());
160 dict_entry_reader.PopVariantOfUint16(properties.volume.get());
161 }
162 }
163
164 if (properties.codec != kInvalidCodec &&
165 properties.state != kInvalidState) {
166 delegate_->SetConfiguration(transport_path, properties);
167 } else {
168 LOG(ERROR) << "SetConfiguration called with incorrect parameters: "
169 << method_call->ToString();
170 }
171
172 std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
173 }
174
175 // Called by dbus:: when the remote device receives the configuration for
176 // media transport.
SelectConfiguration(dbus::MethodCall * method_call,dbus::ExportedObject::ResponseSender response_sender)177 void SelectConfiguration(
178 dbus::MethodCall* method_call,
179 dbus::ExportedObject::ResponseSender response_sender) {
180 DVLOG(1) << "SelectConfiguration";
181
182 DCHECK(OnOriginThread());
183 DCHECK(delegate_);
184
185 dbus::MessageReader reader(method_call);
186 const uint8_t* capabilities = nullptr;
187 size_t length = 0;
188 if (!reader.PopArrayOfBytes(&capabilities, &length)) {
189 LOG(ERROR) << "SelectConfiguration called with incorrect parameters: "
190 << method_call->ToString();
191 return;
192 }
193
194 std::vector<uint8_t> configuration(capabilities, capabilities + length);
195
196 // |delegate_| generates the response to |SelectConfiguration| and sends it
197 // back via |callback|.
198 Delegate::SelectConfigurationCallback callback =
199 base::Bind(&BluetoothMediaEndpointServiceProviderImpl::OnConfiguration,
200 weak_ptr_factory_.GetWeakPtr(), method_call,
201 base::Passed(&response_sender));
202
203 delegate_->SelectConfiguration(configuration, callback);
204 }
205
206 // Called by dbus:: when the remote device is about to close the connection.
ClearConfiguration(dbus::MethodCall * method_call,dbus::ExportedObject::ResponseSender response_sender)207 void ClearConfiguration(
208 dbus::MethodCall* method_call,
209 dbus::ExportedObject::ResponseSender response_sender) {
210 DVLOG(1) << "ClearConfiguration";
211
212 DCHECK(OnOriginThread());
213 DCHECK(delegate_);
214
215 dbus::MessageReader reader(method_call);
216 dbus::ObjectPath transport_path;
217 if (!reader.PopObjectPath(&transport_path)) {
218 LOG(ERROR) << "ClearConfiguration called with incorrect parameters: "
219 << method_call->ToString();
220 return;
221 }
222
223 delegate_->ClearConfiguration(transport_path);
224
225 std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
226 }
227
228 // Called by Bluetooth daemon to do the clean up after unregistering the Media
229 // Endpoint.
Release(dbus::MethodCall * method_call,dbus::ExportedObject::ResponseSender response_sender)230 void Release(dbus::MethodCall* method_call,
231 dbus::ExportedObject::ResponseSender response_sender) {
232 DVLOG(1) << "Release";
233
234 DCHECK(OnOriginThread());
235 DCHECK(delegate_);
236
237 delegate_->Released();
238
239 std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call));
240 }
241
242 // Called by Delegate to response to a method requiring transport
243 // configuration.
OnConfiguration(dbus::MethodCall * method_call,dbus::ExportedObject::ResponseSender response_sender,const std::vector<uint8_t> & configuration)244 void OnConfiguration(dbus::MethodCall* method_call,
245 dbus::ExportedObject::ResponseSender response_sender,
246 const std::vector<uint8_t>& configuration) {
247 DVLOG(1) << "OnConfiguration";
248
249 DCHECK(OnOriginThread());
250
251 // Generates the response to the method call.
252 std::unique_ptr<dbus::Response> response(
253 dbus::Response::FromMethodCall(method_call));
254 dbus::MessageWriter writer(response.get());
255 if (configuration.empty()) {
256 LOG(ERROR) << "OnConfiguration called with empty configuration.";
257 writer.AppendArrayOfBytes(nullptr, 0);
258 } else {
259 writer.AppendArrayOfBytes(&configuration[0], configuration.size());
260 }
261 std::move(response_sender).Run(std::move(response));
262 }
263
264 // Origin thread (i.e. the UI thread in production).
265 base::PlatformThreadId origin_thread_id_;
266
267 // D-Bus Bus object is exported on.
268 dbus::Bus* bus_;
269
270 // All incoming method calls are passed on to |delegate_|. |callback| passed
271 // to |delegate+| will generate the response for those methods whose returns
272 // are non-void.
273 Delegate* delegate_;
274
275 // D-Bus object path of the object we are exporting, kept so we can unregister
276 // again in you destructor.
277 dbus::ObjectPath object_path_;
278
279 // D-Bus object we are exporting, owned by this object.
280 scoped_refptr<dbus::ExportedObject> exported_object_;
281
282 // Weak pointer factory for generating 'this' printers that might live longer
283 // than we do.
284 // Note This should remain the last member so it'll be destroyed and
285 // invalidate it's weak pointers before any other members are destroyed.
286 base::WeakPtrFactory<BluetoothMediaEndpointServiceProviderImpl>
287 weak_ptr_factory_{this};
288
289 DISALLOW_COPY_AND_ASSIGN(BluetoothMediaEndpointServiceProviderImpl);
290 };
291
292 BluetoothMediaEndpointServiceProvider::Delegate::TransportProperties::
TransportProperties()293 TransportProperties()
294 : codec(kInvalidCodec), state(kInvalidState) {}
295
296 BluetoothMediaEndpointServiceProvider::Delegate::TransportProperties::
297 ~TransportProperties() = default;
298
299 BluetoothMediaEndpointServiceProvider::BluetoothMediaEndpointServiceProvider() =
300 default;
301
302 BluetoothMediaEndpointServiceProvider::
303 ~BluetoothMediaEndpointServiceProvider() = default;
304
305 BluetoothMediaEndpointServiceProvider*
Create(dbus::Bus * bus,const dbus::ObjectPath & object_path,Delegate * delegate)306 BluetoothMediaEndpointServiceProvider::Create(
307 dbus::Bus* bus,
308 const dbus::ObjectPath& object_path,
309 Delegate* delegate) {
310 // Returns a real implementation.
311 if (!bluez::BluezDBusManager::Get()->IsUsingFakes()) {
312 return new BluetoothMediaEndpointServiceProviderImpl(bus, object_path,
313 delegate);
314 }
315 // Returns a fake implementation.
316 return new FakeBluetoothMediaEndpointServiceProvider(object_path, delegate);
317 }
318
319 } // namespace bluez
320