Index: device/bluetooth/bluetooth_audio_sink_chromeos.cc |
diff --git a/device/bluetooth/bluetooth_audio_sink_chromeos.cc b/device/bluetooth/bluetooth_audio_sink_chromeos.cc |
index fb1ddf7d6ec09565442cde1013ddaeed52f93150..0f76be8ab1d1664a735e27129b4a2cf894bb4e04 100644 |
--- a/device/bluetooth/bluetooth_audio_sink_chromeos.cc |
+++ b/device/bluetooth/bluetooth_audio_sink_chromeos.cc |
@@ -4,12 +4,15 @@ |
#include "device/bluetooth/bluetooth_audio_sink_chromeos.h" |
+#include <unistd.h> |
+ |
#include <algorithm> |
#include <sstream> |
#include <string> |
#include <vector> |
#include "base/debug/stack_trace.h" |
+#include "base/files/file_util.h" |
#include "base/logging.h" |
#include "chromeos/dbus/dbus_thread_manager.h" |
#include "dbus/message.h" |
@@ -23,6 +26,10 @@ namespace { |
// TODO(mcchou): Add the constant to dbus/service_constants.h. |
const char kBluetoothAudioSinkServicePath[] = "/org/chromium/AudioSink"; |
+const int kInvalidFd = -1; |
+const uint16_t kInvalidReadMtu = 0; |
+const uint16_t kInvalidWriteMtu = 0; |
+ |
ObjectPath GenerateEndpointPath() { |
static unsigned int sequence_number = 0; |
++sequence_number; |
@@ -78,25 +85,27 @@ BluetoothAudioSinkChromeOS::BluetoothAudioSinkChromeOS( |
scoped_refptr<device::BluetoothAdapter> adapter) |
: state_(BluetoothAudioSink::STATE_INVALID), |
volume_(BluetoothAudioSink::kInvalidVolume), |
- read_mtu_(nullptr), |
- write_mtu_(nullptr), |
+ read_mtu_(kInvalidReadMtu), |
+ write_mtu_(kInvalidWriteMtu), |
+ read_has_failed_(false), |
adapter_(adapter), |
weak_ptr_factory_(this) { |
VLOG(1) << "BluetoothAudioSinkChromeOS created"; |
- DCHECK(adapter_.get()); |
- DCHECK(adapter_->IsPresent()); |
+ CHECK(adapter_.get()); |
+ CHECK(adapter_->IsPresent()); |
+ CHECK(DBusThreadManager::IsInitialized()); |
adapter_->AddObserver(this); |
- chromeos::BluetoothMediaClient* media = |
+ BluetoothMediaClient* media = |
DBusThreadManager::Get()->GetBluetoothMediaClient(); |
- DCHECK(media); |
+ CHECK(media); |
media->AddObserver(this); |
- chromeos::BluetoothMediaTransportClient *transport = |
- chromeos::DBusThreadManager::Get()->GetBluetoothMediaTransportClient(); |
- DCHECK(transport); |
+ BluetoothMediaTransportClient* transport = |
+ DBusThreadManager::Get()->GetBluetoothMediaTransportClient(); |
+ CHECK(transport); |
transport->AddObserver(this); |
StateChanged(device::BluetoothAudioSink::STATE_DISCONNECTED); |
@@ -114,14 +123,14 @@ BluetoothAudioSinkChromeOS::~BluetoothAudioSinkChromeOS() { |
adapter_->RemoveObserver(this); |
- chromeos::BluetoothMediaClient* media = |
+ BluetoothMediaClient* media = |
DBusThreadManager::Get()->GetBluetoothMediaClient(); |
- DCHECK(media); |
+ CHECK(media); |
media->RemoveObserver(this); |
- chromeos::BluetoothMediaTransportClient *transport = |
- chromeos::DBusThreadManager::Get()->GetBluetoothMediaTransportClient(); |
- DCHECK(transport); |
+ BluetoothMediaTransportClient* transport = |
+ DBusThreadManager::Get()->GetBluetoothMediaTransportClient(); |
+ CHECK(transport); |
transport->RemoveObserver(this); |
} |
@@ -133,9 +142,9 @@ void BluetoothAudioSinkChromeOS::Unregister( |
if (!DBusThreadManager::IsInitialized()) |
error_callback.Run(BluetoothAudioSink::ERROR_NOT_UNREGISTERED); |
- chromeos::BluetoothMediaClient* media = |
+ BluetoothMediaClient* media = |
DBusThreadManager::Get()->GetBluetoothMediaClient(); |
- DCHECK(media); |
+ CHECK(media); |
media->UnregisterEndpoint( |
media_path_, |
@@ -148,13 +157,13 @@ void BluetoothAudioSinkChromeOS::Unregister( |
void BluetoothAudioSinkChromeOS::AddObserver( |
BluetoothAudioSink::Observer* observer) { |
- DCHECK(observer); |
+ CHECK(observer); |
observers_.AddObserver(observer); |
} |
void BluetoothAudioSinkChromeOS::RemoveObserver( |
BluetoothAudioSink::Observer* observer) { |
- DCHECK(observer); |
+ CHECK(observer); |
observers_.RemoveObserver(observer); |
} |
@@ -166,6 +175,54 @@ uint16_t BluetoothAudioSinkChromeOS::GetVolume() const { |
return volume_; |
} |
+void BluetoothAudioSinkChromeOS::Register( |
+ const BluetoothAudioSink::Options& options, |
+ const base::Closure& callback, |
+ const BluetoothAudioSink::ErrorCallback& error_callback) { |
+ VLOG(1) << "Register"; |
+ |
+ DCHECK(adapter_.get()); |
+ DCHECK_EQ(state_, BluetoothAudioSink::STATE_DISCONNECTED); |
+ |
+ // Gets system bus. |
+ dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus(); |
+ |
+ // Creates a Media Endpoint with newly-generated path. |
+ endpoint_path_ = GenerateEndpointPath(); |
+ media_endpoint_.reset( |
+ BluetoothMediaEndpointServiceProvider::Create( |
+ system_bus, endpoint_path_, this)); |
+ |
+ DCHECK(media_endpoint_.get()); |
+ |
+ // Creates endpoint properties with |options|. |
+ options_ = options; |
+ chromeos::BluetoothMediaClient::EndpointProperties endpoint_properties; |
+ endpoint_properties.uuid = BluetoothMediaClient::kBluetoothAudioSinkUUID; |
+ endpoint_properties.codec = options_.codec; |
+ endpoint_properties.capabilities = options_.capabilities; |
+ |
+ media_path_ = static_cast<BluetoothAdapterChromeOS*>( |
+ adapter_.get())->object_path(); |
+ |
+ BluetoothMediaClient* media = |
+ DBusThreadManager::Get()->GetBluetoothMediaClient(); |
+ CHECK(media); |
+ media->RegisterEndpoint( |
+ media_path_, |
+ endpoint_path_, |
+ endpoint_properties, |
+ base::Bind(&BluetoothAudioSinkChromeOS::OnRegisterSucceeded, |
+ weak_ptr_factory_.GetWeakPtr(), callback), |
+ base::Bind(&BluetoothAudioSinkChromeOS::OnRegisterFailed, |
+ weak_ptr_factory_.GetWeakPtr(), error_callback)); |
+} |
+ |
+BluetoothMediaEndpointServiceProvider* |
+ BluetoothAudioSinkChromeOS::GetEndpointServiceProvider() { |
+ return media_endpoint_.get(); |
+} |
+ |
void BluetoothAudioSinkChromeOS::AdapterPresentChanged( |
device::BluetoothAdapter* adapter, bool present) { |
VLOG(1) << "AdapterPresentChanged: " << present; |
@@ -217,7 +274,7 @@ void BluetoothAudioSinkChromeOS::MediaTransportPropertyChanged( |
VLOG(1) << "MediaTransportPropertyChanged: " << property_name; |
// Retrieves the property set of the transport object with |object_path|. |
- chromeos::BluetoothMediaTransportClient::Properties* properties = |
+ BluetoothMediaTransportClient::Properties* properties = |
DBusThreadManager::Get() |
->GetBluetoothMediaTransportClient() |
->GetProperties(object_path); |
@@ -270,6 +327,7 @@ void BluetoothAudioSinkChromeOS::ClearConfiguration( |
const ObjectPath& transport_path) { |
if (transport_path != transport_path_) |
return; |
+ |
VLOG(1) << "ClearConfiguration"; |
StateChanged(BluetoothAudioSink::STATE_DISCONNECTED); |
} |
@@ -279,52 +337,71 @@ void BluetoothAudioSinkChromeOS::Released() { |
StateChanged(BluetoothAudioSink::STATE_INVALID); |
} |
-void BluetoothAudioSinkChromeOS::Register( |
- const BluetoothAudioSink::Options& options, |
- const base::Closure& callback, |
- const BluetoothAudioSink::ErrorCallback& error_callback) { |
- VLOG(1) << "Register"; |
+void BluetoothAudioSinkChromeOS::OnFileCanReadWithoutBlocking(int fd) { |
+ ReadFromFile(); |
+} |
- DCHECK(adapter_.get()); |
- DCHECK_EQ(state_, BluetoothAudioSink::STATE_DISCONNECTED); |
+void BluetoothAudioSinkChromeOS::OnFileCanWriteWithoutBlocking(int fd) { |
+ // Do nothing for now. |
+} |
- // Gets system bus. |
- dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus(); |
+void BluetoothAudioSinkChromeOS::AcquireFD() { |
+ VLOG(1) << "AcquireFD - transport path: " << transport_path_.value(); |
- // Creates a Media Endpoint with newly-generated path. |
- endpoint_path_ = GenerateEndpointPath(); |
- media_endpoint_.reset( |
- BluetoothMediaEndpointServiceProvider::Create( |
- system_bus, endpoint_path_, this)); |
+ read_has_failed_ = false; |
- DCHECK(media_endpoint_.get()); |
+ DBusThreadManager::Get()->GetBluetoothMediaTransportClient()->Acquire( |
+ transport_path_, |
+ base::Bind(&BluetoothAudioSinkChromeOS::OnAcquireSucceeded, |
+ weak_ptr_factory_.GetWeakPtr()), |
+ base::Bind(&BluetoothAudioSinkChromeOS::OnAcquireFailed, |
+ weak_ptr_factory_.GetWeakPtr())); |
+} |
- // Creates endpoint properties with |options|. |
- options_ = options; |
- chromeos::BluetoothMediaClient::EndpointProperties endpoint_properties; |
- endpoint_properties.uuid = BluetoothMediaClient::kBluetoothAudioSinkUUID; |
- endpoint_properties.codec = options_.codec; |
- endpoint_properties.capabilities = options_.capabilities; |
+void BluetoothAudioSinkChromeOS::WatchFD() { |
+ CHECK(file_.get() && file_->IsValid()); |
- media_path_ = static_cast<BluetoothAdapterChromeOS*>( |
- adapter_.get())->object_path(); |
+ VLOG(1) << "WatchFD - file: " << file_->GetPlatformFile() |
+ << ", file validity: " << file_->IsValid(); |
- chromeos::BluetoothMediaClient* media = |
- DBusThreadManager::Get()->GetBluetoothMediaClient(); |
- DCHECK(media); |
- media->RegisterEndpoint( |
- media_path_, |
- endpoint_path_, |
- endpoint_properties, |
- base::Bind(&BluetoothAudioSinkChromeOS::OnRegisterSucceeded, |
- weak_ptr_factory_.GetWeakPtr(), callback), |
- base::Bind(&BluetoothAudioSinkChromeOS::OnRegisterFailed, |
- weak_ptr_factory_.GetWeakPtr(), error_callback)); |
+ base::MessageLoopForIO::current()->WatchFileDescriptor( |
+ file_->GetPlatformFile(), true, base::MessageLoopForIO::WATCH_READ, |
+ &fd_read_watcher_, this); |
} |
-BluetoothMediaEndpointServiceProvider* |
- BluetoothAudioSinkChromeOS::GetEndpointServiceProvider() { |
- return media_endpoint_.get(); |
+void BluetoothAudioSinkChromeOS::StopWatchingFD() { |
+ if (!file_.get()) { |
+ VLOG(1) << "StopWatchingFD - skip"; |
+ return; |
+ } |
+ |
+ bool stopped = fd_read_watcher_.StopWatchingFileDescriptor(); |
+ VLOG(1) << "StopWatchingFD - watch stopped: " << stopped; |
+ CHECK(stopped); |
+ |
+ read_mtu_ = kInvalidReadMtu; |
+ write_mtu_ = kInvalidWriteMtu; |
+ file_.reset(); // This will close the file descriptor. |
+} |
+ |
+void BluetoothAudioSinkChromeOS::ReadFromFile() { |
+ DCHECK(file_.get() && file_->IsValid()); |
+ DCHECK(data_.get()); |
+ |
+ int size = file_->ReadAtCurrentPosNoBestEffort(data_.get(), read_mtu_); |
+ |
+ if (size == -1) { |
+ // To reduce the number of logs, log only once for multiple failures. |
+ if (!read_has_failed_) { |
+ VLOG(1) << "ReadFromFile - failed"; |
+ read_has_failed_ = true; |
+ } |
+ return; |
+ } |
+ |
+ VLOG(1) << "ReadFromFile - read " << size << " bytes"; |
+ FOR_EACH_OBSERVER(BluetoothAudioSink::Observer, observers_, |
+ BluetoothAudioSinkDataAvailable(this, data_.get(), size)); |
} |
void BluetoothAudioSinkChromeOS::StateChanged( |
@@ -332,7 +409,7 @@ void BluetoothAudioSinkChromeOS::StateChanged( |
if (state == state_) |
return; |
- VLOG(1) << "StateChnaged: " << StateToString(state); |
+ VLOG(1) << "StateChanged - state: " << StateToString(state); |
switch (state) { |
case BluetoothAudioSink::STATE_INVALID: |
@@ -342,18 +419,13 @@ void BluetoothAudioSinkChromeOS::StateChanged( |
ResetTransport(); |
break; |
case BluetoothAudioSink::STATE_IDLE: |
- // TODO(mcchou): BUG=441581 |
- // Triggered by MediaTransportPropertyChanged and SetConfiguration. |
- // Stop watching on file descriptor if there is one. |
+ StopWatchingFD(); |
break; |
case BluetoothAudioSink::STATE_PENDING: |
- // TODO(mcchou): BUG=441581 |
- // Call BluetoothMediaTransportClient::Acquire() to get fd and mtus. |
+ AcquireFD(); |
break; |
case BluetoothAudioSink::STATE_ACTIVE: |
- // TODO(mcchou): BUG=441581 |
- // Read from fd and call DataAvailable. |
- ReadFromFD(); |
+ WatchFD(); |
break; |
default: |
break; |
@@ -397,7 +469,7 @@ void BluetoothAudioSinkChromeOS::OnRegisterFailed( |
void BluetoothAudioSinkChromeOS::OnUnregisterSucceeded( |
const base::Closure& callback) { |
- VLOG(1) << "Unregisterd"; |
+ VLOG(1) << "Unregistered - endpoint: " << endpoint_path_.value(); |
// Once the state becomes STATE_INVALID, media, media transport and media |
// endpoint will be reset. |
@@ -415,12 +487,54 @@ void BluetoothAudioSinkChromeOS::OnUnregisterFailed( |
error_callback.Run(BluetoothAudioSink::ERROR_NOT_UNREGISTERED); |
} |
-void BluetoothAudioSinkChromeOS::ReadFromFD() { |
- DCHECK_GE(fd_.value(), 0); |
+void BluetoothAudioSinkChromeOS::OnAcquireSucceeded( |
+ dbus::FileDescriptor* fd, |
+ const uint16_t read_mtu, |
+ const uint16_t write_mtu) { |
+ CHECK(fd); |
+ fd->CheckValidity(); |
+ CHECK(fd->is_valid() && fd->value() != kInvalidFd); |
+ CHECK_GT(read_mtu, kInvalidReadMtu); |
+ CHECK_GT(write_mtu, kInvalidWriteMtu); |
+ |
+ // Avoids unnecessary memory reallocation if read MTU doesn't change. |
+ if (read_mtu != read_mtu_) { |
+ read_mtu_ = read_mtu; |
+ data_.reset(new char[read_mtu_]); |
+ VLOG(1) << "OnAcquireSucceeded - allocate " << read_mtu_ |
+ << " bytes of memory"; |
+ } |
+ |
+ write_mtu_ = write_mtu; |
+ |
+ // Avoids closing the same file descriptor caused by reassignment. |
+ if (!file_.get() || file_->GetPlatformFile() != fd->value()) { |
+ // Takes ownership of the file descriptor. |
+ file_.reset(new base::File(fd->TakeValue())); |
+ DCHECK(file_->IsValid()); |
+ VLOG(1) << "OnAcquireSucceeded - update file"; |
+ } |
+ |
+ VLOG(1) << "OnAcquireSucceeded - file: " << file_->GetPlatformFile() |
+ << ", read MTU: " << read_mtu_ << ", write MTU: " << write_mtu_; |
+} |
+ |
+void BluetoothAudioSinkChromeOS::OnAcquireFailed( |
+ const std::string& error_name, |
+ const std::string& error_message) { |
+ VLOG(1) << "OnAcquireFailed - error name: " << error_name |
+ << ", error message: " << error_message; |
+} |
- // TODO(mcchou): BUG=441581 |
- // Read from file descriptor using watcher and create a buffer to contain the |
- // data. Notify |Observers_| while there is audio data available. |
+void BluetoothAudioSinkChromeOS::OnReleaseFDSucceeded() { |
+ VLOG(1) << "OnReleaseFDSucceeded"; |
+} |
+ |
+void BluetoothAudioSinkChromeOS::OnReleaseFDFailed( |
+ const std::string& error_name, |
+ const std::string& error_message) { |
+ VLOG(1) << "OnReleaseFDFailed - error name: " << error_name |
+ << ", error message: " << error_message; |
} |
void BluetoothAudioSinkChromeOS::ResetMedia() { |
@@ -430,15 +544,18 @@ void BluetoothAudioSinkChromeOS::ResetMedia() { |
} |
void BluetoothAudioSinkChromeOS::ResetTransport() { |
- VLOG(1) << "ResetTransport"; |
- |
- if (transport_path_.value() == "") |
+ if (!transport_path_.IsValid()) { |
+ VLOG(1) << "ResetTransport - skip"; |
return; |
- transport_path_ = dbus::ObjectPath(""); |
+ } |
+ |
+ VLOG(1) << "ResetTransport - clean-up"; |
+ |
VolumeChanged(BluetoothAudioSink::kInvalidVolume); |
- read_mtu_ = 0; |
- write_mtu_ = 0; |
- fd_.PutValue(-1); |
+ transport_path_ = dbus::ObjectPath(""); |
+ read_mtu_ = kInvalidReadMtu; |
+ write_mtu_ = kInvalidWriteMtu; |
+ file_.reset(); |
} |
void BluetoothAudioSinkChromeOS::ResetEndpoint() { |