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.
5 #include "device/bluetooth/bluetooth_audio_sink_chromeos.h"
12 #include "base/debug/stack_trace.h"
13 #include "base/logging.h"
14 #include "chromeos/dbus/dbus_thread_manager.h"
15 #include "dbus/message.h"
16 #include "device/bluetooth/bluetooth_adapter_chromeos.h"
18 using dbus::ObjectPath
;
19 using device::BluetoothAudioSink
;
23 // TODO(mcchou): Add the constant to dbus/service_constants.h.
24 const char kBluetoothAudioSinkServicePath
[] = "/org/chromium/AudioSink";
26 ObjectPath
GenerateEndpointPath() {
27 static unsigned int sequence_number
= 0;
29 std::stringstream path
;
30 path
<< kBluetoothAudioSinkServicePath
<< "/endpoint" << sequence_number
;
31 return ObjectPath(path
.str());
34 std::string
StateToString(const BluetoothAudioSink::State
& state
) {
36 case BluetoothAudioSink::STATE_INVALID
:
38 case BluetoothAudioSink::STATE_DISCONNECTED
:
39 return "disconnected";
40 case BluetoothAudioSink::STATE_IDLE
:
42 case BluetoothAudioSink::STATE_PENDING
:
44 case BluetoothAudioSink::STATE_ACTIVE
:
51 std::string
ErrorCodeToString(const BluetoothAudioSink::ErrorCode
& error_code
) {
53 case BluetoothAudioSink::ERROR_UNSUPPORTED_PLATFORM
:
54 return "unsupported platform";
55 case BluetoothAudioSink::ERROR_INVALID_ADAPTER
:
56 return "invalid adapter";
57 case BluetoothAudioSink::ERROR_NOT_REGISTERED
:
58 return "not registered";
59 case BluetoothAudioSink::ERROR_NOT_UNREGISTERED
:
60 return "not unregistered";
66 // A dummy error callback for calling Unregister() in destructor.
67 void UnregisterErrorCallback(
68 device::BluetoothAudioSink::ErrorCode error_code
) {
69 VLOG(1) << "UnregisterErrorCallback - " << ErrorCodeToString(error_code
)
70 << "(" << error_code
<< ")";
77 BluetoothAudioSinkChromeOS::BluetoothAudioSinkChromeOS(
78 scoped_refptr
<device::BluetoothAdapter
> adapter
)
79 : state_(BluetoothAudioSink::STATE_INVALID
),
80 volume_(BluetoothAudioSink::kInvalidVolume
),
84 weak_ptr_factory_(this) {
85 VLOG(1) << "BluetoothAudioSinkChromeOS created";
87 DCHECK(adapter_
.get());
88 DCHECK(adapter_
->IsPresent());
90 adapter_
->AddObserver(this);
92 chromeos::BluetoothMediaClient
* media
=
93 DBusThreadManager::Get()->GetBluetoothMediaClient();
95 media
->AddObserver(this);
97 chromeos::BluetoothMediaTransportClient
*transport
=
98 chromeos::DBusThreadManager::Get()->GetBluetoothMediaTransportClient();
100 transport
->AddObserver(this);
102 StateChanged(device::BluetoothAudioSink::STATE_DISCONNECTED
);
105 BluetoothAudioSinkChromeOS::~BluetoothAudioSinkChromeOS() {
106 VLOG(1) << "BluetoothAudioSinkChromeOS destroyed";
108 DCHECK(adapter_
.get());
110 if (state_
!= BluetoothAudioSink::STATE_INVALID
&& media_endpoint_
.get()) {
111 Unregister(base::Bind(&base::DoNothing
),
112 base::Bind(&UnregisterErrorCallback
));
115 adapter_
->RemoveObserver(this);
117 chromeos::BluetoothMediaClient
* media
=
118 DBusThreadManager::Get()->GetBluetoothMediaClient();
120 media
->RemoveObserver(this);
122 chromeos::BluetoothMediaTransportClient
*transport
=
123 chromeos::DBusThreadManager::Get()->GetBluetoothMediaTransportClient();
125 transport
->RemoveObserver(this);
128 void BluetoothAudioSinkChromeOS::Unregister(
129 const base::Closure
& callback
,
130 const device::BluetoothAudioSink::ErrorCallback
& error_callback
) {
131 VLOG(1) << "Unregister";
133 if (!DBusThreadManager::IsInitialized())
134 error_callback
.Run(BluetoothAudioSink::ERROR_NOT_UNREGISTERED
);
136 chromeos::BluetoothMediaClient
* media
=
137 DBusThreadManager::Get()->GetBluetoothMediaClient();
140 media
->UnregisterEndpoint(
143 base::Bind(&BluetoothAudioSinkChromeOS::OnUnregisterSucceeded
,
144 weak_ptr_factory_
.GetWeakPtr(), callback
),
145 base::Bind(&BluetoothAudioSinkChromeOS::OnUnregisterFailed
,
146 weak_ptr_factory_
.GetWeakPtr(), error_callback
));
149 void BluetoothAudioSinkChromeOS::AddObserver(
150 BluetoothAudioSink::Observer
* observer
) {
152 observers_
.AddObserver(observer
);
155 void BluetoothAudioSinkChromeOS::RemoveObserver(
156 BluetoothAudioSink::Observer
* observer
) {
158 observers_
.RemoveObserver(observer
);
161 BluetoothAudioSink::State
BluetoothAudioSinkChromeOS::GetState() const {
165 uint16_t BluetoothAudioSinkChromeOS::GetVolume() const {
169 void BluetoothAudioSinkChromeOS::AdapterPresentChanged(
170 device::BluetoothAdapter
* adapter
, bool present
) {
171 VLOG(1) << "AdapterPresentChanged: " << present
;
173 if (adapter
->IsPresent()) {
174 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED
);
176 adapter_
->RemoveObserver(this);
177 StateChanged(BluetoothAudioSink::STATE_INVALID
);
181 void BluetoothAudioSinkChromeOS::AdapterPoweredChanged(
182 device::BluetoothAdapter
* adapter
, bool powered
) {
183 VLOG(1) << "AdapterPoweredChanged: " << powered
;
185 // Regardless of the new powered state, |state_| goes to STATE_DISCONNECTED.
186 // If false, the transport is closed, but the endpoint is still valid for use.
187 // If true, the previous transport has been torn down, so the |state_| has to
188 // be disconnected before SetConfigruation is called.
189 if (state_
!= BluetoothAudioSink::STATE_INVALID
)
190 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED
);
193 void BluetoothAudioSinkChromeOS::MediaRemoved(const ObjectPath
& object_path
) {
194 if (object_path
== media_path_
) {
195 VLOG(1) << "MediaRemoved: " << object_path
.value();
196 StateChanged(BluetoothAudioSink::STATE_INVALID
);
200 void BluetoothAudioSinkChromeOS::MediaTransportRemoved(
201 const ObjectPath
& object_path
) {
202 // Whenever powered of |adapter_| turns false while present stays true, media
203 // transport object should be removed accordingly, and the state should be
204 // changed to STATE_DISCONNECTED.
205 if (object_path
== transport_path_
) {
206 VLOG(1) << "MediaTransportRemoved: " << object_path
.value();
207 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED
);
211 void BluetoothAudioSinkChromeOS::MediaTransportPropertyChanged(
212 const ObjectPath
& object_path
,
213 const std::string
& property_name
) {
214 if (object_path
!= transport_path_
)
217 VLOG(1) << "MediaTransportPropertyChanged: " << property_name
;
219 // Retrieves the property set of the transport object with |object_path|.
220 chromeos::BluetoothMediaTransportClient::Properties
* properties
=
221 DBusThreadManager::Get()
222 ->GetBluetoothMediaTransportClient()
223 ->GetProperties(object_path
);
225 // Dispatches a property changed event to the corresponding handler.
226 if (property_name
== properties
->state
.name()) {
227 if (properties
->state
.value() ==
228 BluetoothMediaTransportClient::kStateIdle
) {
229 StateChanged(BluetoothAudioSink::STATE_IDLE
);
230 } else if (properties
->state
.value() ==
231 BluetoothMediaTransportClient::kStatePending
) {
232 StateChanged(BluetoothAudioSink::STATE_PENDING
);
233 } else if (properties
->state
.value() ==
234 BluetoothMediaTransportClient::kStateActive
) {
235 StateChanged(BluetoothAudioSink::STATE_ACTIVE
);
237 } else if (property_name
== properties
->volume
.name()) {
238 VolumeChanged(properties
->volume
.value());
242 void BluetoothAudioSinkChromeOS::SetConfiguration(
243 const ObjectPath
& transport_path
,
244 const TransportProperties
& properties
) {
245 VLOG(1) << "SetConfiguration";
246 transport_path_
= transport_path
;
248 // The initial state for a connection should be "idle".
249 if (properties
.state
!= BluetoothMediaTransportClient::kStateIdle
) {
250 VLOG(1) << "SetConfiugration - unexpected state :" << properties
.state
;
254 // Updates |volume_| if the volume level is provided in |properties|.
255 if (properties
.volume
.get()) {
256 VolumeChanged(*properties
.volume
);
259 StateChanged(BluetoothAudioSink::STATE_IDLE
);
262 void BluetoothAudioSinkChromeOS::SelectConfiguration(
263 const std::vector
<uint8_t>& capabilities
,
264 const SelectConfigurationCallback
& callback
) {
265 VLOG(1) << "SelectConfiguration";
266 callback
.Run(options_
.capabilities
);
269 void BluetoothAudioSinkChromeOS::ClearConfiguration(
270 const ObjectPath
& transport_path
) {
271 if (transport_path
!= transport_path_
)
273 VLOG(1) << "ClearConfiguration";
274 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED
);
277 void BluetoothAudioSinkChromeOS::Released() {
278 VLOG(1) << "Released";
279 StateChanged(BluetoothAudioSink::STATE_INVALID
);
282 void BluetoothAudioSinkChromeOS::Register(
283 const BluetoothAudioSink::Options
& options
,
284 const base::Closure
& callback
,
285 const BluetoothAudioSink::ErrorCallback
& error_callback
) {
286 VLOG(1) << "Register";
288 DCHECK(adapter_
.get());
289 DCHECK_EQ(state_
, BluetoothAudioSink::STATE_DISCONNECTED
);
292 dbus::Bus
* system_bus
= DBusThreadManager::Get()->GetSystemBus();
294 // Creates a Media Endpoint with newly-generated path.
295 endpoint_path_
= GenerateEndpointPath();
296 media_endpoint_
.reset(
297 BluetoothMediaEndpointServiceProvider::Create(
298 system_bus
, endpoint_path_
, this));
300 DCHECK(media_endpoint_
.get());
302 // Creates endpoint properties with |options|.
304 chromeos::BluetoothMediaClient::EndpointProperties endpoint_properties
;
305 endpoint_properties
.uuid
= BluetoothMediaClient::kBluetoothAudioSinkUUID
;
306 endpoint_properties
.codec
= options_
.codec
;
307 endpoint_properties
.capabilities
= options_
.capabilities
;
309 media_path_
= static_cast<BluetoothAdapterChromeOS
*>(
310 adapter_
.get())->object_path();
312 chromeos::BluetoothMediaClient
* media
=
313 DBusThreadManager::Get()->GetBluetoothMediaClient();
315 media
->RegisterEndpoint(
319 base::Bind(&BluetoothAudioSinkChromeOS::OnRegisterSucceeded
,
320 weak_ptr_factory_
.GetWeakPtr(), callback
),
321 base::Bind(&BluetoothAudioSinkChromeOS::OnRegisterFailed
,
322 weak_ptr_factory_
.GetWeakPtr(), error_callback
));
325 BluetoothMediaEndpointServiceProvider
*
326 BluetoothAudioSinkChromeOS::GetEndpointServiceProvider() {
327 return media_endpoint_
.get();
330 void BluetoothAudioSinkChromeOS::StateChanged(
331 BluetoothAudioSink::State state
) {
335 VLOG(1) << "StateChnaged: " << StateToString(state
);
338 case BluetoothAudioSink::STATE_INVALID
:
341 case BluetoothAudioSink::STATE_DISCONNECTED
:
344 case BluetoothAudioSink::STATE_IDLE
:
345 // TODO(mcchou): BUG=441581
346 // Triggered by MediaTransportPropertyChanged and SetConfiguration.
347 // Stop watching on file descriptor if there is one.
349 case BluetoothAudioSink::STATE_PENDING
:
350 // TODO(mcchou): BUG=441581
351 // Call BluetoothMediaTransportClient::Acquire() to get fd and mtus.
353 case BluetoothAudioSink::STATE_ACTIVE
:
354 // TODO(mcchou): BUG=441581
355 // Read from fd and call DataAvailable.
363 FOR_EACH_OBSERVER(BluetoothAudioSink::Observer
, observers_
,
364 BluetoothAudioSinkStateChanged(this, state_
));
367 void BluetoothAudioSinkChromeOS::VolumeChanged(uint16_t volume
) {
368 if (volume
== volume_
)
371 VLOG(1) << "VolumeChanged: " << volume
;
373 volume_
= std::min(volume
, BluetoothAudioSink::kInvalidVolume
);
374 FOR_EACH_OBSERVER(BluetoothAudioSink::Observer
, observers_
,
375 BluetoothAudioSinkVolumeChanged(this, volume_
));
378 void BluetoothAudioSinkChromeOS::OnRegisterSucceeded(
379 const base::Closure
& callback
) {
380 DCHECK(media_endpoint_
.get());
381 VLOG(1) << "OnRegisterSucceeded";
383 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED
);
387 void BluetoothAudioSinkChromeOS::OnRegisterFailed(
388 const BluetoothAudioSink::ErrorCallback
& error_callback
,
389 const std::string
& error_name
,
390 const std::string
& error_message
) {
391 VLOG(1) << "OnRegisterFailed - error name: " << error_name
392 << ", error message: " << error_message
;
395 error_callback
.Run(BluetoothAudioSink::ERROR_NOT_REGISTERED
);
398 void BluetoothAudioSinkChromeOS::OnUnregisterSucceeded(
399 const base::Closure
& callback
) {
400 VLOG(1) << "Unregisterd";
402 // Once the state becomes STATE_INVALID, media, media transport and media
403 // endpoint will be reset.
404 StateChanged(BluetoothAudioSink::STATE_INVALID
);
408 void BluetoothAudioSinkChromeOS::OnUnregisterFailed(
409 const device::BluetoothAudioSink::ErrorCallback
& error_callback
,
410 const std::string
& error_name
,
411 const std::string
& error_message
) {
412 VLOG(1) << "OnUnregisterFailed - error name: " << error_name
413 << ", error message: " << error_message
;
415 error_callback
.Run(BluetoothAudioSink::ERROR_NOT_UNREGISTERED
);
418 void BluetoothAudioSinkChromeOS::ReadFromFD() {
419 DCHECK_GE(fd_
.value(), 0);
421 // TODO(mcchou): BUG=441581
422 // Read from file descriptor using watcher and create a buffer to contain the
423 // data. Notify |Observers_| while there is audio data available.
426 void BluetoothAudioSinkChromeOS::ResetMedia() {
427 VLOG(1) << "ResetMedia";
429 media_path_
= dbus::ObjectPath("");
432 void BluetoothAudioSinkChromeOS::ResetTransport() {
433 VLOG(1) << "ResetTransport";
435 if (transport_path_
.value() == "")
437 transport_path_
= dbus::ObjectPath("");
438 VolumeChanged(BluetoothAudioSink::kInvalidVolume
);
444 void BluetoothAudioSinkChromeOS::ResetEndpoint() {
445 VLOG(1) << "ResetEndpoint";
447 endpoint_path_
= ObjectPath("");
448 media_endpoint_
= nullptr;
451 } // namespace chromeos