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"
14 #include "base/debug/stack_trace.h"
15 #include "base/files/file_util.h"
16 #include "base/logging.h"
17 #include "chromeos/dbus/dbus_thread_manager.h"
18 #include "dbus/message.h"
19 #include "device/bluetooth/bluetooth_adapter_chromeos.h"
21 using dbus::ObjectPath
;
22 using device::BluetoothAudioSink
;
26 // TODO(mcchou): Add the constant to dbus/service_constants.h.
27 const char kBluetoothAudioSinkServicePath
[] = "/org/chromium/AudioSink";
29 const int kInvalidFd
= -1;
30 const uint16_t kInvalidReadMtu
= 0;
31 const uint16_t kInvalidWriteMtu
= 0;
33 ObjectPath
GenerateEndpointPath() {
34 static unsigned int sequence_number
= 0;
36 std::stringstream path
;
37 path
<< kBluetoothAudioSinkServicePath
<< "/endpoint" << sequence_number
;
38 return ObjectPath(path
.str());
41 std::string
StateToString(const BluetoothAudioSink::State
& state
) {
43 case BluetoothAudioSink::STATE_INVALID
:
45 case BluetoothAudioSink::STATE_DISCONNECTED
:
46 return "disconnected";
47 case BluetoothAudioSink::STATE_IDLE
:
49 case BluetoothAudioSink::STATE_PENDING
:
51 case BluetoothAudioSink::STATE_ACTIVE
:
58 std::string
ErrorCodeToString(const BluetoothAudioSink::ErrorCode
& error_code
) {
60 case BluetoothAudioSink::ERROR_UNSUPPORTED_PLATFORM
:
61 return "unsupported platform";
62 case BluetoothAudioSink::ERROR_INVALID_ADAPTER
:
63 return "invalid adapter";
64 case BluetoothAudioSink::ERROR_NOT_REGISTERED
:
65 return "not registered";
66 case BluetoothAudioSink::ERROR_NOT_UNREGISTERED
:
67 return "not unregistered";
73 // A dummy error callback for calling Unregister() in destructor.
74 void UnregisterErrorCallback(
75 device::BluetoothAudioSink::ErrorCode error_code
) {
76 VLOG(1) << "UnregisterErrorCallback - " << ErrorCodeToString(error_code
)
77 << "(" << error_code
<< ")";
84 BluetoothAudioSinkChromeOS::BluetoothAudioSinkChromeOS(
85 scoped_refptr
<device::BluetoothAdapter
> adapter
)
86 : state_(BluetoothAudioSink::STATE_INVALID
),
87 volume_(BluetoothAudioSink::kInvalidVolume
),
88 read_mtu_(kInvalidReadMtu
),
89 write_mtu_(kInvalidWriteMtu
),
90 read_has_failed_(false),
92 weak_ptr_factory_(this) {
93 VLOG(1) << "BluetoothAudioSinkChromeOS created";
95 CHECK(adapter_
.get());
96 CHECK(adapter_
->IsPresent());
97 CHECK(DBusThreadManager::IsInitialized());
99 adapter_
->AddObserver(this);
101 BluetoothMediaClient
* media
=
102 DBusThreadManager::Get()->GetBluetoothMediaClient();
104 media
->AddObserver(this);
106 BluetoothMediaTransportClient
* transport
=
107 DBusThreadManager::Get()->GetBluetoothMediaTransportClient();
109 transport
->AddObserver(this);
111 StateChanged(device::BluetoothAudioSink::STATE_DISCONNECTED
);
114 BluetoothAudioSinkChromeOS::~BluetoothAudioSinkChromeOS() {
115 VLOG(1) << "BluetoothAudioSinkChromeOS destroyed";
117 DCHECK(adapter_
.get());
119 if (state_
!= BluetoothAudioSink::STATE_INVALID
&& media_endpoint_
.get()) {
120 Unregister(base::Bind(&base::DoNothing
),
121 base::Bind(&UnregisterErrorCallback
));
124 adapter_
->RemoveObserver(this);
126 BluetoothMediaClient
* media
=
127 DBusThreadManager::Get()->GetBluetoothMediaClient();
129 media
->RemoveObserver(this);
131 BluetoothMediaTransportClient
* transport
=
132 DBusThreadManager::Get()->GetBluetoothMediaTransportClient();
134 transport
->RemoveObserver(this);
137 void BluetoothAudioSinkChromeOS::Unregister(
138 const base::Closure
& callback
,
139 const device::BluetoothAudioSink::ErrorCallback
& error_callback
) {
140 VLOG(1) << "Unregister";
142 if (!DBusThreadManager::IsInitialized())
143 error_callback
.Run(BluetoothAudioSink::ERROR_NOT_UNREGISTERED
);
145 BluetoothMediaClient
* media
=
146 DBusThreadManager::Get()->GetBluetoothMediaClient();
149 media
->UnregisterEndpoint(
152 base::Bind(&BluetoothAudioSinkChromeOS::OnUnregisterSucceeded
,
153 weak_ptr_factory_
.GetWeakPtr(), callback
),
154 base::Bind(&BluetoothAudioSinkChromeOS::OnUnregisterFailed
,
155 weak_ptr_factory_
.GetWeakPtr(), error_callback
));
158 void BluetoothAudioSinkChromeOS::AddObserver(
159 BluetoothAudioSink::Observer
* observer
) {
161 observers_
.AddObserver(observer
);
164 void BluetoothAudioSinkChromeOS::RemoveObserver(
165 BluetoothAudioSink::Observer
* observer
) {
167 observers_
.RemoveObserver(observer
);
170 BluetoothAudioSink::State
BluetoothAudioSinkChromeOS::GetState() const {
174 uint16_t BluetoothAudioSinkChromeOS::GetVolume() const {
178 void BluetoothAudioSinkChromeOS::Register(
179 const BluetoothAudioSink::Options
& options
,
180 const base::Closure
& callback
,
181 const BluetoothAudioSink::ErrorCallback
& error_callback
) {
182 VLOG(1) << "Register";
184 DCHECK(adapter_
.get());
185 DCHECK_EQ(state_
, BluetoothAudioSink::STATE_DISCONNECTED
);
188 dbus::Bus
* system_bus
= DBusThreadManager::Get()->GetSystemBus();
190 // Creates a Media Endpoint with newly-generated path.
191 endpoint_path_
= GenerateEndpointPath();
192 media_endpoint_
.reset(
193 BluetoothMediaEndpointServiceProvider::Create(
194 system_bus
, endpoint_path_
, this));
196 DCHECK(media_endpoint_
.get());
198 // Creates endpoint properties with |options|.
200 chromeos::BluetoothMediaClient::EndpointProperties endpoint_properties
;
201 endpoint_properties
.uuid
= BluetoothMediaClient::kBluetoothAudioSinkUUID
;
202 endpoint_properties
.codec
= options_
.codec
;
203 endpoint_properties
.capabilities
= options_
.capabilities
;
205 media_path_
= static_cast<BluetoothAdapterChromeOS
*>(
206 adapter_
.get())->object_path();
208 BluetoothMediaClient
* media
=
209 DBusThreadManager::Get()->GetBluetoothMediaClient();
211 media
->RegisterEndpoint(
215 base::Bind(&BluetoothAudioSinkChromeOS::OnRegisterSucceeded
,
216 weak_ptr_factory_
.GetWeakPtr(), callback
),
217 base::Bind(&BluetoothAudioSinkChromeOS::OnRegisterFailed
,
218 weak_ptr_factory_
.GetWeakPtr(), error_callback
));
221 BluetoothMediaEndpointServiceProvider
*
222 BluetoothAudioSinkChromeOS::GetEndpointServiceProvider() {
223 return media_endpoint_
.get();
226 void BluetoothAudioSinkChromeOS::AdapterPresentChanged(
227 device::BluetoothAdapter
* adapter
, bool present
) {
228 VLOG(1) << "AdapterPresentChanged: " << present
;
230 if (adapter
!= adapter_
.get())
233 if (adapter
->IsPresent()) {
234 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED
);
236 adapter_
->RemoveObserver(this);
237 StateChanged(BluetoothAudioSink::STATE_INVALID
);
241 void BluetoothAudioSinkChromeOS::AdapterPoweredChanged(
242 device::BluetoothAdapter
* adapter
, bool powered
) {
243 VLOG(1) << "AdapterPoweredChanged: " << powered
;
245 if (adapter
!= adapter_
.get())
248 // Regardless of the new powered state, |state_| goes to STATE_DISCONNECTED.
249 // If false, the transport is closed, but the endpoint is still valid for use.
250 // If true, the previous transport has been torn down, so the |state_| has to
251 // be disconnected before SetConfigruation is called.
252 if (state_
!= BluetoothAudioSink::STATE_INVALID
)
253 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED
);
256 void BluetoothAudioSinkChromeOS::MediaRemoved(const ObjectPath
& object_path
) {
257 if (object_path
== media_path_
) {
258 VLOG(1) << "MediaRemoved: " << object_path
.value();
259 StateChanged(BluetoothAudioSink::STATE_INVALID
);
263 void BluetoothAudioSinkChromeOS::MediaTransportRemoved(
264 const ObjectPath
& object_path
) {
265 // Whenever powered of |adapter_| turns false while present stays true, media
266 // transport object should be removed accordingly, and the state should be
267 // changed to STATE_DISCONNECTED.
268 if (object_path
== transport_path_
) {
269 VLOG(1) << "MediaTransportRemoved: " << object_path
.value();
270 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED
);
274 void BluetoothAudioSinkChromeOS::MediaTransportPropertyChanged(
275 const ObjectPath
& object_path
,
276 const std::string
& property_name
) {
277 if (object_path
!= transport_path_
)
280 VLOG(1) << "MediaTransportPropertyChanged: " << property_name
;
282 // Retrieves the property set of the transport object with |object_path|.
283 BluetoothMediaTransportClient::Properties
* properties
=
284 DBusThreadManager::Get()
285 ->GetBluetoothMediaTransportClient()
286 ->GetProperties(object_path
);
288 // Dispatches a property changed event to the corresponding handler.
289 if (property_name
== properties
->state
.name()) {
290 if (properties
->state
.value() ==
291 BluetoothMediaTransportClient::kStateIdle
) {
292 StateChanged(BluetoothAudioSink::STATE_IDLE
);
293 } else if (properties
->state
.value() ==
294 BluetoothMediaTransportClient::kStatePending
) {
295 StateChanged(BluetoothAudioSink::STATE_PENDING
);
296 } else if (properties
->state
.value() ==
297 BluetoothMediaTransportClient::kStateActive
) {
298 StateChanged(BluetoothAudioSink::STATE_ACTIVE
);
300 } else if (property_name
== properties
->volume
.name()) {
301 VolumeChanged(properties
->volume
.value());
305 void BluetoothAudioSinkChromeOS::SetConfiguration(
306 const ObjectPath
& transport_path
,
307 const TransportProperties
& properties
) {
308 VLOG(1) << "SetConfiguration";
309 transport_path_
= transport_path
;
311 // The initial state for a connection should be "idle".
312 if (properties
.state
!= BluetoothMediaTransportClient::kStateIdle
) {
313 VLOG(1) << "SetConfiugration - unexpected state :" << properties
.state
;
317 // Updates |volume_| if the volume level is provided in |properties|.
318 if (properties
.volume
.get()) {
319 VolumeChanged(*properties
.volume
);
322 StateChanged(BluetoothAudioSink::STATE_IDLE
);
325 void BluetoothAudioSinkChromeOS::SelectConfiguration(
326 const std::vector
<uint8_t>& capabilities
,
327 const SelectConfigurationCallback
& callback
) {
328 VLOG(1) << "SelectConfiguration";
329 callback
.Run(options_
.capabilities
);
332 void BluetoothAudioSinkChromeOS::ClearConfiguration(
333 const ObjectPath
& transport_path
) {
334 if (transport_path
!= transport_path_
)
337 VLOG(1) << "ClearConfiguration";
338 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED
);
341 void BluetoothAudioSinkChromeOS::Released() {
342 VLOG(1) << "Released";
343 StateChanged(BluetoothAudioSink::STATE_INVALID
);
346 void BluetoothAudioSinkChromeOS::OnFileCanReadWithoutBlocking(int fd
) {
350 void BluetoothAudioSinkChromeOS::OnFileCanWriteWithoutBlocking(int fd
) {
351 // Do nothing for now.
354 void BluetoothAudioSinkChromeOS::AcquireFD() {
355 VLOG(1) << "AcquireFD - transport path: " << transport_path_
.value();
357 read_has_failed_
= false;
359 DBusThreadManager::Get()->GetBluetoothMediaTransportClient()->Acquire(
361 base::Bind(&BluetoothAudioSinkChromeOS::OnAcquireSucceeded
,
362 weak_ptr_factory_
.GetWeakPtr()),
363 base::Bind(&BluetoothAudioSinkChromeOS::OnAcquireFailed
,
364 weak_ptr_factory_
.GetWeakPtr()));
367 void BluetoothAudioSinkChromeOS::WatchFD() {
368 CHECK(file_
.get() && file_
->IsValid());
370 VLOG(1) << "WatchFD - file: " << file_
->GetPlatformFile()
371 << ", file validity: " << file_
->IsValid();
373 base::MessageLoopForIO::current()->WatchFileDescriptor(
374 file_
->GetPlatformFile(), true, base::MessageLoopForIO::WATCH_READ
,
375 &fd_read_watcher_
, this);
378 void BluetoothAudioSinkChromeOS::StopWatchingFD() {
380 VLOG(1) << "StopWatchingFD - skip";
384 bool stopped
= fd_read_watcher_
.StopWatchingFileDescriptor();
385 VLOG(1) << "StopWatchingFD - watch stopped: " << stopped
;
388 read_mtu_
= kInvalidReadMtu
;
389 write_mtu_
= kInvalidWriteMtu
;
390 file_
.reset(); // This will close the file descriptor.
393 void BluetoothAudioSinkChromeOS::ReadFromFile() {
394 DCHECK(file_
.get() && file_
->IsValid());
397 int size
= file_
->ReadAtCurrentPosNoBestEffort(data_
.get(), read_mtu_
);
400 // To reduce the number of logs, log only once for multiple failures.
401 if (!read_has_failed_
) {
402 VLOG(1) << "ReadFromFile - failed";
403 read_has_failed_
= true;
408 VLOG(1) << "ReadFromFile - read " << size
<< " bytes";
410 BluetoothAudioSink::Observer
, observers_
,
411 BluetoothAudioSinkDataAvailable(this, data_
.get(), size
, read_mtu_
));
414 void BluetoothAudioSinkChromeOS::StateChanged(
415 BluetoothAudioSink::State state
) {
419 VLOG(1) << "StateChanged - state: " << StateToString(state
);
422 case BluetoothAudioSink::STATE_INVALID
:
425 case BluetoothAudioSink::STATE_DISCONNECTED
:
428 case BluetoothAudioSink::STATE_IDLE
:
431 case BluetoothAudioSink::STATE_PENDING
:
434 case BluetoothAudioSink::STATE_ACTIVE
:
442 FOR_EACH_OBSERVER(BluetoothAudioSink::Observer
, observers_
,
443 BluetoothAudioSinkStateChanged(this, state_
));
446 void BluetoothAudioSinkChromeOS::VolumeChanged(uint16_t volume
) {
447 if (volume
== volume_
)
450 VLOG(1) << "VolumeChanged: " << volume
;
452 volume_
= std::min(volume
, BluetoothAudioSink::kInvalidVolume
);
453 FOR_EACH_OBSERVER(BluetoothAudioSink::Observer
, observers_
,
454 BluetoothAudioSinkVolumeChanged(this, volume_
));
457 void BluetoothAudioSinkChromeOS::OnRegisterSucceeded(
458 const base::Closure
& callback
) {
459 DCHECK(media_endpoint_
.get());
460 VLOG(1) << "OnRegisterSucceeded";
462 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED
);
466 void BluetoothAudioSinkChromeOS::OnRegisterFailed(
467 const BluetoothAudioSink::ErrorCallback
& error_callback
,
468 const std::string
& error_name
,
469 const std::string
& error_message
) {
470 VLOG(1) << "OnRegisterFailed - error name: " << error_name
471 << ", error message: " << error_message
;
474 error_callback
.Run(BluetoothAudioSink::ERROR_NOT_REGISTERED
);
477 void BluetoothAudioSinkChromeOS::OnUnregisterSucceeded(
478 const base::Closure
& callback
) {
479 VLOG(1) << "Unregistered - endpoint: " << endpoint_path_
.value();
481 // Once the state becomes STATE_INVALID, media, media transport and media
482 // endpoint will be reset.
483 StateChanged(BluetoothAudioSink::STATE_INVALID
);
487 void BluetoothAudioSinkChromeOS::OnUnregisterFailed(
488 const device::BluetoothAudioSink::ErrorCallback
& error_callback
,
489 const std::string
& error_name
,
490 const std::string
& error_message
) {
491 VLOG(1) << "OnUnregisterFailed - error name: " << error_name
492 << ", error message: " << error_message
;
494 error_callback
.Run(BluetoothAudioSink::ERROR_NOT_UNREGISTERED
);
497 void BluetoothAudioSinkChromeOS::OnAcquireSucceeded(
498 dbus::FileDescriptor
* fd
,
499 const uint16_t read_mtu
,
500 const uint16_t write_mtu
) {
503 CHECK(fd
->is_valid() && fd
->value() != kInvalidFd
);
504 CHECK_GT(read_mtu
, kInvalidReadMtu
);
505 CHECK_GT(write_mtu
, kInvalidWriteMtu
);
507 // Avoids unnecessary memory reallocation if read MTU doesn't change.
508 if (read_mtu
!= read_mtu_
) {
509 read_mtu_
= read_mtu
;
510 data_
.reset(new char[read_mtu_
]);
511 VLOG(1) << "OnAcquireSucceeded - allocate " << read_mtu_
512 << " bytes of memory";
515 write_mtu_
= write_mtu
;
517 // Avoids closing the same file descriptor caused by reassignment.
518 if (!file_
.get() || file_
->GetPlatformFile() != fd
->value()) {
519 // Takes ownership of the file descriptor.
520 file_
.reset(new base::File(fd
->TakeValue()));
521 DCHECK(file_
->IsValid());
522 VLOG(1) << "OnAcquireSucceeded - update file";
525 VLOG(1) << "OnAcquireSucceeded - file: " << file_
->GetPlatformFile()
526 << ", read MTU: " << read_mtu_
<< ", write MTU: " << write_mtu_
;
529 void BluetoothAudioSinkChromeOS::OnAcquireFailed(
530 const std::string
& error_name
,
531 const std::string
& error_message
) {
532 VLOG(1) << "OnAcquireFailed - error name: " << error_name
533 << ", error message: " << error_message
;
536 void BluetoothAudioSinkChromeOS::OnReleaseFDSucceeded() {
537 VLOG(1) << "OnReleaseFDSucceeded";
540 void BluetoothAudioSinkChromeOS::OnReleaseFDFailed(
541 const std::string
& error_name
,
542 const std::string
& error_message
) {
543 VLOG(1) << "OnReleaseFDFailed - error name: " << error_name
544 << ", error message: " << error_message
;
547 void BluetoothAudioSinkChromeOS::ResetMedia() {
548 VLOG(1) << "ResetMedia";
550 media_path_
= dbus::ObjectPath("");
553 void BluetoothAudioSinkChromeOS::ResetTransport() {
554 if (!transport_path_
.IsValid()) {
555 VLOG(1) << "ResetTransport - skip";
559 VLOG(1) << "ResetTransport - clean-up";
561 VolumeChanged(BluetoothAudioSink::kInvalidVolume
);
562 transport_path_
= dbus::ObjectPath("");
563 read_mtu_
= kInvalidReadMtu
;
564 write_mtu_
= kInvalidWriteMtu
;
568 void BluetoothAudioSinkChromeOS::ResetEndpoint() {
569 VLOG(1) << "ResetEndpoint";
571 endpoint_path_
= ObjectPath("");
572 media_endpoint_
= nullptr;
575 } // namespace chromeos