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(WARNING
, !success
) << "Failed to export "
110 << interface_name
<< "." << method_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 DCHECK(OnOriginThread());
119 dbus::MessageReader
reader(method_call
);
120 dbus::ObjectPath transport_path
;
121 dbus::MessageReader
property_reader(method_call
);
122 if (!reader
.PopObjectPath(&transport_path
) ||
123 !reader
.PopArray(&property_reader
)) {
124 LOG(WARNING
) << "SetConfiguration called with incorrect parameters: "
125 << method_call
->ToString();
129 // Parses |properties| and passes the property set as a
130 // Delegate::TransportProperties structure to |delegate_|.
131 Delegate::TransportProperties properties
;
132 while (property_reader
.HasMoreData()) {
133 dbus::MessageReader
dict_entry_reader(nullptr);
135 if (!property_reader
.PopDictEntry(&dict_entry_reader
) ||
136 !dict_entry_reader
.PopString(&key
)) {
137 LOG(WARNING
) << "SetConfiguration called with incorrect parameters: "
138 << method_call
->ToString();
139 } else if (key
== BluetoothMediaTransportClient::kDeviceProperty
) {
140 dict_entry_reader
.PopVariantOfObjectPath(&properties
.device
);
141 } else if (key
== BluetoothMediaTransportClient::kUUIDProperty
) {
142 dict_entry_reader
.PopVariantOfString(&properties
.uuid
);
143 } else if (key
== BluetoothMediaTransportClient::kCodecProperty
) {
144 dict_entry_reader
.PopVariantOfByte(&properties
.codec
);
145 } else if (key
== BluetoothMediaTransportClient::kConfigurationProperty
) {
146 dbus::MessageReader
variant_reader(nullptr);
147 const uint8_t* bytes
= nullptr;
149 dict_entry_reader
.PopVariant(&variant_reader
);
150 variant_reader
.PopArrayOfBytes(&bytes
, &length
);
151 properties
.configuration
.assign(bytes
, bytes
+ length
);
152 } else if (key
== BluetoothMediaTransportClient::kStateProperty
) {
153 dict_entry_reader
.PopVariantOfString(&properties
.state
);
154 } else if (key
== BluetoothMediaTransportClient::kDelayProperty
) {
155 properties
.delay
.reset(new uint16_t());
156 dict_entry_reader
.PopVariantOfUint16(properties
.delay
.get());
157 } else if (key
== BluetoothMediaTransportClient::kVolumeProperty
) {
158 properties
.volume
.reset(new uint16_t());
159 dict_entry_reader
.PopVariantOfUint16(properties
.volume
.get());
163 if (properties
.codec
!= kInvalidCodec
&&
164 properties
.state
!= kInvalidState
) {
165 delegate_
->SetConfiguration(transport_path
, properties
);
167 LOG(WARNING
) << "SetConfiguration called with incorrect parameters: "
168 << method_call
->ToString();
171 response_sender
.Run(dbus::Response::FromMethodCall(method_call
));
174 // Called by dbus:: when the remote device receives the configuration for
176 void SelectConfiguration(
177 dbus::MethodCall
* method_call
,
178 dbus::ExportedObject::ResponseSender response_sender
) {
179 DCHECK(OnOriginThread());
182 dbus::MessageReader
reader(method_call
);
183 const uint8_t* capabilities
= nullptr;
185 if (!reader
.PopArrayOfBytes(&capabilities
, &length
)) {
186 LOG(WARNING
) << "SelectConfiguration called with incorrect parameters: "
187 << method_call
->ToString();
191 std::vector
<uint8_t> configuration(capabilities
, capabilities
+ length
);
193 // |delegate_| generates the response to |SelectConfiguration| and sends it
194 // back via |callback|.
195 Delegate::SelectConfigurationCallback callback
= base::Bind(
196 &BluetoothMediaEndpointServiceProviderImpl::OnConfiguration
,
197 weak_ptr_factory_
.GetWeakPtr(),
201 delegate_
->SelectConfiguration(configuration
, callback
);
204 // Called by dbus:: when the remote device is about to close the connection.
205 void ClearConfiguration(
206 dbus::MethodCall
* method_call
,
207 dbus::ExportedObject::ResponseSender response_sender
) {
208 DCHECK(OnOriginThread());
211 dbus::MessageReader
reader(method_call
);
212 dbus::ObjectPath transport_path
;
213 if (!reader
.PopObjectPath(&transport_path
)) {
214 LOG(WARNING
) << "ClearConfiguration called with incorrect parameters: "
215 << method_call
->ToString();
219 delegate_
->ClearConfiguration(transport_path
);
221 response_sender
.Run(dbus::Response::FromMethodCall(method_call
));
224 // Called by Bluetooth daemon to do the clean up after unregistering the Media
226 void Release(dbus::MethodCall
* method_call
,
227 dbus::ExportedObject::ResponseSender response_sender
) {
228 DCHECK(OnOriginThread());
231 delegate_
->Released();
233 response_sender
.Run(dbus::Response::FromMethodCall(method_call
));
236 // Called by Delegate to response to a method requiring transport
238 void OnConfiguration(dbus::MethodCall
* method_call
,
239 dbus::ExportedObject::ResponseSender response_sender
,
240 const std::vector
<uint8_t>& configuration
) {
241 DCHECK(OnOriginThread());
243 // Generates the response to the method call.
244 scoped_ptr
<dbus::Response
> response(
245 dbus::Response::FromMethodCall(method_call
));
246 dbus::MessageWriter
writer(response
.get());
247 if (configuration
.empty()) {
248 LOG(WARNING
) << "OnConfiguration called with empty configuration.";
249 writer
.AppendArrayOfBytes(nullptr, 0);
251 writer
.AppendArrayOfBytes(&configuration
[0], configuration
.size());
253 response_sender
.Run(response
.Pass());
256 // Origin thread (i.e. the UI thread in production).
257 base::PlatformThreadId origin_thread_id_
;
259 // D-Bus Bus object is exported on.
262 // All incoming method calls are passed on to |delegate_|. |callback| passed
263 // to |delegate+| will generate the response for those methods whose returns
267 // D-Bus object path of the object we are exporting, kept so we can unregister
268 // again in you destructor.
269 dbus::ObjectPath object_path_
;
271 // D-Bus object we are exporting, owned by this object.
272 scoped_refptr
<dbus::ExportedObject
> exported_object_
;
274 // Weak pointer factory for generating 'this' printers that might live longer
276 // Note This should remain the last member so it'll be destroyed and
277 // invalidate it's weak pointers before any other members are destroyed.
278 base::WeakPtrFactory
<BluetoothMediaEndpointServiceProviderImpl
>
281 DISALLOW_COPY_AND_ASSIGN(BluetoothMediaEndpointServiceProviderImpl
);
284 BluetoothMediaEndpointServiceProvider::Delegate::TransportProperties::
285 TransportProperties()
286 : codec(kInvalidCodec
),
287 state(kInvalidState
) {
290 BluetoothMediaEndpointServiceProvider::Delegate::TransportProperties::
291 ~TransportProperties() {
294 BluetoothMediaEndpointServiceProvider::BluetoothMediaEndpointServiceProvider() {
297 BluetoothMediaEndpointServiceProvider::
298 ~BluetoothMediaEndpointServiceProvider() {
301 BluetoothMediaEndpointServiceProvider
*
302 BluetoothMediaEndpointServiceProvider::Create(
304 const dbus::ObjectPath
& object_path
,
305 Delegate
* delegate
) {
306 // Returns a real implementation.
307 if (!DBusThreadManager::Get()->IsUsingStub(DBusClientBundle::BLUETOOTH
)) {
308 return new BluetoothMediaEndpointServiceProviderImpl(
309 bus
, object_path
, delegate
);
311 // Returns a fake implementation.
312 return new FakeBluetoothMediaEndpointServiceProvider(object_path
, delegate
);
315 } // namespace chromeos