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 7791563070b0ec7dd02a834d297d1fb0846a6469..98124e87263c207558a893d1c378b0fefaf91684 100644 |
--- a/device/bluetooth/bluetooth_audio_sink_chromeos.cc |
+++ b/device/bluetooth/bluetooth_audio_sink_chromeos.cc |
@@ -4,6 +4,7 @@ |
#include "device/bluetooth/bluetooth_audio_sink_chromeos.h" |
+#include <algorithm> |
#include <sstream> |
#include <vector> |
@@ -13,17 +14,26 @@ |
#include "dbus/message.h" |
#include "device/bluetooth/bluetooth_adapter_chromeos.h" |
+using dbus::ObjectPath; |
+using device::BluetoothAudioSink; |
+ |
namespace { |
// TODO(mcchou): Add the constant to dbus/service_constants.h. |
const char kBluetoothAudioSinkServicePath[] = "/org/chromium/AudioSink"; |
-dbus::ObjectPath GenerateEndpointPath() { |
+ObjectPath GenerateEndpointPath() { |
static unsigned int sequence_number = 0; |
++sequence_number; |
std::stringstream path; |
path << kBluetoothAudioSinkServicePath << "/endpoint" << sequence_number; |
- return dbus::ObjectPath(path.str()); |
+ return ObjectPath(path.str()); |
+} |
+ |
+// A dummy error callback for calling Unregister() in destructor. |
+void UnregisterErrorCallback( |
+ device::BluetoothAudioSink::ErrorCode error_code) { |
+ VLOG(1) << "Bluetooth audio sink: Error code: " << error_code; |
} |
} // namespace |
@@ -32,10 +42,10 @@ namespace chromeos { |
BluetoothAudioSinkChromeOS::BluetoothAudioSinkChromeOS( |
scoped_refptr<device::BluetoothAdapter> adapter) |
- : state_(device::BluetoothAudioSink::STATE_INVALID), |
- volume_(0), |
- read_mtu_(0), |
- write_mtu_(0), |
+ : state_(BluetoothAudioSink::STATE_INVALID), |
+ volume_(BluetoothAudioSink::kInvalidVolume), |
+ read_mtu_(nullptr), |
+ write_mtu_(nullptr), |
adapter_(adapter), |
weak_ptr_factory_(this) { |
DCHECK(adapter_.get()); |
@@ -58,6 +68,12 @@ BluetoothAudioSinkChromeOS::BluetoothAudioSinkChromeOS( |
BluetoothAudioSinkChromeOS::~BluetoothAudioSinkChromeOS() { |
DCHECK(adapter_.get()); |
+ |
+ if (state_ != BluetoothAudioSink::STATE_INVALID && media_endpoint_.get()) { |
+ Unregister(base::Bind(&base::DoNothing), |
+ base::Bind(&UnregisterErrorCallback)); |
+ } |
+ |
adapter_->RemoveObserver(this); |
chromeos::BluetoothMediaClient* media = |
@@ -69,35 +85,40 @@ BluetoothAudioSinkChromeOS::~BluetoothAudioSinkChromeOS() { |
chromeos::DBusThreadManager::Get()->GetBluetoothMediaTransportClient(); |
DCHECK(transport); |
transport->RemoveObserver(this); |
- |
- // TODO(mcchou): BUG=441581 |
- // Unregister() should be called while media and media endpoint are still |
- // valid. |
} |
void BluetoothAudioSinkChromeOS::Unregister( |
const base::Closure& callback, |
const device::BluetoothAudioSink::ErrorCallback& error_callback) { |
- // TODO(mcchou): BUG=441581 |
- // Call UnregisterEndpoint on the media object with |media_path_| and clean |
- // |observers_| and transport_path_ and reset state_ and volume. |
- // Check whether the media endpoint is registered before invoking |
- // UnregisterEndpoint. |
+ if (!DBusThreadManager::IsInitialized()) |
+ error_callback.Run(BluetoothAudioSink::ERROR_NOT_UNREGISTERED); |
+ |
+ chromeos::BluetoothMediaClient* media = |
+ DBusThreadManager::Get()->GetBluetoothMediaClient(); |
+ DCHECK(media); |
+ |
+ media->UnregisterEndpoint( |
+ media_path_, |
+ endpoint_path_, |
+ base::Bind(&BluetoothAudioSinkChromeOS::OnUnregisterSucceeded, |
+ weak_ptr_factory_.GetWeakPtr(), callback), |
+ base::Bind(&BluetoothAudioSinkChromeOS::OnUnregisterFailed, |
+ weak_ptr_factory_.GetWeakPtr(), error_callback)); |
} |
void BluetoothAudioSinkChromeOS::AddObserver( |
- device::BluetoothAudioSink::Observer* observer) { |
+ BluetoothAudioSink::Observer* observer) { |
DCHECK(observer); |
observers_.AddObserver(observer); |
} |
void BluetoothAudioSinkChromeOS::RemoveObserver( |
- device::BluetoothAudioSink::Observer* observer) { |
+ BluetoothAudioSink::Observer* observer) { |
DCHECK(observer); |
observers_.RemoveObserver(observer); |
} |
-device::BluetoothAudioSink::State BluetoothAudioSinkChromeOS::GetState() const { |
+BluetoothAudioSink::State BluetoothAudioSinkChromeOS::GetState() const { |
return state_; |
} |
@@ -106,39 +127,49 @@ uint16_t BluetoothAudioSinkChromeOS::GetVolume() const { |
} |
void BluetoothAudioSinkChromeOS::AdapterPresentChanged( |
- device::BluetoothAdapter* adapter, |
- bool present) { |
+ device::BluetoothAdapter* adapter, bool present) { |
VLOG(1) << "Bluetooth audio sink: Bluetooth adapter present changed: " |
<< present; |
if (adapter->IsPresent()) { |
- StateChanged(device::BluetoothAudioSink::STATE_DISCONNECTED); |
+ StateChanged(BluetoothAudioSink::STATE_DISCONNECTED); |
} else { |
- StateChanged(device::BluetoothAudioSink::STATE_INVALID); |
+ adapter_->RemoveObserver(this); |
+ StateChanged(BluetoothAudioSink::STATE_INVALID); |
} |
} |
-void BluetoothAudioSinkChromeOS::MediaRemoved( |
- const dbus::ObjectPath& object_path) { |
- // TODO(mcchou): BUG=441581 |
- // Changes |state_| to STATE_INVALID or STATE_DISCONNECTED? |
+void BluetoothAudioSinkChromeOS::AdapterPoweredChanged( |
+ device::BluetoothAdapter* adapter, bool powered) { |
+ VLOG(1) << "Bluetooth audio sink: Bluetooth adapter powered changed: " |
+ << powered; |
+ |
+ // Regardless of the new powered state, |state_| goes to STATE_DISCONNECTED. |
+ // If false, the transport is closed, but the endpoint is still valid for use. |
+ // If true, the previous transport has been torn down, so the |state_| has to |
+ // be disconnected before SetConfigruation is called. |
+ if (state_ != BluetoothAudioSink::STATE_INVALID) |
+ StateChanged(BluetoothAudioSink::STATE_DISCONNECTED); |
+} |
+ |
+void BluetoothAudioSinkChromeOS::MediaRemoved(const ObjectPath& object_path) { |
if (object_path == media_path_) { |
- StateChanged(device::BluetoothAudioSink::STATE_INVALID); |
+ StateChanged(BluetoothAudioSink::STATE_INVALID); |
} |
} |
void BluetoothAudioSinkChromeOS::MediaTransportRemoved( |
- const dbus::ObjectPath& object_path) { |
+ const ObjectPath& object_path) { |
// Whenever powered of |adapter_| turns false while present stays true, media |
// transport object should be removed accordingly, and the state should be |
// changed to STATE_DISCONNECTED. |
if (object_path == transport_path_) { |
- StateChanged(device::BluetoothAudioSink::STATE_DISCONNECTED); |
+ StateChanged(BluetoothAudioSink::STATE_DISCONNECTED); |
} |
} |
void BluetoothAudioSinkChromeOS::MediaTransportPropertyChanged( |
- const dbus::ObjectPath& object_path, |
+ const ObjectPath& object_path, |
const std::string& property_name) { |
if (object_path != transport_path_) |
return; |
@@ -153,13 +184,13 @@ void BluetoothAudioSinkChromeOS::MediaTransportPropertyChanged( |
if (property_name == properties->state.name()) { |
if (properties->state.value() == |
BluetoothMediaTransportClient::kStateIdle) { |
- StateChanged(device::BluetoothAudioSink::STATE_IDLE); |
+ StateChanged(BluetoothAudioSink::STATE_IDLE); |
} else if (properties->state.value() == |
BluetoothMediaTransportClient::kStatePending) { |
- StateChanged(device::BluetoothAudioSink::STATE_PENDING); |
+ StateChanged(BluetoothAudioSink::STATE_PENDING); |
} else if (properties->state.value() == |
BluetoothMediaTransportClient::kStateActive) { |
- StateChanged(device::BluetoothAudioSink::STATE_ACTIVE); |
+ StateChanged(BluetoothAudioSink::STATE_ACTIVE); |
} |
} else if (property_name == properties->volume.name()) { |
VolumeChanged(properties->volume.value()); |
@@ -170,7 +201,7 @@ void BluetoothAudioSinkChromeOS::MediaTransportPropertyChanged( |
} |
void BluetoothAudioSinkChromeOS::SetConfiguration( |
- const dbus::ObjectPath& transport_path, |
+ const ObjectPath& transport_path, |
const TransportProperties& properties) { |
VLOG(1) << "Bluetooth audio sink: SetConfiguration called"; |
transport_path_ = transport_path; |
@@ -182,10 +213,11 @@ void BluetoothAudioSinkChromeOS::SetConfiguration( |
} |
// Updates |volume_| if the volume level is provided in |properties|. |
- if (properties.volume.get() != nullptr) |
+ if (properties.volume.get()) { |
VolumeChanged(*properties.volume); |
+ } |
- StateChanged(device::BluetoothAudioSink::STATE_IDLE); |
+ StateChanged(BluetoothAudioSink::STATE_IDLE); |
} |
void BluetoothAudioSinkChromeOS::SelectConfiguration( |
@@ -196,22 +228,24 @@ void BluetoothAudioSinkChromeOS::SelectConfiguration( |
} |
void BluetoothAudioSinkChromeOS::ClearConfiguration( |
- const dbus::ObjectPath& transport_path) { |
+ const ObjectPath& transport_path) { |
+ if (transport_path != transport_path_) |
+ return; |
VLOG(1) << "Bluetooth audio sink: ClearConfiguration called"; |
- StateChanged(device::BluetoothAudioSink::STATE_DISCONNECTED); |
+ StateChanged(BluetoothAudioSink::STATE_DISCONNECTED); |
} |
void BluetoothAudioSinkChromeOS::Released() { |
VLOG(1) << "Bluetooth audio sink: Released called"; |
- StateChanged(device::BluetoothAudioSink::STATE_INVALID); |
+ StateChanged(BluetoothAudioSink::STATE_INVALID); |
} |
void BluetoothAudioSinkChromeOS::Register( |
- const device::BluetoothAudioSink::Options& options, |
+ const BluetoothAudioSink::Options& options, |
const base::Closure& callback, |
- const device::BluetoothAudioSink::ErrorCallback& error_callback) { |
+ const BluetoothAudioSink::ErrorCallback& error_callback) { |
DCHECK(adapter_.get()); |
- DCHECK_EQ(state_, device::BluetoothAudioSink::STATE_DISCONNECTED); |
+ DCHECK_EQ(state_, BluetoothAudioSink::STATE_DISCONNECTED); |
// Gets system bus. |
dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus(); |
@@ -253,65 +287,58 @@ BluetoothMediaEndpointServiceProvider* |
} |
void BluetoothAudioSinkChromeOS::StateChanged( |
- device::BluetoothAudioSink::State state) { |
+ BluetoothAudioSink::State state) { |
if (state == state_) |
return; |
VLOG(1) << "Bluetooth audio sink state changed: " << state; |
- |
switch (state) { |
- case device::BluetoothAudioSink::STATE_INVALID: { |
- // TODO(mcchou): BUG=441581 |
+ case BluetoothAudioSink::STATE_INVALID: |
ResetMedia(); |
- ResetTransport(); |
ResetEndpoint(); |
- break; |
- } |
- case device::BluetoothAudioSink::STATE_DISCONNECTED: { |
- // TODO(mcchou): BUG=441581 |
- // Clean media transport and remove the audio sink from its observer list. |
+ case BluetoothAudioSink::STATE_DISCONNECTED: |
ResetTransport(); |
break; |
- } |
- case device::BluetoothAudioSink::STATE_IDLE: { |
+ case BluetoothAudioSink::STATE_IDLE: |
// TODO(mcchou): BUG=441581 |
// Triggered by MediaTransportPropertyChanged and SetConfiguration. |
// Stop watching on file descriptor if there is one. |
break; |
- } |
- case device::BluetoothAudioSink::STATE_PENDING: { |
+ case BluetoothAudioSink::STATE_PENDING: |
// TODO(mcchou): BUG=441581 |
// Call BluetoothMediaTransportClient::Acquire() to get fd and mtus. |
break; |
- } |
- case device::BluetoothAudioSink::STATE_ACTIVE: { |
+ case BluetoothAudioSink::STATE_ACTIVE: |
// TODO(mcchou): BUG=441581 |
// Read from fd and call DataAvailable. |
ReadFromFD(); |
break; |
- } |
default: |
break; |
} |
state_ = state; |
- FOR_EACH_OBSERVER(device::BluetoothAudioSink::Observer, observers_, |
+ FOR_EACH_OBSERVER(BluetoothAudioSink::Observer, observers_, |
BluetoothAudioSinkStateChanged(this, state_)); |
} |
void BluetoothAudioSinkChromeOS::VolumeChanged(uint16_t volume) { |
- DCHECK_NE(volume, volume_); |
+ if (volume == volume_) |
+ return; |
+ |
VLOG(1) << "Bluetooth audio sink volume changed: " << volume; |
- volume_ = volume; |
- FOR_EACH_OBSERVER(device::BluetoothAudioSink::Observer, observers_, |
+ volume_ = std::min(volume, BluetoothAudioSink::kInvalidVolume); |
+ |
+ FOR_EACH_OBSERVER(BluetoothAudioSink::Observer, observers_, |
BluetoothAudioSinkVolumeChanged(this, volume_)); |
} |
void BluetoothAudioSinkChromeOS::OnRegisterSucceeded( |
const base::Closure& callback) { |
+ DCHECK(media_endpoint_.get()); |
VLOG(1) << "Bluetooth audio sink registerd"; |
- StateChanged(device::BluetoothAudioSink::STATE_DISCONNECTED); |
+ StateChanged(BluetoothAudioSink::STATE_DISCONNECTED); |
callback.Run(); |
} |
@@ -319,11 +346,28 @@ void BluetoothAudioSinkChromeOS::OnRegisterFailed( |
const BluetoothAudioSink::ErrorCallback& error_callback, |
const std::string& error_name, |
const std::string& error_message) { |
- DCHECK(media_endpoint_.get()); |
VLOG(1) << "Bluetooth audio sink: " << error_name << ": " << error_message; |
ResetEndpoint(); |
- error_callback.Run(device::BluetoothAudioSink::ERROR_NOT_REGISTERED); |
+ error_callback.Run(BluetoothAudioSink::ERROR_NOT_REGISTERED); |
+} |
+ |
+void BluetoothAudioSinkChromeOS::OnUnregisterSucceeded( |
+ const base::Closure& callback) { |
+ VLOG(1) << "Bluetooth audio sink unregisterd"; |
+ |
+ // Once the state becomes STATE_INVALID, media, media transport and media |
+ // endpoint will be reset. |
+ StateChanged(BluetoothAudioSink::STATE_INVALID); |
+ callback.Run(); |
+} |
+ |
+void BluetoothAudioSinkChromeOS::OnUnregisterFailed( |
+ const device::BluetoothAudioSink::ErrorCallback& error_callback, |
+ const std::string& error_name, |
+ const std::string& error_message) { |
+ VLOG(1) << "Bluetooth audio sink: " << error_name << ": " << error_message; |
+ error_callback.Run(BluetoothAudioSink::ERROR_NOT_UNREGISTERED); |
} |
void BluetoothAudioSinkChromeOS::ReadFromFD() { |
@@ -339,15 +383,17 @@ void BluetoothAudioSinkChromeOS::ResetMedia() { |
} |
void BluetoothAudioSinkChromeOS::ResetTransport() { |
+ if (transport_path_.value() == "") |
+ return; |
transport_path_ = dbus::ObjectPath(""); |
- volume_ = 0; |
+ VolumeChanged(BluetoothAudioSink::kInvalidVolume); |
read_mtu_ = 0; |
write_mtu_ = 0; |
fd_.PutValue(-1); |
} |
void BluetoothAudioSinkChromeOS::ResetEndpoint() { |
- endpoint_path_ = dbus::ObjectPath(""); |
+ endpoint_path_ = ObjectPath(""); |
media_endpoint_ = nullptr; |
} |