Commit acef01cb authored by Olga Sharonova's avatar Olga Sharonova Committed by Commit Bot

media::AudioSystem: disconnect from Audio service when not in use.

Also added mojo::InterfacePtr<>::IsExpectingResponse().

Bug: 672469, 792441
Change-Id: I9c508cafae84b393e226834c4236724d59641829
Reviewed-on: https://chromium-review.googlesource.com/998166
Commit-Queue: Olga Sharonova <olka@chromium.org>
Reviewed-by: default avatarMax Morin <maxmorin@chromium.org>
Reviewed-by: default avatarKen Rockot <rockot@chromium.org>
Cr-Commit-Position: refs/heads/master@{#549254}
parent d422b5d9
......@@ -145,6 +145,9 @@ class InterfacePtr {
return internal_state_.HasAssociatedInterfaces();
}
// Returns true if bound and awaiting a response to a message.
bool IsExpectingResponse() { return internal_state_.has_pending_callbacks(); }
// Indicates whether the message pipe has encountered an error. If true,
// method calls made on this interface will be dropped (and may already have
// been dropped).
......
......@@ -13,12 +13,22 @@
namespace audio {
AudioSystemToServiceAdapter::AudioSystemToServiceAdapter(
std::unique_ptr<service_manager::Connector> connector)
std::unique_ptr<service_manager::Connector> connector,
base::TimeDelta disconnect_timeout)
: connector_(std::move(connector)) {
DCHECK(connector_);
DETACH_FROM_THREAD(thread_checker_);
if (disconnect_timeout > base::TimeDelta()) {
disconnect_timer_.emplace(
FROM_HERE, disconnect_timeout, this,
&AudioSystemToServiceAdapter::DisconnectOnTimeout);
}
}
AudioSystemToServiceAdapter::AudioSystemToServiceAdapter(
std::unique_ptr<service_manager::Connector> connector)
: AudioSystemToServiceAdapter(std::move(connector), base::TimeDelta()) {}
AudioSystemToServiceAdapter::~AudioSystemToServiceAdapter() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
......@@ -91,9 +101,22 @@ mojom::SystemInfo* AudioSystemToServiceAdapter::GetSystemInfo() {
base::Unretained(this)));
DCHECK(system_info_);
}
if (disconnect_timer_)
disconnect_timer_->Reset();
return system_info_.get();
}
void AudioSystemToServiceAdapter::DisconnectOnTimeout() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DVLOG(4) << "AudioSystemToServiceAdapter::Disconnect";
if (system_info_.IsExpectingResponse()) {
if (disconnect_timer_)
disconnect_timer_->Reset();
return;
}
system_info_.reset();
}
void AudioSystemToServiceAdapter::OnConnectionError() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DVLOG(4) << "AudioSystemToServiceAdapter::OnConnectionError";
......
......@@ -7,7 +7,10 @@
#include <memory>
#include "base/optional.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "media/audio/audio_system.h"
#include "services/audio/public/mojom/system_info.mojom.h"
......@@ -22,9 +25,14 @@ namespace audio {
// empty optionals / false booleans.
class AudioSystemToServiceAdapter : public media::AudioSystem {
public:
// If |disconnect_timeout| is positive, the instance will disconnect from
// Audio service upon |disconnect_timeout| if not in use, and reconnect on the
// next attempt to use it.
AudioSystemToServiceAdapter(
std::unique_ptr<service_manager::Connector> connector,
base::TimeDelta disconnect_timeout);
explicit AudioSystemToServiceAdapter(
std::unique_ptr<service_manager::Connector> connector);
~AudioSystemToServiceAdapter() override;
// AudioSystem implementation.
......@@ -48,12 +56,16 @@ class AudioSystemToServiceAdapter : public media::AudioSystem {
private:
mojom::SystemInfo* GetSystemInfo();
void DisconnectOnTimeout();
void OnConnectionError();
// Will be bound to the thread AudioSystemToServiceAdapter is used on.
const std::unique_ptr<service_manager::Connector> connector_;
mojom::SystemInfoPtr system_info_;
// To disconnect from the audio service when not in use.
base::Optional<base::DelayTimer> disconnect_timer_;
THREAD_CHECKER(thread_checker_);
DISALLOW_COPY_AND_ASSIGN(AudioSystemToServiceAdapter);
};
......
......@@ -3,6 +3,8 @@
// found in the LICENSE file.
#include "services/audio/public/cpp/audio_system_to_service_adapter.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "media/audio/audio_system_test_util.h"
#include "media/audio/test_audio_thread.h"
#include "mojo/public/cpp/bindings/binding_set.h"
......@@ -11,6 +13,8 @@
#include "services/audio/system_info.h"
#include "services/service_manager/public/cpp/connector.h"
#include "services/service_manager/public/mojom/connector.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::Exactly;
......@@ -339,6 +343,171 @@ TEST_F(AudioSystemToServiceAdapterConnectionLossTest, GetInputDeviceInfo) {
EXPECT_FALSE(system_info_binding_->is_bound());
}
namespace {
static constexpr auto kResponseDelay = base::TimeDelta::FromMilliseconds(10);
static constexpr char kSomeDeviceId[] = "Some device";
static constexpr char kValidReplyId[] =
"If you can read it you received the reply";
} // namespace
class AudioSystemToServiceAdapterDisconnectTest : public testing::Test {
public:
AudioSystemToServiceAdapterDisconnectTest() {}
~AudioSystemToServiceAdapterDisconnectTest() override {}
protected:
class MockSystemInfo : public mojom::SystemInfo {
public:
MockSystemInfo(base::TimeDelta response_delay) {}
~MockSystemInfo() override {}
private:
// audio::mojom::SystemInfo implementation.
void GetInputStreamParameters(
const std::string& device_id,
GetInputStreamParametersCallback callback) override {
NOTIMPLEMENTED();
}
void GetOutputStreamParameters(
const std::string& device_id,
GetOutputStreamParametersCallback callback) override {
NOTIMPLEMENTED();
}
void HasInputDevices(HasInputDevicesCallback callback) override {
NOTIMPLEMENTED();
}
void HasOutputDevices(HasOutputDevicesCallback callback) override {
NOTIMPLEMENTED();
}
void GetInputDeviceDescriptions(
GetInputDeviceDescriptionsCallback callback) override {
NOTIMPLEMENTED();
}
void GetOutputDeviceDescriptions(
GetOutputDeviceDescriptionsCallback callback) override {
NOTIMPLEMENTED();
}
void GetAssociatedOutputDeviceID(
const std::string& input_device_id,
GetAssociatedOutputDeviceIDCallback callback) override {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::BindOnce(std::move(callback), kValidReplyId),
kResponseDelay);
}
void GetInputDeviceInfo(const std::string& input_device_id,
GetInputDeviceInfoCallback callback) override {
NOTIMPLEMENTED();
}
}; // class MockSystemInfo
std::unique_ptr<service_manager::Connector> GetConnector() {
service_manager::mojom::ConnectorRequest ignored_request;
auto connector = service_manager::Connector::Create(&ignored_request);
service_manager::Connector::TestApi connector_test_api(connector.get());
connector_test_api.OverrideBinderForTesting(
service_manager::Identity(mojom::kServiceName),
mojom::SystemInfo::Name_,
base::BindRepeating(
&AudioSystemToServiceAdapterDisconnectTest::BindSystemInfoRequest,
base::Unretained(this)));
return connector;
}
void BindSystemInfoRequest(mojo::ScopedMessagePipeHandle handle) {
ClientConnected();
system_info_binding_.Bind(mojom::SystemInfoRequest(std::move(handle)));
system_info_binding_.set_connection_error_handler(base::BindOnce(
&AudioSystemToServiceAdapterDisconnectTest::OnConnectionError,
base::Unretained(this)));
}
void OnConnectionError() {
system_info_binding_.Close();
ClientDisconnected();
}
MOCK_METHOD0(ClientConnected, void(void));
MOCK_METHOD0(ClientDisconnected, void(void));
base::test::ScopedTaskEnvironment scoped_task_environment_{
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
const base::Optional<std::string> valid_reply_{kValidReplyId};
base::MockCallback<media::AudioSystem::OnDeviceIdCallback> response_received_;
MockSystemInfo mock_system_info_{kResponseDelay};
mojo::Binding<mojom::SystemInfo> system_info_binding_{&mock_system_info_};
};
TEST_F(AudioSystemToServiceAdapterDisconnectTest,
ResponseDelayIsShorterThanDisconnectTimeout) {
const base::TimeDelta kDisconnectTimeout = kResponseDelay * 2;
AudioSystemToServiceAdapter audio_system(GetConnector(), kDisconnectTimeout);
{
EXPECT_CALL(*this, ClientConnected());
EXPECT_CALL(*this, ClientDisconnected()).Times(0);
EXPECT_CALL(response_received_, Run(valid_reply_));
audio_system.GetAssociatedOutputDeviceID(kSomeDeviceId,
response_received_.Get());
scoped_task_environment_.FastForwardBy(kResponseDelay);
}
EXPECT_CALL(*this, ClientDisconnected());
scoped_task_environment_.FastForwardBy(kDisconnectTimeout);
}
TEST_F(AudioSystemToServiceAdapterDisconnectTest,
ResponseDelayIsLongerThanDisconnectTimeout) {
const base::TimeDelta kDisconnectTimeout = kResponseDelay / 2;
AudioSystemToServiceAdapter audio_system(GetConnector(), kDisconnectTimeout);
{
EXPECT_CALL(*this, ClientConnected());
EXPECT_CALL(*this, ClientDisconnected()).Times(0);
EXPECT_CALL(response_received_, Run(valid_reply_));
audio_system.GetAssociatedOutputDeviceID(kSomeDeviceId,
response_received_.Get());
scoped_task_environment_.FastForwardBy(kResponseDelay);
}
EXPECT_CALL(*this, ClientDisconnected());
scoped_task_environment_.FastForwardBy(kDisconnectTimeout);
}
TEST_F(AudioSystemToServiceAdapterDisconnectTest,
DisconnectTimeoutIsResetOnSecondRequest) {
const base::TimeDelta kDisconnectTimeout = kResponseDelay * 1.5;
AudioSystemToServiceAdapter audio_system(GetConnector(), kDisconnectTimeout);
{
EXPECT_CALL(*this, ClientConnected());
EXPECT_CALL(*this, ClientDisconnected()).Times(0);
EXPECT_CALL(response_received_, Run(valid_reply_));
audio_system.GetAssociatedOutputDeviceID(kSomeDeviceId,
response_received_.Get());
scoped_task_environment_.FastForwardBy(kResponseDelay);
}
{
EXPECT_CALL(*this, ClientConnected()).Times(0);
EXPECT_CALL(*this, ClientDisconnected()).Times(0);
EXPECT_CALL(response_received_, Run(valid_reply_));
audio_system.GetAssociatedOutputDeviceID(kSomeDeviceId,
response_received_.Get());
scoped_task_environment_.FastForwardBy(kResponseDelay);
}
EXPECT_CALL(*this, ClientDisconnected());
scoped_task_environment_.FastForwardBy(kDisconnectTimeout);
}
TEST_F(AudioSystemToServiceAdapterDisconnectTest,
DoesNotDisconnectIfNoTimeout) {
AudioSystemToServiceAdapter audio_system(GetConnector(), base::TimeDelta());
EXPECT_CALL(*this, ClientConnected());
EXPECT_CALL(*this, ClientDisconnected()).Times(0);
EXPECT_CALL(response_received_, Run(valid_reply_));
audio_system.GetAssociatedOutputDeviceID(kSomeDeviceId,
response_received_.Get());
scoped_task_environment_.FastForwardUntilNoTasksRemain();
}
} // namespace audio
// AudioSystem interface conformance tests.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment