Revert of Remove Qualcomm MSAA from blacklist for versions >= Lollipop. (patchset...
[chromium-blink-merge.git] / device / bluetooth / bluetooth_audio_sink_chromeos.cc
blobdcd751bcd4cd37ac90830862ba35639b66ccd53a
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"
7 #include <unistd.h>
9 #include <algorithm>
10 #include <sstream>
11 #include <string>
12 #include <vector>
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;
24 namespace {
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;
35 ++sequence_number;
36 std::stringstream path;
37 path << kBluetoothAudioSinkServicePath << "/endpoint" << sequence_number;
38 return ObjectPath(path.str());
41 std::string StateToString(const BluetoothAudioSink::State& state) {
42 switch (state) {
43 case BluetoothAudioSink::STATE_INVALID:
44 return "invalid";
45 case BluetoothAudioSink::STATE_DISCONNECTED:
46 return "disconnected";
47 case BluetoothAudioSink::STATE_IDLE:
48 return "idle";
49 case BluetoothAudioSink::STATE_PENDING:
50 return "pending";
51 case BluetoothAudioSink::STATE_ACTIVE:
52 return "active";
53 default:
54 return "unknown";
58 std::string ErrorCodeToString(const BluetoothAudioSink::ErrorCode& error_code) {
59 switch (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";
68 default:
69 return "unknown";
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 << ")";
80 } // namespace
82 namespace chromeos {
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),
91 adapter_(adapter),
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();
103 CHECK(media);
104 media->AddObserver(this);
106 BluetoothMediaTransportClient* transport =
107 DBusThreadManager::Get()->GetBluetoothMediaTransportClient();
108 CHECK(transport);
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();
128 CHECK(media);
129 media->RemoveObserver(this);
131 BluetoothMediaTransportClient* transport =
132 DBusThreadManager::Get()->GetBluetoothMediaTransportClient();
133 CHECK(transport);
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();
147 CHECK(media);
149 media->UnregisterEndpoint(
150 media_path_,
151 endpoint_path_,
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) {
160 CHECK(observer);
161 observers_.AddObserver(observer);
164 void BluetoothAudioSinkChromeOS::RemoveObserver(
165 BluetoothAudioSink::Observer* observer) {
166 CHECK(observer);
167 observers_.RemoveObserver(observer);
170 BluetoothAudioSink::State BluetoothAudioSinkChromeOS::GetState() const {
171 return state_;
174 uint16_t BluetoothAudioSinkChromeOS::GetVolume() const {
175 return volume_;
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);
187 // Gets system bus.
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|.
199 options_ = 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();
210 CHECK(media);
211 media->RegisterEndpoint(
212 media_path_,
213 endpoint_path_,
214 endpoint_properties,
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->IsPresent()) {
231 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
232 } else {
233 adapter_->RemoveObserver(this);
234 StateChanged(BluetoothAudioSink::STATE_INVALID);
238 void BluetoothAudioSinkChromeOS::AdapterPoweredChanged(
239 device::BluetoothAdapter* adapter, bool powered) {
240 VLOG(1) << "AdapterPoweredChanged: " << powered;
242 // Regardless of the new powered state, |state_| goes to STATE_DISCONNECTED.
243 // If false, the transport is closed, but the endpoint is still valid for use.
244 // If true, the previous transport has been torn down, so the |state_| has to
245 // be disconnected before SetConfigruation is called.
246 if (state_ != BluetoothAudioSink::STATE_INVALID)
247 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
250 void BluetoothAudioSinkChromeOS::MediaRemoved(const ObjectPath& object_path) {
251 if (object_path == media_path_) {
252 VLOG(1) << "MediaRemoved: " << object_path.value();
253 StateChanged(BluetoothAudioSink::STATE_INVALID);
257 void BluetoothAudioSinkChromeOS::MediaTransportRemoved(
258 const ObjectPath& object_path) {
259 // Whenever powered of |adapter_| turns false while present stays true, media
260 // transport object should be removed accordingly, and the state should be
261 // changed to STATE_DISCONNECTED.
262 if (object_path == transport_path_) {
263 VLOG(1) << "MediaTransportRemoved: " << object_path.value();
264 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
268 void BluetoothAudioSinkChromeOS::MediaTransportPropertyChanged(
269 const ObjectPath& object_path,
270 const std::string& property_name) {
271 if (object_path != transport_path_)
272 return;
274 VLOG(1) << "MediaTransportPropertyChanged: " << property_name;
276 // Retrieves the property set of the transport object with |object_path|.
277 BluetoothMediaTransportClient::Properties* properties =
278 DBusThreadManager::Get()
279 ->GetBluetoothMediaTransportClient()
280 ->GetProperties(object_path);
282 // Dispatches a property changed event to the corresponding handler.
283 if (property_name == properties->state.name()) {
284 if (properties->state.value() ==
285 BluetoothMediaTransportClient::kStateIdle) {
286 StateChanged(BluetoothAudioSink::STATE_IDLE);
287 } else if (properties->state.value() ==
288 BluetoothMediaTransportClient::kStatePending) {
289 StateChanged(BluetoothAudioSink::STATE_PENDING);
290 } else if (properties->state.value() ==
291 BluetoothMediaTransportClient::kStateActive) {
292 StateChanged(BluetoothAudioSink::STATE_ACTIVE);
294 } else if (property_name == properties->volume.name()) {
295 VolumeChanged(properties->volume.value());
299 void BluetoothAudioSinkChromeOS::SetConfiguration(
300 const ObjectPath& transport_path,
301 const TransportProperties& properties) {
302 VLOG(1) << "SetConfiguration";
303 transport_path_ = transport_path;
305 // The initial state for a connection should be "idle".
306 if (properties.state != BluetoothMediaTransportClient::kStateIdle) {
307 VLOG(1) << "SetConfiugration - unexpected state :" << properties.state;
308 return;
311 // Updates |volume_| if the volume level is provided in |properties|.
312 if (properties.volume.get()) {
313 VolumeChanged(*properties.volume);
316 StateChanged(BluetoothAudioSink::STATE_IDLE);
319 void BluetoothAudioSinkChromeOS::SelectConfiguration(
320 const std::vector<uint8_t>& capabilities,
321 const SelectConfigurationCallback& callback) {
322 VLOG(1) << "SelectConfiguration";
323 callback.Run(options_.capabilities);
326 void BluetoothAudioSinkChromeOS::ClearConfiguration(
327 const ObjectPath& transport_path) {
328 if (transport_path != transport_path_)
329 return;
331 VLOG(1) << "ClearConfiguration";
332 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
335 void BluetoothAudioSinkChromeOS::Released() {
336 VLOG(1) << "Released";
337 StateChanged(BluetoothAudioSink::STATE_INVALID);
340 void BluetoothAudioSinkChromeOS::OnFileCanReadWithoutBlocking(int fd) {
341 ReadFromFile();
344 void BluetoothAudioSinkChromeOS::OnFileCanWriteWithoutBlocking(int fd) {
345 // Do nothing for now.
348 void BluetoothAudioSinkChromeOS::AcquireFD() {
349 VLOG(1) << "AcquireFD - transport path: " << transport_path_.value();
351 read_has_failed_ = false;
353 DBusThreadManager::Get()->GetBluetoothMediaTransportClient()->Acquire(
354 transport_path_,
355 base::Bind(&BluetoothAudioSinkChromeOS::OnAcquireSucceeded,
356 weak_ptr_factory_.GetWeakPtr()),
357 base::Bind(&BluetoothAudioSinkChromeOS::OnAcquireFailed,
358 weak_ptr_factory_.GetWeakPtr()));
361 void BluetoothAudioSinkChromeOS::WatchFD() {
362 CHECK(file_.get() && file_->IsValid());
364 VLOG(1) << "WatchFD - file: " << file_->GetPlatformFile()
365 << ", file validity: " << file_->IsValid();
367 base::MessageLoopForIO::current()->WatchFileDescriptor(
368 file_->GetPlatformFile(), true, base::MessageLoopForIO::WATCH_READ,
369 &fd_read_watcher_, this);
372 void BluetoothAudioSinkChromeOS::StopWatchingFD() {
373 if (!file_.get()) {
374 VLOG(1) << "StopWatchingFD - skip";
375 return;
378 bool stopped = fd_read_watcher_.StopWatchingFileDescriptor();
379 VLOG(1) << "StopWatchingFD - watch stopped: " << stopped;
380 CHECK(stopped);
382 read_mtu_ = kInvalidReadMtu;
383 write_mtu_ = kInvalidWriteMtu;
384 file_.reset(); // This will close the file descriptor.
387 void BluetoothAudioSinkChromeOS::ReadFromFile() {
388 DCHECK(file_.get() && file_->IsValid());
389 DCHECK(data_.get());
391 int size = file_->ReadAtCurrentPosNoBestEffort(data_.get(), read_mtu_);
393 if (size == -1) {
394 // To reduce the number of logs, log only once for multiple failures.
395 if (!read_has_failed_) {
396 VLOG(1) << "ReadFromFile - failed";
397 read_has_failed_ = true;
399 return;
402 VLOG(1) << "ReadFromFile - read " << size << " bytes";
403 FOR_EACH_OBSERVER(
404 BluetoothAudioSink::Observer, observers_,
405 BluetoothAudioSinkDataAvailable(this, data_.get(), size, read_mtu_));
408 void BluetoothAudioSinkChromeOS::StateChanged(
409 BluetoothAudioSink::State state) {
410 if (state == state_)
411 return;
413 VLOG(1) << "StateChanged - state: " << StateToString(state);
415 switch (state) {
416 case BluetoothAudioSink::STATE_INVALID:
417 ResetMedia();
418 ResetEndpoint();
419 case BluetoothAudioSink::STATE_DISCONNECTED:
420 ResetTransport();
421 break;
422 case BluetoothAudioSink::STATE_IDLE:
423 StopWatchingFD();
424 break;
425 case BluetoothAudioSink::STATE_PENDING:
426 AcquireFD();
427 break;
428 case BluetoothAudioSink::STATE_ACTIVE:
429 WatchFD();
430 break;
431 default:
432 break;
435 state_ = state;
436 FOR_EACH_OBSERVER(BluetoothAudioSink::Observer, observers_,
437 BluetoothAudioSinkStateChanged(this, state_));
440 void BluetoothAudioSinkChromeOS::VolumeChanged(uint16_t volume) {
441 if (volume == volume_)
442 return;
444 VLOG(1) << "VolumeChanged: " << volume;
446 volume_ = std::min(volume, BluetoothAudioSink::kInvalidVolume);
447 FOR_EACH_OBSERVER(BluetoothAudioSink::Observer, observers_,
448 BluetoothAudioSinkVolumeChanged(this, volume_));
451 void BluetoothAudioSinkChromeOS::OnRegisterSucceeded(
452 const base::Closure& callback) {
453 DCHECK(media_endpoint_.get());
454 VLOG(1) << "OnRegisterSucceeded";
456 StateChanged(BluetoothAudioSink::STATE_DISCONNECTED);
457 callback.Run();
460 void BluetoothAudioSinkChromeOS::OnRegisterFailed(
461 const BluetoothAudioSink::ErrorCallback& error_callback,
462 const std::string& error_name,
463 const std::string& error_message) {
464 VLOG(1) << "OnRegisterFailed - error name: " << error_name
465 << ", error message: " << error_message;
467 ResetEndpoint();
468 error_callback.Run(BluetoothAudioSink::ERROR_NOT_REGISTERED);
471 void BluetoothAudioSinkChromeOS::OnUnregisterSucceeded(
472 const base::Closure& callback) {
473 VLOG(1) << "Unregistered - endpoint: " << endpoint_path_.value();
475 // Once the state becomes STATE_INVALID, media, media transport and media
476 // endpoint will be reset.
477 StateChanged(BluetoothAudioSink::STATE_INVALID);
478 callback.Run();
481 void BluetoothAudioSinkChromeOS::OnUnregisterFailed(
482 const device::BluetoothAudioSink::ErrorCallback& error_callback,
483 const std::string& error_name,
484 const std::string& error_message) {
485 VLOG(1) << "OnUnregisterFailed - error name: " << error_name
486 << ", error message: " << error_message;
488 error_callback.Run(BluetoothAudioSink::ERROR_NOT_UNREGISTERED);
491 void BluetoothAudioSinkChromeOS::OnAcquireSucceeded(
492 dbus::FileDescriptor* fd,
493 const uint16_t read_mtu,
494 const uint16_t write_mtu) {
495 CHECK(fd);
496 fd->CheckValidity();
497 CHECK(fd->is_valid() && fd->value() != kInvalidFd);
498 CHECK_GT(read_mtu, kInvalidReadMtu);
499 CHECK_GT(write_mtu, kInvalidWriteMtu);
501 // Avoids unnecessary memory reallocation if read MTU doesn't change.
502 if (read_mtu != read_mtu_) {
503 read_mtu_ = read_mtu;
504 data_.reset(new char[read_mtu_]);
505 VLOG(1) << "OnAcquireSucceeded - allocate " << read_mtu_
506 << " bytes of memory";
509 write_mtu_ = write_mtu;
511 // Avoids closing the same file descriptor caused by reassignment.
512 if (!file_.get() || file_->GetPlatformFile() != fd->value()) {
513 // Takes ownership of the file descriptor.
514 file_.reset(new base::File(fd->TakeValue()));
515 DCHECK(file_->IsValid());
516 VLOG(1) << "OnAcquireSucceeded - update file";
519 VLOG(1) << "OnAcquireSucceeded - file: " << file_->GetPlatformFile()
520 << ", read MTU: " << read_mtu_ << ", write MTU: " << write_mtu_;
523 void BluetoothAudioSinkChromeOS::OnAcquireFailed(
524 const std::string& error_name,
525 const std::string& error_message) {
526 VLOG(1) << "OnAcquireFailed - error name: " << error_name
527 << ", error message: " << error_message;
530 void BluetoothAudioSinkChromeOS::OnReleaseFDSucceeded() {
531 VLOG(1) << "OnReleaseFDSucceeded";
534 void BluetoothAudioSinkChromeOS::OnReleaseFDFailed(
535 const std::string& error_name,
536 const std::string& error_message) {
537 VLOG(1) << "OnReleaseFDFailed - error name: " << error_name
538 << ", error message: " << error_message;
541 void BluetoothAudioSinkChromeOS::ResetMedia() {
542 VLOG(1) << "ResetMedia";
544 media_path_ = dbus::ObjectPath("");
547 void BluetoothAudioSinkChromeOS::ResetTransport() {
548 if (!transport_path_.IsValid()) {
549 VLOG(1) << "ResetTransport - skip";
550 return;
553 VLOG(1) << "ResetTransport - clean-up";
555 VolumeChanged(BluetoothAudioSink::kInvalidVolume);
556 transport_path_ = dbus::ObjectPath("");
557 read_mtu_ = kInvalidReadMtu;
558 write_mtu_ = kInvalidWriteMtu;
559 file_.reset();
562 void BluetoothAudioSinkChromeOS::ResetEndpoint() {
563 VLOG(1) << "ResetEndpoint";
565 endpoint_path_ = ObjectPath("");
566 media_endpoint_ = nullptr;
569 } // namespace chromeos