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