diff options
author | mcchou <mcchou@chromium.org> | 2015-02-17 16:21:11 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-18 00:21:59 +0000 |
commit | 016980f10044b64144293f9e1ba0ce4c5fddeda3 (patch) | |
tree | ee052114c6bde9341035489a35848b2f61659517 /device | |
parent | 1ff220bced57014ddeb99e321aa30f58efa15b15 (diff) | |
download | chromium_src-016980f10044b64144293f9e1ba0ce4c5fddeda3.zip chromium_src-016980f10044b64144293f9e1ba0ce4c5fddeda3.tar.gz chromium_src-016980f10044b64144293f9e1ba0ce4c5fddeda3.tar.bz2 |
device/bluetooth:Implement BluetoothMediaEndpointServiceProvider delegate and media-related overrides.
This CL implements delegate overrides for
BluetoothMediaEndpointServiceProvider and observer overrides for
BluetoothMediaClient and BluetoothMediaTransportClient. These
overrides will be called once a remote device connects to a local
A2DP audio sink, and the state of the audio sink should become idle.
The behaviors of FakeBluetoothMediaTransportClient,
FakeBluetoothMediaEndpointServiceProvider and
FakeMediaEndpointServiceProvider are also defined for
connection-related tests in this CL.
BUG=441581
TEST=device_unittests --gtest_filter=*AudioSink*
Review URL: https://codereview.chromium.org/910023002
Cr-Commit-Position: refs/heads/master@{#316709}
Diffstat (limited to 'device')
-rw-r--r-- | device/bluetooth/bluetooth_audio_sink_chromeos.cc | 162 | ||||
-rw-r--r-- | device/bluetooth/bluetooth_audio_sink_chromeos.h | 28 | ||||
-rw-r--r-- | device/bluetooth/bluetooth_audio_sink_chromeos_unittest.cc | 259 |
3 files changed, 372 insertions, 77 deletions
diff --git a/device/bluetooth/bluetooth_audio_sink_chromeos.cc b/device/bluetooth/bluetooth_audio_sink_chromeos.cc index 9623c78..7791563 100644 --- a/device/bluetooth/bluetooth_audio_sink_chromeos.cc +++ b/device/bluetooth/bluetooth_audio_sink_chromeos.cc @@ -5,9 +5,12 @@ #include "device/bluetooth/bluetooth_audio_sink_chromeos.h" #include <sstream> +#include <vector> +#include "base/debug/stack_trace.h" #include "base/logging.h" #include "chromeos/dbus/dbus_thread_manager.h" +#include "dbus/message.h" #include "device/bluetooth/bluetooth_adapter_chromeos.h" namespace { @@ -38,18 +41,50 @@ BluetoothAudioSinkChromeOS::BluetoothAudioSinkChromeOS( DCHECK(adapter_.get()); DCHECK(adapter_->IsPresent()); - StateChanged(device::BluetoothAudioSink::STATE_DISCONNECTED); adapter_->AddObserver(this); + + chromeos::BluetoothMediaClient* media = + DBusThreadManager::Get()->GetBluetoothMediaClient(); + DCHECK(media); + media->AddObserver(this); + + chromeos::BluetoothMediaTransportClient *transport = + chromeos::DBusThreadManager::Get()->GetBluetoothMediaTransportClient(); + DCHECK(transport); + transport->AddObserver(this); + + StateChanged(device::BluetoothAudioSink::STATE_DISCONNECTED); } BluetoothAudioSinkChromeOS::~BluetoothAudioSinkChromeOS() { DCHECK(adapter_.get()); adapter_->RemoveObserver(this); + + chromeos::BluetoothMediaClient* media = + DBusThreadManager::Get()->GetBluetoothMediaClient(); + DCHECK(media); + media->RemoveObserver(this); + + chromeos::BluetoothMediaTransportClient *transport = + 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. +} + void BluetoothAudioSinkChromeOS::AddObserver( device::BluetoothAudioSink::Observer* observer) { DCHECK(observer); @@ -86,52 +121,88 @@ void BluetoothAudioSinkChromeOS::AdapterPresentChanged( void BluetoothAudioSinkChromeOS::MediaRemoved( const dbus::ObjectPath& object_path) { // TODO(mcchou): BUG=441581 - // Check if |object_path| equals to |media_path_|. If true, change the state - // of the audio sink, call StateChanged and reset the audio sink. + // Changes |state_| to STATE_INVALID or STATE_DISCONNECTED? + if (object_path == media_path_) { + StateChanged(device::BluetoothAudioSink::STATE_INVALID); + } } void BluetoothAudioSinkChromeOS::MediaTransportRemoved( const dbus::ObjectPath& object_path) { - // TODO(mcchou): BUG=441581 - // Check if |object_path| equals to |transport_path_|. If true, change the - // state of the audio sink, call StateChanged and reset the audio sink. // 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); + } } void BluetoothAudioSinkChromeOS::MediaTransportPropertyChanged( const dbus::ObjectPath& object_path, const std::string& property_name) { - // TODO(mcchou): BUG=441581 - // Call StateChanged and VolumeChanged accordingly if there is any change on - // state/volume. + if (object_path != transport_path_) + return; + + // Retrieves the property set of the transport object with |object_path|. + chromeos::BluetoothMediaTransportClient::Properties* properties = + DBusThreadManager::Get() + ->GetBluetoothMediaTransportClient() + ->GetProperties(object_path); + + // Dispatches a property changed event to the corresponding handler. + if (property_name == properties->state.name()) { + if (properties->state.value() == + BluetoothMediaTransportClient::kStateIdle) { + StateChanged(device::BluetoothAudioSink::STATE_IDLE); + } else if (properties->state.value() == + BluetoothMediaTransportClient::kStatePending) { + StateChanged(device::BluetoothAudioSink::STATE_PENDING); + } else if (properties->state.value() == + BluetoothMediaTransportClient::kStateActive) { + StateChanged(device::BluetoothAudioSink::STATE_ACTIVE); + } + } else if (property_name == properties->volume.name()) { + VolumeChanged(properties->volume.value()); + } else { + VLOG(1) << "Bluetooth audio sink: transport property " << property_name + << " changed"; + } } void BluetoothAudioSinkChromeOS::SetConfiguration( const dbus::ObjectPath& transport_path, - const dbus::MessageReader& properties) { - // TODO(mcchou): BUG=441581 - // Update |transport_path_| and store properties if needed. + const TransportProperties& properties) { + VLOG(1) << "Bluetooth audio sink: SetConfiguration called"; + transport_path_ = transport_path; + + // The initial state for a connection should be "idle". + if (properties.state != BluetoothMediaTransportClient::kStateIdle) { + VLOG(1) << "Bluetooth Audio Sink: unexpected state " << properties.state; + return; + } + + // Updates |volume_| if the volume level is provided in |properties|. + if (properties.volume.get() != nullptr) + VolumeChanged(*properties.volume); + + StateChanged(device::BluetoothAudioSink::STATE_IDLE); } void BluetoothAudioSinkChromeOS::SelectConfiguration( const std::vector<uint8_t>& capabilities, const SelectConfigurationCallback& callback) { - // TODO(mcchou): BUG=441581 - // Use SelectConfigurationCallback to return the agreed capabilities. VLOG(1) << "Bluetooth audio sink: SelectConfiguration called"; callback.Run(options_.capabilities); } void BluetoothAudioSinkChromeOS::ClearConfiguration( const dbus::ObjectPath& transport_path) { - // TODO(mcchou): BUG=441581 - // Reset the configuration to the default one and close IOBuffer. + VLOG(1) << "Bluetooth audio sink: ClearConfiguration called"; + StateChanged(device::BluetoothAudioSink::STATE_DISCONNECTED); } -void BluetoothAudioSinkChromeOS::Release() { - VLOG(1) << "Bluetooth audio sink: Release called"; +void BluetoothAudioSinkChromeOS::Released() { + VLOG(1) << "Bluetooth audio sink: Released called"; StateChanged(device::BluetoothAudioSink::STATE_INVALID); } @@ -139,10 +210,6 @@ void BluetoothAudioSinkChromeOS::Register( const device::BluetoothAudioSink::Options& options, const base::Closure& callback, const device::BluetoothAudioSink::ErrorCallback& error_callback) { - // TODO(mcchou): BUG=441581 - // Get Media object, initiate an Media Endpoint with options, and return the - // audio sink via callback. Add the audio sink as observer of both Media and - // Media Transport. DCHECK(adapter_.get()); DCHECK_EQ(state_, device::BluetoothAudioSink::STATE_DISCONNECTED); @@ -164,13 +231,12 @@ void BluetoothAudioSinkChromeOS::Register( endpoint_properties.codec = options_.codec; endpoint_properties.capabilities = options_.capabilities; - // Gets Media object exported by D-Bus and registers the endpoint. + media_path_ = static_cast<BluetoothAdapterChromeOS*>( + adapter_.get())->object_path(); + chromeos::BluetoothMediaClient* media = DBusThreadManager::Get()->GetBluetoothMediaClient(); - BluetoothAdapterChromeOS* adapter_chromeos = - static_cast<BluetoothAdapterChromeOS*>(adapter_.get()); - media->AddObserver(this); - media_path_ = adapter_chromeos->object_path(); + DCHECK(media); media->RegisterEndpoint( media_path_, endpoint_path_, @@ -181,14 +247,9 @@ void BluetoothAudioSinkChromeOS::Register( weak_ptr_factory_.GetWeakPtr(), error_callback)); } -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. +BluetoothMediaEndpointServiceProvider* + BluetoothAudioSinkChromeOS::GetEndpointServiceProvider() { + return media_endpoint_.get(); } void BluetoothAudioSinkChromeOS::StateChanged( @@ -201,18 +262,21 @@ void BluetoothAudioSinkChromeOS::StateChanged( switch (state) { case device::BluetoothAudioSink::STATE_INVALID: { // TODO(mcchou): BUG=441581 - // Clean media, media transport and media endpoint. + ResetMedia(); + ResetTransport(); + ResetEndpoint(); break; } case device::BluetoothAudioSink::STATE_DISCONNECTED: { // TODO(mcchou): BUG=441581 - // Clean media transport. + // Clean media transport and remove the audio sink from its observer list. + ResetTransport(); break; } case device::BluetoothAudioSink::STATE_IDLE: { // TODO(mcchou): BUG=441581 - // Triggered by MediaTransportPropertyChanged. Stop watching on file - // descriptor if there is one. + // Triggered by MediaTransportPropertyChanged and SetConfiguration. + // Stop watching on file descriptor if there is one. break; } case device::BluetoothAudioSink::STATE_PENDING: { @@ -258,8 +322,7 @@ void BluetoothAudioSinkChromeOS::OnRegisterFailed( DCHECK(media_endpoint_.get()); VLOG(1) << "Bluetooth audio sink: " << error_name << ": " << error_message; - endpoint_path_ = dbus::ObjectPath(""); - media_endpoint_ = nullptr; + ResetEndpoint(); error_callback.Run(device::BluetoothAudioSink::ERROR_NOT_REGISTERED); } @@ -271,4 +334,21 @@ void BluetoothAudioSinkChromeOS::ReadFromFD() { // data. Notify |Observers_| while there is audio data available. } +void BluetoothAudioSinkChromeOS::ResetMedia() { + media_path_ = dbus::ObjectPath(""); +} + +void BluetoothAudioSinkChromeOS::ResetTransport() { + transport_path_ = dbus::ObjectPath(""); + volume_ = 0; + read_mtu_ = 0; + write_mtu_ = 0; + fd_.PutValue(-1); +} + +void BluetoothAudioSinkChromeOS::ResetEndpoint() { + endpoint_path_ = dbus::ObjectPath(""); + media_endpoint_ = nullptr; +} + } // namespace chromeos diff --git a/device/bluetooth/bluetooth_audio_sink_chromeos.h b/device/bluetooth/bluetooth_audio_sink_chromeos.h index 37994d3..3af285b 100644 --- a/device/bluetooth/bluetooth_audio_sink_chromeos.h +++ b/device/bluetooth/bluetooth_audio_sink_chromeos.h @@ -23,6 +23,8 @@ namespace chromeos { +class BluetoothAudioSinkChromeOSTest; + class DEVICE_BLUETOOTH_EXPORT BluetoothAudioSinkChromeOS : public device::BluetoothAudioSink, public device::BluetoothAdapter::Observer, @@ -34,6 +36,12 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAudioSinkChromeOS scoped_refptr<device::BluetoothAdapter> adapter); // device::BluetoothAudioSink overrides. + // Unregisters a BluetoothAudioSink. |callback| should handle + // the clean-up after the audio sink is deleted successfully, otherwise + // |error_callback| will be called. + void Unregister( + const base::Closure& callback, + const device::BluetoothAudioSink::ErrorCallback& error_callback) override; void AddObserver(BluetoothAudioSink::Observer* observer) override; void RemoveObserver(BluetoothAudioSink::Observer* observer) override; device::BluetoothAudioSink::State GetState() const override; @@ -53,12 +61,12 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAudioSinkChromeOS // BluetoothMediaEndpointServiceProvider::Delegate overrides. void SetConfiguration(const dbus::ObjectPath& transport_path, - const dbus::MessageReader& properties) override; + const TransportProperties& properties) override; void SelectConfiguration( const std::vector<uint8_t>& capabilities, const SelectConfigurationCallback& callback) override; void ClearConfiguration(const dbus::ObjectPath& transport_path) override; - void Release() override; + void Released() override; // Registers a BluetoothAudioSink. User applications can use |options| to // configure the audio sink. |callback| will be executed if the audio sink is @@ -69,12 +77,9 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAudioSinkChromeOS const base::Closure& callback, const device::BluetoothAudioSink::ErrorCallback& error_callback); - // Unregisters a BluetoothAudioSink. |callback| should handle - // the clean-up after the audio sink is deleted successfully, otherwise - // |error_callback| will be called. - void Unregister( - const base::Closure& callback, - const device::BluetoothAudioSink::ErrorCallback& error_callback) override; + // Returns a pointer to the media endpoint object. This function should be + // used for testing purpose only. + BluetoothMediaEndpointServiceProvider* GetEndpointServiceProvider(); private: ~BluetoothAudioSinkChromeOS() override; @@ -98,6 +103,13 @@ class DEVICE_BLUETOOTH_EXPORT BluetoothAudioSinkChromeOS // notify |observer_| while the audio data is available. void ReadFromFD(); + // Helper functions to clean up media, media transport and media endpoint. + // Called when the |state_| changes to either STATE_INVALID or + // STATE_DISCONNECTED. + void ResetMedia(); + void ResetTransport(); + void ResetEndpoint(); + // The connection state between the BluetoothAudioSinkChromeOS and the remote // device. device::BluetoothAudioSink::State state_; diff --git a/device/bluetooth/bluetooth_audio_sink_chromeos_unittest.cc b/device/bluetooth/bluetooth_audio_sink_chromeos_unittest.cc index 5fcccfb..9aa727f 100644 --- a/device/bluetooth/bluetooth_audio_sink_chromeos_unittest.cc +++ b/device/bluetooth/bluetooth_audio_sink_chromeos_unittest.cc @@ -8,7 +8,13 @@ #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/message_loop/message_loop.h" +#include "chromeos/dbus/bluetooth_media_client.h" +#include "chromeos/dbus/bluetooth_media_endpoint_service_provider.h" +#include "chromeos/dbus/bluetooth_media_transport_client.h" #include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_bluetooth_media_client.h" +#include "chromeos/dbus/fake_bluetooth_media_endpoint_service_provider.h" +#include "chromeos/dbus/fake_bluetooth_media_transport_client.h" #include "dbus/object_path.h" #include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_adapter_factory.h" @@ -16,6 +22,7 @@ #include "device/bluetooth/bluetooth_audio_sink_chromeos.h" #include "testing/gtest/include/gtest/gtest.h" +using dbus::ObjectPath; using device::BluetoothAdapter; using device::BluetoothAdapterFactory; using device::BluetoothAudioSink; @@ -56,12 +63,23 @@ class TestAudioSinkObserver : public BluetoothAudioSink::Observer { class BluetoothAudioSinkChromeOSTest : public testing::Test { public: void SetUp() override { - chromeos::DBusThreadManager::Initialize(); + DBusThreadManager::Initialize(); callback_count_ = 0; error_callback_count_ = 0; - audio_sink_ = nullptr; - adapter_ = nullptr; + + // Initiates Delegate::TransportProperties with default values. + properties_.device = + ObjectPath(FakeBluetoothMediaTransportClient::kTransportDevicePath); + properties_.uuid = BluetoothMediaClient::kBluetoothAudioSinkUUID; + properties_.codec = FakeBluetoothMediaTransportClient::kTransportCodec; + properties_.configuration = + FakeBluetoothMediaTransportClient::kTransportConfiguration; + properties_.state = BluetoothMediaTransportClient::kStateIdle; + properties_.delay.reset( + new uint16_t(FakeBluetoothMediaTransportClient::kTransportDelay)); + properties_.volume.reset( + new uint16_t(FakeBluetoothMediaTransportClient::kTransportVolume)); GetAdapter(); } @@ -76,13 +94,14 @@ class BluetoothAudioSinkChromeOSTest : public testing::Test { DBusThreadManager::Shutdown(); } - // Get the existing Bluetooth adapter. + // Gets the existing Bluetooth adapter. void GetAdapter() { BluetoothAdapterFactory::GetAdapter( base::Bind(&BluetoothAudioSinkChromeOSTest::GetAdapterCallback, base::Unretained(this))); } + // Called whenever BluetoothAdapter is retrieved successfully. void GetAdapterCallback(scoped_refptr<BluetoothAdapter> adapter) { adapter_ = adapter; @@ -96,11 +115,33 @@ class BluetoothAudioSinkChromeOSTest : public testing::Test { base::Unretained(this))); ASSERT_TRUE(adapter_->IsPresent()); ASSERT_TRUE(adapter_->IsPowered()); - ASSERT_EQ(callback_count_, 1); - ASSERT_EQ(error_callback_count_, 0); + EXPECT_EQ(callback_count_, 1); + EXPECT_EQ(error_callback_count_, 0); + + // Resets callback_count_. --callback_count_; } + // Registers BluetoothAudioSinkChromeOS with default codec and capabilities. + void GetAudioSink() { + // Sets up valid codec and capabilities. + BluetoothAudioSink::Options options; + ASSERT_EQ(options.codec, 0x00); + ASSERT_EQ(options.capabilities, + std::vector<uint8_t>({0x3f, 0xff, 0x12, 0x35})); + + // Registers |audio_sink_| with valid codec and capabilities + adapter_->RegisterAudioSink( + options, + base::Bind(&BluetoothAudioSinkChromeOSTest::RegisterCallback, + base::Unretained(this)), + base::Bind(&BluetoothAudioSinkChromeOSTest::RegisterErrorCallback, + base::Unretained(this))); + + EXPECT_EQ(callback_count_, 1); + EXPECT_EQ(error_callback_count_, 0); + } + // Called whenever RegisterAudioSink is completed successfully. void RegisterCallback( scoped_refptr<BluetoothAudioSink> audio_sink) { @@ -113,7 +154,16 @@ class BluetoothAudioSinkChromeOSTest : public testing::Test { // Called whenever RegisterAudioSink failed. void RegisterErrorCallback(BluetoothAudioSink::ErrorCode error_code) { ++error_callback_count_; - ASSERT_EQ(error_code, BluetoothAudioSink::ERROR_NOT_REGISTERED); + EXPECT_EQ(error_code, BluetoothAudioSink::ERROR_NOT_REGISTERED); + } + + // Called whenever there capabilities are returned from SelectConfiguration. + void SelectConfigurationCallback(const std::vector<uint8_t>& capabilities) { + ++callback_count_; + + // |capabilities| should be the same as the capabilities for registering an + // audio sink in GetAudioSink(). + EXPECT_EQ(capabilities, std::vector<uint8_t>({0x3f, 0xff, 0x12, 0x35})); } // Generic callbacks. @@ -128,31 +178,25 @@ class BluetoothAudioSinkChromeOSTest : public testing::Test { protected: int callback_count_; int error_callback_count_; + base::MessageLoop message_loop_; + scoped_refptr<BluetoothAdapter> adapter_; scoped_refptr<BluetoothAudioSink> audio_sink_; + + // The default property set used while calling SetConfiguration on a media + // endpoint object. + BluetoothMediaEndpointServiceProvider::Delegate::TransportProperties + properties_; }; TEST_F(BluetoothAudioSinkChromeOSTest, RegisterSucceeded) { - // Sets up valid codec and capabilities. - BluetoothAudioSink::Options options; - ASSERT_EQ(options.codec, 0x00); - ASSERT_EQ(options.capabilities, - std::vector<uint8_t>({0x3f, 0xff, 0x12, 0x35})); - adapter_->RegisterAudioSink( - options, - base::Bind(&BluetoothAudioSinkChromeOSTest::RegisterCallback, - base::Unretained(this)), - base::Bind(&BluetoothAudioSinkChromeOSTest::RegisterErrorCallback, - base::Unretained(this))); + GetAudioSink(); - // Adds observer for the audio sink. + // Adds an observer for |audio_sink_|. TestAudioSinkObserver observer(audio_sink_); - - ASSERT_EQ(callback_count_, 1); - ASSERT_EQ(error_callback_count_, 0); - ASSERT_EQ(observer.state_changed_count_, 0); - ASSERT_EQ(observer.volume_changed_count_, 0); + EXPECT_EQ(observer.state_changed_count_, 0); + EXPECT_EQ(observer.volume_changed_count_, 0); } TEST_F(BluetoothAudioSinkChromeOSTest, RegisterFailedWithInvalidOptions) { @@ -168,8 +212,8 @@ TEST_F(BluetoothAudioSinkChromeOSTest, RegisterFailedWithInvalidOptions) { base::Bind(&BluetoothAudioSinkChromeOSTest::RegisterErrorCallback, base::Unretained(this))); - ASSERT_EQ(callback_count_, 0); - ASSERT_EQ(error_callback_count_, 1); + EXPECT_EQ(callback_count_, 0); + EXPECT_EQ(error_callback_count_, 1); // Sets options with a valid codec and invalid capabilities. options.codec = 0x00; @@ -181,8 +225,167 @@ TEST_F(BluetoothAudioSinkChromeOSTest, RegisterFailedWithInvalidOptions) { base::Bind(&BluetoothAudioSinkChromeOSTest::RegisterErrorCallback, base::Unretained(this))); - ASSERT_EQ(callback_count_, 0); - ASSERT_EQ(error_callback_count_, 2); + EXPECT_EQ(callback_count_, 0); + EXPECT_EQ(error_callback_count_, 2); +} + +TEST_F(BluetoothAudioSinkChromeOSTest, SelectConfiguration) { + GetAudioSink(); + + // Adds an observer for |audio_sink_|. + TestAudioSinkObserver observer(audio_sink_); + EXPECT_EQ(observer.state_changed_count_, 0); + EXPECT_EQ(observer.volume_changed_count_, 0); + + // Simulates calling SelectConfiguration on the media endpoint object owned by + // |audio_sink_| with some fake capabilities. + BluetoothAudioSinkChromeOS* audio_sink_chromeos = + static_cast<BluetoothAudioSinkChromeOS*>(audio_sink_.get()); + FakeBluetoothMediaEndpointServiceProvider* media_endpoint = + static_cast<FakeBluetoothMediaEndpointServiceProvider*>( + audio_sink_chromeos->GetEndpointServiceProvider()); + ASSERT_NE(media_endpoint, nullptr); + + media_endpoint->SelectConfiguration( + std::vector<uint8_t>({0x21, 0x15, 0x33, 0x2C}), + base::Bind(&BluetoothAudioSinkChromeOSTest::SelectConfigurationCallback, + base::Unretained(this))); + + EXPECT_EQ(audio_sink_->GetState(), BluetoothAudioSink::STATE_DISCONNECTED); + EXPECT_EQ(callback_count_, 2); + EXPECT_EQ(error_callback_count_, 0); + EXPECT_EQ(observer.state_changed_count_, 0); + EXPECT_EQ(observer.volume_changed_count_, 0); +} + +TEST_F(BluetoothAudioSinkChromeOSTest, SetConfiguration) { + GetAudioSink(); + + // Adds an observer for |audio_sink_|. + TestAudioSinkObserver observer(audio_sink_); + EXPECT_EQ(observer.state_changed_count_, 0); + EXPECT_EQ(observer.volume_changed_count_, 0); + + // Simulates calling SetConfiguration on the media endpoint object owned by + // |audio_sink_| with a fake transport path and a + // Delegate::TransportProperties structure. + BluetoothAudioSinkChromeOS* audio_sink_chromeos = + static_cast<BluetoothAudioSinkChromeOS*>(audio_sink_.get()); + FakeBluetoothMediaEndpointServiceProvider* media_endpoint = + static_cast<FakeBluetoothMediaEndpointServiceProvider*>( + audio_sink_chromeos->GetEndpointServiceProvider()); + ASSERT_NE(media_endpoint, nullptr); + + media_endpoint->SetConfiguration( + ObjectPath(FakeBluetoothMediaTransportClient::kTransportPath), + properties_); + + EXPECT_EQ(audio_sink_->GetState(), BluetoothAudioSink::STATE_IDLE); + EXPECT_EQ(callback_count_, 1); + EXPECT_EQ(error_callback_count_, 0); + EXPECT_EQ(observer.state_changed_count_, 1); + EXPECT_EQ(observer.volume_changed_count_, 1); +} + +TEST_F(BluetoothAudioSinkChromeOSTest, SetConfigurationWithUnexpectedState) { + GetAudioSink(); + + // Adds an observer for |audio_sink_|. + TestAudioSinkObserver observer(audio_sink_); + EXPECT_EQ(observer.state_changed_count_, 0); + EXPECT_EQ(observer.volume_changed_count_, 0); + + // Simulates calling SetConfiguration on the media endpoint object owned by + // |audio_sink_| with a fake transport path and a + // Delegate::TransportProperties structure. + BluetoothAudioSinkChromeOS* audio_sink_chromeos = + static_cast<BluetoothAudioSinkChromeOS*>(audio_sink_.get()); + FakeBluetoothMediaEndpointServiceProvider* media_endpoint = + static_cast<FakeBluetoothMediaEndpointServiceProvider*>( + audio_sink_chromeos->GetEndpointServiceProvider()); + ASSERT_NE(media_endpoint, nullptr); + + // Set state of Delegate::TransportProperties with an unexpected value. + properties_.state = "active"; + + media_endpoint->SetConfiguration( + ObjectPath(FakeBluetoothMediaTransportClient::kTransportPath), + properties_); + + EXPECT_EQ(audio_sink_->GetState(), BluetoothAudioSink::STATE_DISCONNECTED); + EXPECT_EQ(callback_count_, 1); + EXPECT_EQ(error_callback_count_, 0); + EXPECT_EQ(observer.state_changed_count_, 0); + EXPECT_EQ(observer.volume_changed_count_, 0); +} + +// Checks if the observer is notified while the media object is +// removed(invisible). Once the media object is removed, the audio sink is no +// longer valid. +TEST_F(BluetoothAudioSinkChromeOSTest, ObserverNotifiedWhenMediaRemoved) { + GetAudioSink(); + + // Adds an observer for |audio_sink_|. + TestAudioSinkObserver observer(audio_sink_); + EXPECT_EQ(observer.state_changed_count_, 0); + EXPECT_EQ(observer.volume_changed_count_, 0); + + // Gets the media object and makes it invisible to see if the state of the + // audio sink changes accordingly. + FakeBluetoothMediaClient* media = static_cast<FakeBluetoothMediaClient*>( + DBusThreadManager::Get()->GetBluetoothMediaClient()); + media->SetVisible(false); + + BluetoothAudioSinkChromeOS* audio_sink_chromeos = + static_cast<BluetoothAudioSinkChromeOS*>(audio_sink_.get()); + FakeBluetoothMediaEndpointServiceProvider* media_endpoint = + static_cast<FakeBluetoothMediaEndpointServiceProvider*>( + audio_sink_chromeos->GetEndpointServiceProvider()); + + EXPECT_EQ(audio_sink_->GetState(), BluetoothAudioSink::STATE_INVALID); + EXPECT_EQ(media_endpoint, nullptr); +} + +// Checks if the observer is notified while the media transport is +// removed(invisible). Once the media transport object is removed, the audio +// sink is disconnected. +TEST_F(BluetoothAudioSinkChromeOSTest, ObserverNotifiedWhenTransportRemoved) { + GetAudioSink(); + + // Adds an observer for |audio_sink_|. + TestAudioSinkObserver observer(audio_sink_); + EXPECT_EQ(observer.state_changed_count_, 0); + EXPECT_EQ(observer.volume_changed_count_, 0); + + // Simulates calling SetConfiguration on the media endpoint object owned by + // |audio_sink_| with a fake transport path and a + // Delegate::TransportProperties structure. + BluetoothAudioSinkChromeOS* audio_sink_chromeos = + static_cast<BluetoothAudioSinkChromeOS*>(audio_sink_.get()); + FakeBluetoothMediaEndpointServiceProvider* media_endpoint = + static_cast<FakeBluetoothMediaEndpointServiceProvider*>( + audio_sink_chromeos->GetEndpointServiceProvider()); + ASSERT_NE(media_endpoint, nullptr); + + media_endpoint->SetConfiguration( + ObjectPath(FakeBluetoothMediaTransportClient::kTransportPath), + properties_); + + EXPECT_EQ(audio_sink_->GetState(), BluetoothAudioSink::STATE_IDLE); + EXPECT_EQ(callback_count_, 1); + EXPECT_EQ(error_callback_count_, 0); + EXPECT_EQ(observer.state_changed_count_, 1); + EXPECT_EQ(observer.volume_changed_count_, 1); + + // Gets the media transport object and makes it invalid to see if the state + // of the audio sink changes accordingly. + FakeBluetoothMediaTransportClient* transport = + static_cast<FakeBluetoothMediaTransportClient*>( + DBusThreadManager::Get()->GetBluetoothMediaTransportClient()); + + transport->SetValid(media_endpoint->object_path(), false); + EXPECT_EQ(audio_sink_->GetState(), BluetoothAudioSink::STATE_DISCONNECTED); + EXPECT_NE(media_endpoint, nullptr); } } // namespace chromeos |