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.
5 #include "chromeos/dbus/bluetooth_media_endpoint_service_provider.h"
8 #include "base/logging.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/threading/platform_thread.h"
12 #include "chromeos/dbus/bluetooth_media_transport_client.h"
13 #include "chromeos/dbus/dbus_thread_manager.h"
14 #include "chromeos/dbus/fake_bluetooth_media_endpoint_service_provider.h"
15 #include "dbus/exported_object.h"
19 // TODO(mcchou): Move these constants to dbus/service_constants.h.
20 // Bluetooth Media Endpoint service identifier.
21 const char kBluetoothMediaEndpointInterface
[] = "org.bluez.MediaEndpoint1";
23 // Method names in Bluetooth Media Endpoint interface.
24 const char kSetConfiguration
[] = "SetConfiguration";
25 const char kSelectConfiguration
[] = "SelectConfiguration";
26 const char kClearConfiguration
[] = "ClearConfiguration";
27 const char kRelease
[] = "Release";
29 const uint8_t kInvalidCodec
= 0xff;
30 const char kInvalidState
[] = "unknown";
36 // The BluetoothMediaEndopintServiceProvider implementation used in production.
37 class CHROMEOS_EXPORT BluetoothMediaEndpointServiceProviderImpl
38 : public BluetoothMediaEndpointServiceProvider
{
40 BluetoothMediaEndpointServiceProviderImpl(dbus::Bus
* bus
,
41 const dbus::ObjectPath
& object_path
,
43 : origin_thread_id_(base::PlatformThread::CurrentId()),
46 object_path_(object_path
),
47 weak_ptr_factory_(this) {
48 VLOG(1) << "Creating Bluetooth Media Endpoint: " << object_path_
.value();
51 DCHECK(object_path_
.IsValid());
53 exported_object_
= bus_
->GetExportedObject(object_path_
);
55 exported_object_
->ExportMethod(
56 kBluetoothMediaEndpointInterface
,
59 &BluetoothMediaEndpointServiceProviderImpl::SetConfiguration
,
60 weak_ptr_factory_
.GetWeakPtr()),
61 base::Bind(&BluetoothMediaEndpointServiceProviderImpl::OnExported
,
62 weak_ptr_factory_
.GetWeakPtr()));
64 exported_object_
->ExportMethod(
65 kBluetoothMediaEndpointInterface
,
68 &BluetoothMediaEndpointServiceProviderImpl::SelectConfiguration
,
69 weak_ptr_factory_
.GetWeakPtr()),
70 base::Bind(&BluetoothMediaEndpointServiceProviderImpl::OnExported
,
71 weak_ptr_factory_
.GetWeakPtr()));
73 exported_object_
->ExportMethod(
74 kBluetoothMediaEndpointInterface
,
77 &BluetoothMediaEndpointServiceProviderImpl::ClearConfiguration
,
78 weak_ptr_factory_
.GetWeakPtr()),
79 base::Bind(&BluetoothMediaEndpointServiceProviderImpl::OnExported
,
80 weak_ptr_factory_
.GetWeakPtr()));
82 exported_object_
->ExportMethod(
83 kBluetoothMediaEndpointInterface
,
85 base::Bind(&BluetoothMediaEndpointServiceProviderImpl::Release
,
86 weak_ptr_factory_
.GetWeakPtr()),
87 base::Bind(&BluetoothMediaEndpointServiceProviderImpl::OnExported
,
88 weak_ptr_factory_
.GetWeakPtr()));
91 ~BluetoothMediaEndpointServiceProviderImpl() override
{
92 VLOG(1) << "Cleaning up Bluetooth Media Endpoint: "
93 << object_path_
.value();
95 bus_
->UnregisterExportedObject(object_path_
);
99 // Returns true if the current thread is on the origin thread, false
101 bool OnOriginThread() const {
102 return base::PlatformThread::CurrentId() == origin_thread_id_
;
105 // Called by dbus:: when a method is exported.
106 void OnExported(const std::string
& interface_name
,
107 const std::string
& method_name
,
109 LOG_IF(ERROR
, !success
) << "Failed to export " << interface_name
<< "."
113 // Called by dbus:: when the remote device connects to the Media Endpoint.
114 void SetConfiguration(dbus::MethodCall
* method_call
,
115 dbus::ExportedObject::ResponseSender response_sender
) {
116 VLOG(1) << "SetConfiguration";
118 DCHECK(OnOriginThread());
121 dbus::MessageReader
reader(method_call
);
122 dbus::ObjectPath transport_path
;
123 dbus::MessageReader
property_reader(method_call
);
124 if (!reader
.PopObjectPath(&transport_path
) ||
125 !reader
.PopArray(&property_reader
)) {
126 LOG(ERROR
) << "SetConfiguration called with incorrect parameters: "
127 << method_call
->ToString();
131 // Parses |properties| and passes the property set as a
132 // Delegate::TransportProperties structure to |delegate_|.
133 Delegate::TransportProperties properties
;
134 while (property_reader
.HasMoreData()) {
135 dbus::MessageReader
dict_entry_reader(nullptr);
137 if (!property_reader
.PopDictEntry(&dict_entry_reader
) ||
138 !dict_entry_reader
.PopString(&key
)) {
139 LOG(ERROR
) << "SetConfiguration called with incorrect parameters: "
140 << method_call
->ToString();
141 } else if (key
== BluetoothMediaTransportClient::kDeviceProperty
) {
142 dict_entry_reader
.PopVariantOfObjectPath(&properties
.device
);
143 } else if (key
== BluetoothMediaTransportClient::kUUIDProperty
) {
144 dict_entry_reader
.PopVariantOfString(&properties
.uuid
);
145 } else if (key
== BluetoothMediaTransportClient::kCodecProperty
) {
146 dict_entry_reader
.PopVariantOfByte(&properties
.codec
);
147 } else if (key
== BluetoothMediaTransportClient::kConfigurationProperty
) {
148 dbus::MessageReader
variant_reader(nullptr);
149 const uint8_t* bytes
= nullptr;
151 dict_entry_reader
.PopVariant(&variant_reader
);
152 variant_reader
.PopArrayOfBytes(&bytes
, &length
);
153 properties
.configuration
.assign(bytes
, bytes
+ length
);
154 } else if (key
== BluetoothMediaTransportClient::kStateProperty
) {
155 dict_entry_reader
.PopVariantOfString(&properties
.state
);
156 } else if (key
== BluetoothMediaTransportClient::kDelayProperty
) {
157 properties
.delay
.reset(new uint16_t());
158 dict_entry_reader
.PopVariantOfUint16(properties
.delay
.get());
159 } else if (key
== BluetoothMediaTransportClient::kVolumeProperty
) {
160 properties
.volume
.reset(new uint16_t());
161 dict_entry_reader
.PopVariantOfUint16(properties
.volume
.get());
165 if (properties
.codec
!= kInvalidCodec
&&
166 properties
.state
!= kInvalidState
) {
167 delegate_
->SetConfiguration(transport_path
, properties
);
169 LOG(ERROR
) << "SetConfiguration called with incorrect parameters: "
170 << method_call
->ToString();
173 response_sender
.Run(dbus::Response::FromMethodCall(method_call
));
176 // Called by dbus:: when the remote device receives the configuration for
178 void SelectConfiguration(
179 dbus::MethodCall
* method_call
,
180 dbus::ExportedObject::ResponseSender response_sender
) {
181 VLOG(1) << "SelectConfiguration";
183 DCHECK(OnOriginThread());
186 dbus::MessageReader
reader(method_call
);
187 const uint8_t* capabilities
= nullptr;
189 if (!reader
.PopArrayOfBytes(&capabilities
, &length
)) {
190 LOG(ERROR
) << "SelectConfiguration called with incorrect parameters: "
191 << method_call
->ToString();
195 std::vector
<uint8_t> configuration(capabilities
, capabilities
+ length
);
197 // |delegate_| generates the response to |SelectConfiguration| and sends it
198 // back via |callback|.
199 Delegate::SelectConfigurationCallback callback
= base::Bind(
200 &BluetoothMediaEndpointServiceProviderImpl::OnConfiguration
,
201 weak_ptr_factory_
.GetWeakPtr(),
205 delegate_
->SelectConfiguration(configuration
, callback
);
208 // Called by dbus:: when the remote device is about to close the connection.
209 void ClearConfiguration(
210 dbus::MethodCall
* method_call
,
211 dbus::ExportedObject::ResponseSender response_sender
) {
212 VLOG(1) << "ClearConfiguration";
214 DCHECK(OnOriginThread());
217 dbus::MessageReader
reader(method_call
);
218 dbus::ObjectPath transport_path
;
219 if (!reader
.PopObjectPath(&transport_path
)) {
220 LOG(ERROR
) << "ClearConfiguration called with incorrect parameters: "
221 << method_call
->ToString();
225 delegate_
->ClearConfiguration(transport_path
);
227 response_sender
.Run(dbus::Response::FromMethodCall(method_call
));
230 // Called by Bluetooth daemon to do the clean up after unregistering the Media
232 void Release(dbus::MethodCall
* method_call
,
233 dbus::ExportedObject::ResponseSender response_sender
) {
234 VLOG(1) << "Release";
236 DCHECK(OnOriginThread());
239 delegate_
->Released();
241 response_sender
.Run(dbus::Response::FromMethodCall(method_call
));
244 // Called by Delegate to response to a method requiring transport
246 void OnConfiguration(dbus::MethodCall
* method_call
,
247 dbus::ExportedObject::ResponseSender response_sender
,
248 const std::vector
<uint8_t>& configuration
) {
249 VLOG(1) << "OnConfiguration";
251 DCHECK(OnOriginThread());
253 // Generates the response to the method call.
254 scoped_ptr
<dbus::Response
> response(
255 dbus::Response::FromMethodCall(method_call
));
256 dbus::MessageWriter
writer(response
.get());
257 if (configuration
.empty()) {
258 LOG(ERROR
) << "OnConfiguration called with empty configuration.";
259 writer
.AppendArrayOfBytes(nullptr, 0);
261 writer
.AppendArrayOfBytes(&configuration
[0], configuration
.size());
263 response_sender
.Run(response
.Pass());
266 // Origin thread (i.e. the UI thread in production).
267 base::PlatformThreadId origin_thread_id_
;
269 // D-Bus Bus object is exported on.
272 // All incoming method calls are passed on to |delegate_|. |callback| passed
273 // to |delegate+| will generate the response for those methods whose returns
277 // D-Bus object path of the object we are exporting, kept so we can unregister
278 // again in you destructor.
279 dbus::ObjectPath object_path_
;
281 // D-Bus object we are exporting, owned by this object.
282 scoped_refptr
<dbus::ExportedObject
> exported_object_
;
284 // Weak pointer factory for generating 'this' printers that might live longer
286 // Note This should remain the last member so it'll be destroyed and
287 // invalidate it's weak pointers before any other members are destroyed.
288 base::WeakPtrFactory
<BluetoothMediaEndpointServiceProviderImpl
>
291 DISALLOW_COPY_AND_ASSIGN(BluetoothMediaEndpointServiceProviderImpl
);
294 BluetoothMediaEndpointServiceProvider::Delegate::TransportProperties::
295 TransportProperties()
296 : codec(kInvalidCodec
),
297 state(kInvalidState
) {
300 BluetoothMediaEndpointServiceProvider::Delegate::TransportProperties::
301 ~TransportProperties() {
304 BluetoothMediaEndpointServiceProvider::BluetoothMediaEndpointServiceProvider() {
307 BluetoothMediaEndpointServiceProvider::
308 ~BluetoothMediaEndpointServiceProvider() {
311 BluetoothMediaEndpointServiceProvider
*
312 BluetoothMediaEndpointServiceProvider::Create(
314 const dbus::ObjectPath
& object_path
,
315 Delegate
* delegate
) {
316 // Returns a real implementation.
317 if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH
)) {
318 return new BluetoothMediaEndpointServiceProviderImpl(
319 bus
, object_path
, delegate
);
321 // Returns a fake implementation.
322 return new FakeBluetoothMediaEndpointServiceProvider(object_path
, delegate
);
325 } // namespace chromeos