blob: c979543673ad638af0cc995bd2621eb0f668e2b2 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/media_session/audio_focus_manager.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/power_monitor_test_base.h"
#include "base/test/scoped_task_environment.h"
#include "build/build_config.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "services/media_session/audio_focus_manager_metrics_helper.h"
#include "services/media_session/media_session_service.h"
#include "services/media_session/public/cpp/test/audio_focus_test_util.h"
#include "services/media_session/public/cpp/test/mock_media_session.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
#include "services/media_session/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/test/test_connector_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media_session {
namespace {
const char kExampleSourceName[] = "test";
const char kExampleSourceName2[] = "test2";
} // anonymous namespace
// This tests the Audio Focus Manager API. The parameter determines whether
// audio focus is enabled or not. If it is not enabled it should track the media
// sessions but not enforce single session focus.
class AudioFocusManagerTest
: public testing::TestWithParam<mojom::EnforcementMode> {
public:
AudioFocusManagerTest() = default;
void SetUp() override {
auto power_source = std::make_unique<base::PowerMonitorTestSource>();
power_source_ = power_source.get();
base::PowerMonitor::Initialize(std::move(power_source));
// Create an instance of the MediaSessionService.
service_ = std::make_unique<MediaSessionService>(
connector_factory_.RegisterInstance(mojom::kServiceName));
connector_factory_.GetDefaultConnector()->BindInterface(mojom::kServiceName,
&audio_focus_ptr_);
connector_factory_.GetDefaultConnector()->BindInterface(
mojom::kServiceName, &audio_focus_debug_ptr_);
connector_factory_.GetDefaultConnector()->BindInterface(
mojom::kServiceName, &controller_manager_ptr_);
audio_focus_ptr_->SetEnforcementMode(GetParam());
audio_focus_ptr_.FlushForTesting();
}
void TearDown() override {
// Run pending tasks.
base::RunLoop().RunUntilIdle();
service_.reset();
base::PowerMonitor::ShutdownForTesting();
}
AudioFocusManager::RequestId GetAudioFocusedSession() {
const auto audio_focus_requests = GetRequests();
for (auto iter = audio_focus_requests.rbegin();
iter != audio_focus_requests.rend(); ++iter) {
if ((*iter)->audio_focus_type == mojom::AudioFocusType::kGain)
return (*iter)->request_id.value();
}
return base::UnguessableToken::Null();
}
int GetTransientCount() {
return GetCountForType(mojom::AudioFocusType::kGainTransient);
}
int GetTransientMaybeDuckCount() {
return GetCountForType(mojom::AudioFocusType::kGainTransientMayDuck);
}
int GetAmbientCount() {
return GetCountForType(mojom::AudioFocusType::kAmbient);
}
void AbandonAudioFocusNoReset(test::MockMediaSession* session) {
session->audio_focus_request()->AbandonAudioFocus();
session->FlushForTesting();
audio_focus_ptr_.FlushForTesting();
}
AudioFocusManager::RequestId RequestAudioFocus(
test::MockMediaSession* session,
mojom::AudioFocusType audio_focus_type) {
return session->RequestAudioFocusFromService(audio_focus_ptr_,
audio_focus_type);
}
AudioFocusManager::RequestId RequestGroupedAudioFocus(
test::MockMediaSession* session,
mojom::AudioFocusType audio_focus_type,
const base::UnguessableToken& group_id) {
return session->RequestGroupedAudioFocusFromService(
audio_focus_ptr_, audio_focus_type, group_id);
}
mojom::MediaSessionDebugInfoPtr GetDebugInfo(
AudioFocusManager::RequestId request_id) {
mojom::MediaSessionDebugInfoPtr result;
base::OnceCallback<void(mojom::MediaSessionDebugInfoPtr)> callback =
base::BindOnce(
[](mojom::MediaSessionDebugInfoPtr* out_result,
mojom::MediaSessionDebugInfoPtr result) {
*out_result = std::move(result);
},
&result);
GetDebugService()->GetDebugInfoForRequest(request_id, std::move(callback));
audio_focus_ptr_.FlushForTesting();
audio_focus_debug_ptr_.FlushForTesting();
return result;
}
mojom::MediaSessionInfo::SessionState GetState(
test::MockMediaSession* session) {
mojom::MediaSessionInfo::SessionState state = session->GetState();
if (!IsEnforcementEnabled()) {
// If audio focus enforcement is disabled then we should never see ducking
// in the tests.
EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking, state);
}
return state;
}
std::unique_ptr<test::TestAudioFocusObserver> CreateObserver() {
std::unique_ptr<test::TestAudioFocusObserver> observer =
std::make_unique<test::TestAudioFocusObserver>();
GetService()->AddObserver(observer->BindNewPipeAndPassRemote());
audio_focus_ptr_.FlushForTesting();
return observer;
}
mojom::MediaSessionInfo::SessionState GetStateFromParam(
mojom::MediaSessionInfo::SessionState state) {
// If enforcement is enabled then returns the provided state, otherwise
// returns kActive because without enforcement we did not change state.
if (IsEnforcementEnabled())
return state;
return mojom::MediaSessionInfo::SessionState::kActive;
}
void SetSourceName(const std::string& name) {
GetService()->SetSourceName(name);
audio_focus_ptr_.FlushForTesting();
}
mojom::AudioFocusManagerPtr CreateAudioFocusManagerPtr() {
mojom::AudioFocusManagerPtr ptr;
connector_factory_.GetDefaultConnector()->BindInterface(
mojom::kServiceName, mojo::MakeRequest(&ptr));
return ptr;
}
const std::string GetSourceNameForLastRequest() {
std::vector<mojom::AudioFocusRequestStatePtr> requests = GetRequests();
EXPECT_TRUE(requests.back());
return requests.back()->source_name.value();
}
std::unique_ptr<base::HistogramSamples> GetHistogramSamplesSinceTestStart(
const std::string& name) {
return histogram_tester_.GetHistogramSamplesSinceCreation(name);
}
int GetAudioFocusHistogramCount() {
return histogram_tester_
.GetTotalCountsForPrefix("Media.Session.AudioFocus.")
.size();
}
bool IsEnforcementEnabled() const {
#if defined(OS_CHROMEOS)
// Enforcement is enabled by default on Chrome OS.
if (GetParam() == mojom::EnforcementMode::kDefault)
return true;
#endif
return GetParam() == mojom::EnforcementMode::kSingleSession ||
GetParam() == mojom::EnforcementMode::kSingleGroup;
}
bool IsGroupingEnabled() const {
return GetParam() != mojom::EnforcementMode::kSingleSession;
}
base::PowerMonitorTestSource& GetTestPowerSource() { return *power_source_; }
mojom::MediaControllerManagerPtr& controller_manager() {
return controller_manager_ptr_;
}
private:
int GetCountForType(mojom::AudioFocusType type) {
const auto audio_focus_requests = GetRequests();
return std::count_if(audio_focus_requests.begin(),
audio_focus_requests.end(),
[type](const auto& session) {
return session->audio_focus_type == type;
});
}
std::vector<mojom::AudioFocusRequestStatePtr> GetRequests() {
std::vector<mojom::AudioFocusRequestStatePtr> result;
GetService()->GetFocusRequests(base::BindOnce(
[](std::vector<mojom::AudioFocusRequestStatePtr>* out,
std::vector<mojom::AudioFocusRequestStatePtr> requests) {
for (auto& request : requests)
out->push_back(request.Clone());
},
&result));
audio_focus_ptr_.FlushForTesting();
return result;
}
mojom::AudioFocusManager* GetService() const {
return audio_focus_ptr_.get();
}
mojom::AudioFocusManagerDebug* GetDebugService() const {
return audio_focus_debug_ptr_.get();
}
void FlushForTestingIfEnabled() {
if (!IsEnforcementEnabled())
return;
audio_focus_ptr_.FlushForTesting();
}
base::test::ScopedTaskEnvironment task_environment_;
base::HistogramTester histogram_tester_;
service_manager::TestConnectorFactory connector_factory_;
std::unique_ptr<MediaSessionService> service_;
mojom::AudioFocusManagerPtr audio_focus_ptr_;
mojom::AudioFocusManagerDebugPtr audio_focus_debug_ptr_;
mojom::MediaControllerManagerPtr controller_manager_ptr_;
base::PowerMonitorTestSource* power_source_;
DISALLOW_COPY_AND_ASSIGN(AudioFocusManagerTest);
};
INSTANTIATE_TEST_SUITE_P(
,
AudioFocusManagerTest,
testing::Values(mojom::EnforcementMode::kDefault,
mojom::EnforcementMode::kNone,
mojom::EnforcementMode::kSingleGroup,
mojom::EnforcementMode::kSingleSession));
TEST_P(AudioFocusManagerTest, RequestAudioFocusGain_ReplaceFocusedEntry) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
test::MockMediaSession media_session_3;
EXPECT_EQ(base::UnguessableToken::Null(), GetAudioFocusedSession());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kInactive,
GetState(&media_session_1));
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kInactive,
GetState(&media_session_2));
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kInactive,
GetState(&media_session_3));
AudioFocusManager::RequestId request_id_1 =
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id_1, GetAudioFocusedSession());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
AudioFocusManager::RequestId request_id_2 =
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id_2, GetAudioFocusedSession());
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
AudioFocusManager::RequestId request_id_3 =
RequestAudioFocus(&media_session_3, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id_3, GetAudioFocusedSession());
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_2));
}
TEST_P(AudioFocusManagerTest, RequestAudioFocusGain_Duplicate) {
test::MockMediaSession media_session;
EXPECT_EQ(base::UnguessableToken::Null(), GetAudioFocusedSession());
AudioFocusManager::RequestId request_id =
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id, GetAudioFocusedSession());
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id, GetAudioFocusedSession());
}
TEST_P(AudioFocusManagerTest, RequestAudioFocusGain_FromTransient) {
test::MockMediaSession media_session;
AudioFocusManager::RequestId request_id =
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(base::UnguessableToken::Null(), GetAudioFocusedSession());
EXPECT_EQ(1, GetTransientCount());
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id, GetAudioFocusedSession());
EXPECT_EQ(0, GetTransientCount());
}
TEST_P(AudioFocusManagerTest, RequestAudioFocusGain_FromTransientMayDuck) {
test::MockMediaSession media_session;
AudioFocusManager::RequestId request_id = RequestAudioFocus(
&media_session, mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(base::UnguessableToken::Null(), GetAudioFocusedSession());
EXPECT_EQ(1, GetTransientMaybeDuckCount());
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id, GetAudioFocusedSession());
EXPECT_EQ(0, GetTransientMaybeDuckCount());
}
TEST_P(AudioFocusManagerTest, RequestAudioFocusTransient_FromGain) {
test::MockMediaSession media_session;
AudioFocusManager::RequestId request_id =
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id, GetAudioFocusedSession());
EXPECT_EQ(0, GetTransientCount());
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(base::UnguessableToken::Null(), GetAudioFocusedSession());
EXPECT_EQ(1, GetTransientCount());
EXPECT_NE(mojom::MediaSessionInfo::SessionState::kSuspended,
GetState(&media_session));
}
TEST_P(AudioFocusManagerTest, RequestAudioFocusTransientMayDuck_FromGain) {
test::MockMediaSession media_session;
AudioFocusManager::RequestId request_id =
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id, GetAudioFocusedSession());
EXPECT_EQ(0, GetTransientMaybeDuckCount());
RequestAudioFocus(&media_session,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(base::UnguessableToken::Null(), GetAudioFocusedSession());
EXPECT_EQ(1, GetTransientMaybeDuckCount());
EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking,
GetState(&media_session));
}
TEST_P(AudioFocusManagerTest, RequestAudioFocusTransient_FromGainWhileDucking) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(0, GetTransientMaybeDuckCount());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(0, GetTransientCount());
EXPECT_EQ(1, GetTransientMaybeDuckCount());
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(1, GetTransientCount());
EXPECT_EQ(1, GetTransientMaybeDuckCount());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest,
RequestAudioFocusTransientMayDuck_FromGainWhileDucking) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(0, GetTransientMaybeDuckCount());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(1, GetTransientMaybeDuckCount());
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
RequestAudioFocus(&media_session_1,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(2, GetTransientMaybeDuckCount());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, AbandonAudioFocus_RemovesFocusedEntry) {
test::MockMediaSession media_session;
AudioFocusManager::RequestId request_id =
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id, GetAudioFocusedSession());
media_session.AbandonAudioFocusFromClient();
EXPECT_EQ(base::UnguessableToken::Null(), GetAudioFocusedSession());
}
TEST_P(AudioFocusManagerTest, AbandonAudioFocus_MultipleCalls) {
test::MockMediaSession media_session;
AudioFocusManager::RequestId request_id =
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id, GetAudioFocusedSession());
AbandonAudioFocusNoReset(&media_session);
std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver();
media_session.AbandonAudioFocusFromClient();
EXPECT_EQ(base::UnguessableToken::Null(), GetAudioFocusedSession());
EXPECT_TRUE(observer->focus_lost_session().is_null());
}
TEST_P(AudioFocusManagerTest, AbandonAudioFocus_RemovesTransientMayDuckEntry) {
test::MockMediaSession media_session;
RequestAudioFocus(&media_session,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(1, GetTransientMaybeDuckCount());
{
std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver();
media_session.AbandonAudioFocusFromClient();
EXPECT_EQ(0, GetTransientMaybeDuckCount());
EXPECT_TRUE(observer->focus_lost_session()->session_info.Equals(
test::GetMediaSessionInfoSync(&media_session)));
}
}
TEST_P(AudioFocusManagerTest, AbandonAudioFocus_RemovesTransientEntry) {
test::MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(1, GetTransientCount());
{
std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver();
media_session.AbandonAudioFocusFromClient();
EXPECT_EQ(0, GetTransientCount());
EXPECT_TRUE(observer->focus_lost_session()->session_info.Equals(
test::GetMediaSessionInfoSync(&media_session)));
}
}
TEST_P(AudioFocusManagerTest, AbandonAudioFocus_WhileDuckingThenResume) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(0, GetTransientMaybeDuckCount());
EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(1, GetTransientMaybeDuckCount());
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
media_session_1.AbandonAudioFocusFromClient();
EXPECT_EQ(1, GetTransientMaybeDuckCount());
media_session_2.AbandonAudioFocusFromClient();
EXPECT_EQ(0, GetTransientMaybeDuckCount());
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking,
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, AbandonAudioFocus_StopsDucking) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(0, GetTransientMaybeDuckCount());
EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(1, GetTransientMaybeDuckCount());
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
media_session_2.AbandonAudioFocusFromClient();
EXPECT_EQ(0, GetTransientMaybeDuckCount());
EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking,
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, AbandonAudioFocus_ResumesPlayback) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(0, GetTransientCount());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(1, GetTransientCount());
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
media_session_2.AbandonAudioFocusFromClient();
EXPECT_EQ(0, GetTransientCount());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, DuckWhilePlaying) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, GainSuspendsTransient) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGainTransient);
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_2));
}
TEST_P(AudioFocusManagerTest, GainSuspendsTransientMayDuck) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_2,
mojom::AudioFocusType::kGainTransientMayDuck);
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_2));
}
TEST_P(AudioFocusManagerTest, DuckWithMultipleTransientMayDucks) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
test::MockMediaSession media_session_3;
test::MockMediaSession media_session_4;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGainTransient);
EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking,
GetState(&media_session_2));
RequestAudioFocus(&media_session_3,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_2));
RequestAudioFocus(&media_session_4,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_2));
media_session_3.AbandonAudioFocusFromClient();
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_2));
media_session_4.AbandonAudioFocusFromClient();
EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking,
GetState(&media_session_1));
EXPECT_NE(mojom::MediaSessionInfo::SessionState::kDucking,
GetState(&media_session_2));
}
TEST_P(AudioFocusManagerTest, MediaSessionDestroyed_ReleasesFocus) {
{
test::MockMediaSession media_session;
AudioFocusManager::RequestId request_id =
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id, GetAudioFocusedSession());
}
// If the media session is destroyed without abandoning audio focus we do not
// know until we next interact with the manager.
test::MockMediaSession media_session;
RequestAudioFocus(&media_session,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(base::UnguessableToken::Null(), GetAudioFocusedSession());
}
TEST_P(AudioFocusManagerTest, MediaSessionDestroyed_ReleasesTransient) {
{
test::MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(1, GetTransientCount());
}
// If the media session is destroyed without abandoning audio focus we do not
// know until we next interact with the manager.
test::MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(0, GetTransientCount());
}
TEST_P(AudioFocusManagerTest, MediaSessionDestroyed_ReleasesTransientMayDucks) {
{
test::MockMediaSession media_session;
RequestAudioFocus(&media_session,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(1, GetTransientMaybeDuckCount());
}
// If the media session is destroyed without abandoning audio focus we do not
// know until we next interact with the manager.
test::MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(0, GetTransientMaybeDuckCount());
}
TEST_P(AudioFocusManagerTest, GainDucksForceDuck) {
test::MockMediaSession media_session_1(true /* force_duck */);
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
AudioFocusManager::RequestId request_id_2 =
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id_2, GetAudioFocusedSession());
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, ForceDuckSessionShouldAlwaysBeDuckedFromGain) {
test::MockMediaSession media_session_1(true /* force_duck */);
test::MockMediaSession media_session_2;
test::MockMediaSession media_session_3;
AudioFocusManager::RequestId request_id_1 =
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
AudioFocusManager::RequestId request_id_2 =
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGain);
AudioFocusManager::RequestId request_id_3 =
RequestAudioFocus(&media_session_3, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id_3, GetAudioFocusedSession());
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_2));
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
media_session_3.AbandonAudioFocusFromClient();
EXPECT_EQ(request_id_2, GetAudioFocusedSession());
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
media_session_2.AbandonAudioFocusFromClient();
EXPECT_EQ(request_id_1, GetAudioFocusedSession());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest,
ForceDuckSessionShouldAlwaysBeDuckedFromTransient) {
test::MockMediaSession media_session_1(true /* force_duck */);
test::MockMediaSession media_session_2;
AudioFocusManager::RequestId request_id_1 =
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
media_session_2.AbandonAudioFocusFromClient();
EXPECT_EQ(request_id_1, GetAudioFocusedSession());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, AudioFocusObserver_RequestNoop) {
test::MockMediaSession media_session;
AudioFocusManager::RequestId request_id;
{
std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver();
request_id =
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id, GetAudioFocusedSession());
EXPECT_EQ(mojom::AudioFocusType::kGain,
observer->focus_gained_session()->audio_focus_type);
}
{
std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver();
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id, GetAudioFocusedSession());
EXPECT_TRUE(observer->focus_gained_session().is_null());
}
}
TEST_P(AudioFocusManagerTest, AudioFocusObserver_TransientMayDuck) {
test::MockMediaSession media_session;
{
std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver();
RequestAudioFocus(&media_session,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(1, GetTransientMaybeDuckCount());
EXPECT_EQ(mojom::AudioFocusType::kGainTransientMayDuck,
observer->focus_gained_session()->audio_focus_type);
}
{
std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver();
media_session.AbandonAudioFocusFromClient();
EXPECT_EQ(0, GetTransientMaybeDuckCount());
EXPECT_TRUE(observer->focus_lost_session()->session_info.Equals(
test::GetMediaSessionInfoSync(&media_session)));
}
}
TEST_P(AudioFocusManagerTest, GetDebugInfo) {
test::MockMediaSession media_session;
AudioFocusManager::RequestId request_id =
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
mojom::MediaSessionDebugInfoPtr debug_info = GetDebugInfo(request_id);
EXPECT_FALSE(debug_info->name.empty());
EXPECT_FALSE(debug_info->owner.empty());
EXPECT_FALSE(debug_info->state.empty());
}
TEST_P(AudioFocusManagerTest, GetDebugInfo_BadRequestId) {
mojom::MediaSessionDebugInfoPtr debug_info =
GetDebugInfo(base::UnguessableToken::Create());
EXPECT_TRUE(debug_info->name.empty());
}
TEST_P(AudioFocusManagerTest,
RequestAudioFocusTransient_FromGainWhileSuspended) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(0, GetTransientCount());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(1, GetTransientCount());
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(2, GetTransientCount());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest,
RequestAudioFocusTransientMayDuck_FromGainWhileSuspended) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(0, GetTransientCount());
EXPECT_EQ(0, GetTransientMaybeDuckCount());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(1, GetTransientCount());
EXPECT_EQ(0, GetTransientMaybeDuckCount());
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
RequestAudioFocus(&media_session_1,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(1, GetTransientCount());
EXPECT_EQ(1, GetTransientMaybeDuckCount());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, SourceName_AssociatedWithBinding) {
SetSourceName(kExampleSourceName);
mojom::AudioFocusManagerPtr new_ptr = CreateAudioFocusManagerPtr();
new_ptr->SetSourceName(kExampleSourceName2);
new_ptr.FlushForTesting();
test::MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(kExampleSourceName, GetSourceNameForLastRequest());
}
TEST_P(AudioFocusManagerTest, SourceName_Empty) {
test::MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_TRUE(GetSourceNameForLastRequest().empty());
}
TEST_P(AudioFocusManagerTest, SourceName_Updated) {
SetSourceName(kExampleSourceName);
test::MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(kExampleSourceName, GetSourceNameForLastRequest());
SetSourceName(kExampleSourceName2);
EXPECT_EQ(kExampleSourceName, GetSourceNameForLastRequest());
}
TEST_P(AudioFocusManagerTest, RecordUmaMetrics) {
EXPECT_EQ(0, GetAudioFocusHistogramCount());
SetSourceName(kExampleSourceName);
test::MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGainTransient);
{
std::unique_ptr<base::HistogramSamples> samples(
GetHistogramSamplesSinceTestStart(
"Media.Session.AudioFocus.Request.Test"));
EXPECT_EQ(1, samples->TotalCount());
EXPECT_EQ(1, samples->GetCount(static_cast<base::HistogramBase::Sample>(
AudioFocusManagerMetricsHelper::AudioFocusRequestSource::
kInitial)));
}
{
std::unique_ptr<base::HistogramSamples> samples(
GetHistogramSamplesSinceTestStart(
"Media.Session.AudioFocus.Type.Test"));
EXPECT_EQ(1, samples->TotalCount());
EXPECT_EQ(
1,
samples->GetCount(static_cast<base::HistogramBase::Sample>(
AudioFocusManagerMetricsHelper::AudioFocusType::kGainTransient)));
}
EXPECT_EQ(2, GetAudioFocusHistogramCount());
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
{
std::unique_ptr<base::HistogramSamples> samples(
GetHistogramSamplesSinceTestStart(
"Media.Session.AudioFocus.Request.Test"));
EXPECT_EQ(2, samples->TotalCount());
EXPECT_EQ(
1,
samples->GetCount(static_cast<base::HistogramBase::Sample>(
AudioFocusManagerMetricsHelper::AudioFocusRequestSource::kUpdate)));
}
{
std::unique_ptr<base::HistogramSamples> samples(
GetHistogramSamplesSinceTestStart(
"Media.Session.AudioFocus.Type.Test"));
EXPECT_EQ(2, samples->TotalCount());
EXPECT_EQ(1, samples->GetCount(static_cast<base::HistogramBase::Sample>(
AudioFocusManagerMetricsHelper::AudioFocusType::kGain)));
}
EXPECT_EQ(2, GetAudioFocusHistogramCount());
media_session.AbandonAudioFocusFromClient();
{
std::unique_ptr<base::HistogramSamples> samples(
GetHistogramSamplesSinceTestStart(
"Media.Session.AudioFocus.Abandon.Test"));
EXPECT_EQ(1, samples->TotalCount());
EXPECT_EQ(
1, samples->GetCount(static_cast<base::HistogramBase::Sample>(
AudioFocusManagerMetricsHelper::AudioFocusAbandonSource::kAPI)));
}
EXPECT_EQ(3, GetAudioFocusHistogramCount());
}
TEST_P(AudioFocusManagerTest, RecordUmaMetrics_ConnectionError) {
SetSourceName(kExampleSourceName);
{
test::MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
}
test::MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
{
std::unique_ptr<base::HistogramSamples> samples(
GetHistogramSamplesSinceTestStart(
"Media.Session.AudioFocus.Abandon.Test"));
EXPECT_EQ(1, samples->TotalCount());
EXPECT_EQ(1, samples->GetCount(static_cast<base::HistogramBase::Sample>(
AudioFocusManagerMetricsHelper::AudioFocusAbandonSource::
kConnectionError)));
}
}
TEST_P(AudioFocusManagerTest, RecordUmaMetrics_NoSourceName) {
test::MockMediaSession media_session;
RequestAudioFocus(&media_session, mojom::AudioFocusType::kGain);
EXPECT_EQ(0, GetAudioFocusHistogramCount());
}
TEST_P(AudioFocusManagerTest,
AbandonAudioFocus_ObserverFocusGain_NoTopSession) {
test::MockMediaSession media_session;
RequestAudioFocus(&media_session,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(1, GetTransientMaybeDuckCount());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session));
{
std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver();
media_session.AbandonAudioFocusFromClient();
EXPECT_EQ(0, GetTransientMaybeDuckCount());
EXPECT_TRUE(observer->focus_lost_session()->session_info.Equals(
test::GetMediaSessionInfoSync(&media_session)));
EXPECT_TRUE(observer->focus_gained_session().is_null());
auto notifications = observer->notifications();
EXPECT_EQ(1u, notifications.size());
EXPECT_EQ(test::TestAudioFocusObserver::NotificationType::kFocusLost,
notifications[0]);
}
}
TEST_P(AudioFocusManagerTest,
AbandonAudioFocus_ObserverFocusGain_NewTopSession) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(0, GetTransientMaybeDuckCount());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(1, GetTransientMaybeDuckCount());
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
mojom::MediaSessionInfoPtr media_session_1_info =
test::GetMediaSessionInfoSync(&media_session_1);
{
std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver();
media_session_2.AbandonAudioFocusFromClient();
EXPECT_EQ(0, GetTransientMaybeDuckCount());
EXPECT_TRUE(observer->focus_lost_session()->session_info.Equals(
test::GetMediaSessionInfoSync(&media_session_2)));
EXPECT_TRUE(observer->focus_gained_session()->session_info.Equals(
media_session_1_info));
// FocusLost should always come before FocusGained so observers always know
// the current session that has focus.
auto notifications = observer->notifications();
EXPECT_EQ(2u, notifications.size());
EXPECT_EQ(test::TestAudioFocusObserver::NotificationType::kFocusLost,
notifications[0]);
EXPECT_EQ(test::TestAudioFocusObserver::NotificationType::kFocusGained,
notifications[1]);
}
}
TEST_P(AudioFocusManagerTest, AudioFocusGrouping_LayeredFocus) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
test::MockMediaSession media_session_3;
base::UnguessableToken group_id = base::UnguessableToken::Create();
RequestGroupedAudioFocus(&media_session_1, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
// When we request audio focus for media_session_3 the group will take audio
// focus and we suspend the ducking session.
RequestGroupedAudioFocus(&media_session_3,
mojom::AudioFocusType::kGainTransient, group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_3));
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_2));
EXPECT_EQ(GetStateFromParam(
IsGroupingEnabled()
? mojom::MediaSessionInfo::SessionState::kActive
: mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, AudioFocusGrouping_TransientResume) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
test::MockMediaSession media_session_3;
test::MockMediaSession media_session_4;
base::UnguessableToken group_id = base::UnguessableToken::Create();
RequestGroupedAudioFocus(&media_session_1, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGain);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
RequestGroupedAudioFocus(&media_session_3, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_3));
RequestAudioFocus(&media_session_4, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_2));
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_3));
media_session_4.AbandonAudioFocusFromClient();
// TODO(https://crbug.com/916177): This should wait on a more precise
// condition than RunLoop idling, but it's not clear exactly what that
// should be.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(IsGroupingEnabled()
? mojom::MediaSessionInfo::SessionState::kActive
: mojom::MediaSessionInfo::SessionState::kSuspended,
GetState(&media_session_1));
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_2));
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_3));
}
TEST_P(AudioFocusManagerTest, AudioFocusGrouping_DoNotSuspendSameGroup) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
base::UnguessableToken group_id = base::UnguessableToken::Create();
RequestGroupedAudioFocus(&media_session_1, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestGroupedAudioFocus(&media_session_2, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(IsGroupingEnabled()
? mojom::MediaSessionInfo::SessionState::kActive
: mojom::MediaSessionInfo::SessionState::kSuspended,
GetState(&media_session_1));
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
}
TEST_P(AudioFocusManagerTest, AudioFocusGrouping_DuckSameGroup) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
base::UnguessableToken group_id = base::UnguessableToken::Create();
RequestGroupedAudioFocus(&media_session_1, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestGroupedAudioFocus(
&media_session_2, mojom::AudioFocusType::kGainTransientMayDuck, group_id);
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, AudioFocusGrouping_TransientSameGroup) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
base::UnguessableToken group_id = base::UnguessableToken::Create();
RequestGroupedAudioFocus(&media_session_1, mojom::AudioFocusType::kGain,
group_id);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestGroupedAudioFocus(&media_session_2,
mojom::AudioFocusType::kGainTransient, group_id);
EXPECT_EQ(IsGroupingEnabled()
? mojom::MediaSessionInfo::SessionState::kActive
: mojom::MediaSessionInfo::SessionState::kSuspended,
GetState(&media_session_1));
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
}
TEST_P(AudioFocusManagerTest, RequestAudioFocus_PreferStop_LossToGain) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
media_session_1.SetPreferStop(true);
AudioFocusManager::RequestId request_id_1 =
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id_1, GetAudioFocusedSession());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
AudioFocusManager::RequestId request_id_2 =
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id_2, GetAudioFocusedSession());
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kInactive),
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest,
RequestAudioFocus_PreferStop_LossToGainTransient) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
media_session_1.SetPreferStop(true);
AudioFocusManager::RequestId request_id_1 =
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id_1, GetAudioFocusedSession());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, GainFocusTypeHasEffectEvenIfSuspended) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
test::MockMediaSession media_session_3;
AudioFocusManager::RequestId request_id_1 =
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id_1, GetAudioFocusedSession());
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGain);
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
// When the second session becomes suspended and that event originated from
// the session itself then we should keep the other session suspended.
media_session_2.Suspend(mojom::MediaSession::SuspendType::kUI);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kSuspended,
GetState(&media_session_2));
{
test::MockMediaSessionMojoObserver observer(media_session_1);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended));
}
// When the second session is resumed then we should still keep the other
// session suspended.
media_session_2.Resume(mojom::MediaSession::SuspendType::kUI);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
{
test::MockMediaSessionMojoObserver observer(media_session_1);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended));
}
// If a new session takes focus then this should suspend all sessions.
RequestAudioFocus(&media_session_3, mojom::AudioFocusType::kGainTransient);
{
test::MockMediaSessionMojoObserver observer(media_session_2);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended));
}
{
test::MockMediaSessionMojoObserver observer(media_session_1);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended));
}
// If the second session regains focus then it should suspend all sessions.
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGain);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
{
test::MockMediaSessionMojoObserver observer(media_session_1);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended));
}
{
test::MockMediaSessionMojoObserver observer(media_session_3);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended));
}
}
TEST_P(AudioFocusManagerTest, TransientFocusTypeHasNoEffectIfSuspended) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
test::MockMediaSession media_session_3;
AudioFocusManager::RequestId request_id_1 =
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id_1, GetAudioFocusedSession());
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
// When the transient session becomes suspended and that event originates from
// the session itself then we should stop pausing the other session.
media_session_2.Suspend(mojom::MediaSession::SuspendType::kUI);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kSuspended,
GetState(&media_session_2));
{
test::MockMediaSessionMojoObserver observer(media_session_1);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
// When the transient session is resumed then we should pause the other
// session again.
media_session_2.Resume(mojom::MediaSession::SuspendType::kUI);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
{
test::MockMediaSessionMojoObserver observer(media_session_1);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended));
}
// If we have a new session take focus then this should suspend all the other
// sessions and the transient session should have no effect.
RequestAudioFocus(&media_session_3, mojom::AudioFocusType::kGainTransient);
{
test::MockMediaSessionMojoObserver observer(media_session_2);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended));
}
{
test::MockMediaSessionMojoObserver observer(media_session_1);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended));
}
// If the second session regains focus then it should start pausing again.
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
{
test::MockMediaSessionMojoObserver observer(media_session_1);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended));
}
{
test::MockMediaSessionMojoObserver observer(media_session_3);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended));
}
}
TEST_P(AudioFocusManagerTest, TransientDuckFocusTypeHasNoEffectIfSuspended) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
test::MockMediaSession media_session_3;
AudioFocusManager::RequestId request_id_1 =
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id_1, GetAudioFocusedSession());
RequestAudioFocus(&media_session_2,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
// When the ducking session becomes suspended and that event originates from
// the session itself then we should stop ducking.
media_session_2.Suspend(mojom::MediaSession::SuspendType::kUI);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kSuspended,
GetState(&media_session_2));
{
test::MockMediaSessionMojoObserver observer(media_session_1);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
// When the ducking session is resumed then we should resume ducking.
media_session_2.Resume(mojom::MediaSession::SuspendType::kUI);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
{
test::MockMediaSessionMojoObserver observer(media_session_1);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking));
}
// If we have a new session take focus then this should suspend all the other
// sessions and we should not have any ducking from the ducking session (since
// it is suspended).
RequestAudioFocus(&media_session_3, mojom::AudioFocusType::kGainTransient);
{
test::MockMediaSessionMojoObserver observer(media_session_2);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended));
}
{
test::MockMediaSessionMojoObserver observer(media_session_1);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended));
}
// If the ducking session regains focus then it should start ducking again.
RequestAudioFocus(&media_session_2,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
{
test::MockMediaSessionMojoObserver observer(media_session_1);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking));
}
{
test::MockMediaSessionMojoObserver observer(media_session_3);
observer.WaitForState(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking));
}
}
TEST_P(AudioFocusManagerTest, AmbientFocusHasNoEffect) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
AudioFocusManager::RequestId request_id_1 =
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id_1, GetAudioFocusedSession());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
EXPECT_EQ(0, GetAmbientCount());
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kAmbient);
EXPECT_EQ(1, GetAmbientCount());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
media_session_2.AbandonAudioFocusFromClient();
EXPECT_EQ(0, GetAmbientCount());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, AudioFocusObserver_NotTopMost) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
AudioFocusManager::RequestId request_id_1 =
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(request_id_1, GetAudioFocusedSession());
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2,
mojom::AudioFocusType::kGainTransientMayDuck);
EXPECT_EQ(GetStateFromParam(mojom::MediaSessionInfo::SessionState::kDucking),
GetState(&media_session_1));
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_2));
{
std::unique_ptr<test::TestAudioFocusObserver> observer = CreateObserver();
media_session_1.AbandonAudioFocusFromClient();
EXPECT_TRUE(observer->focus_lost_session()->session_info.Equals(
test::GetMediaSessionInfoSync(&media_session_1)));
}
}
TEST_P(AudioFocusManagerTest, SuspendAllSessionOnPowerSuspend) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
{
test::MockMediaSessionMojoObserver observer(media_session_1);
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
{
test::MockMediaSessionMojoObserver observer(media_session_2);
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGain);
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
test::MockMediaSessionMojoObserver observer_1(media_session_1);
test::MockMediaSessionMojoObserver observer_2(media_session_2);
GetTestPowerSource().GenerateSuspendEvent();
observer_1.WaitForState(mojom::MediaSessionInfo::SessionState::kSuspended);
observer_2.WaitForState(mojom::MediaSessionInfo::SessionState::kSuspended);
}
TEST_P(AudioFocusManagerTest, TransientPauseShouldDelayControllerPause) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
mojom::MediaControllerPtr controller;
controller_manager()->CreateMediaControllerForSession(
mojo::MakeRequest(&controller), media_session_1.request_id());
controller_manager().FlushForTesting();
controller->Suspend();
controller.FlushForTesting();
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kSuspended,
GetState(&media_session_1));
// When we abandon the transient session then we will apply the last
// controller action.
media_session_2.AbandonAudioFocusFromClient();
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kSuspended,
GetState(&media_session_1));
}
TEST_P(AudioFocusManagerTest, TransientPauseShouldDelayControllerStop) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
mojom::MediaControllerPtr controller;
controller_manager()->CreateMediaControllerForSession(
mojo::MakeRequest(&controller), media_session_1.request_id());
controller_manager().FlushForTesting();
controller->Stop();
controller.FlushForTesting();
// If enforcement is enabled then the session was previously suspended by the
// transient session and therefore we should be suspended. Otherwise, we
// should be inactive because the stop command would not have been delayed.
EXPECT_EQ(IsEnforcementEnabled()
? mojom::MediaSessionInfo::SessionState::kSuspended
: mojom::MediaSessionInfo::SessionState::kInactive,
GetState(&media_session_1));
{
// When we abandon the transient session then we will apply the last
// controller action.
test::MockMediaSessionMojoObserver observer(media_session_1);
media_session_2.AbandonAudioFocusFromClient();
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kInactive);
}
}
TEST_P(AudioFocusManagerTest, TransientPauseShouldDelayControllerResume) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
mojom::MediaControllerPtr controller;
controller_manager()->CreateMediaControllerForSession(
mojo::MakeRequest(&controller), media_session_1.request_id());
controller_manager().FlushForTesting();
controller->Suspend();
controller.FlushForTesting();
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kSuspended,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kSuspended,
GetState(&media_session_1));
controller->Resume();
controller.FlushForTesting();
// If enforcement is enabled then the session was previously suspended by the
// transient session and therefore we should be suspended. Otherwise, we
// should be active because the resume command would not have been delayed.
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
{
// When we abandon the transient session then we will apply the last
// controller action.
test::MockMediaSessionMojoObserver observer(media_session_1);
media_session_2.AbandonAudioFocusFromClient();
observer.WaitForState(mojom::MediaSessionInfo::SessionState::kActive);
}
}
TEST_P(AudioFocusManagerTest, TransientPauseShouldDelayLastActionOnly) {
test::MockMediaSession media_session_1;
test::MockMediaSession media_session_2;
RequestAudioFocus(&media_session_1, mojom::AudioFocusType::kGain);
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kActive,
GetState(&media_session_1));
RequestAudioFocus(&media_session_2, mojom::AudioFocusType::kGainTransient);
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
mojom::MediaControllerPtr controller;
controller_manager()->CreateMediaControllerForSession(
mojo::MakeRequest(&controller), media_session_1.request_id());
controller_manager().FlushForTesting();
controller->Resume();
controller.FlushForTesting();
// The resume action should be delayed because we were suspended by the
// transient session.
EXPECT_EQ(
GetStateFromParam(mojom::MediaSessionInfo::SessionState::kSuspended),
GetState(&media_session_1));
// Calling suspend while we are still suspended should cancel the delayed
// resume action so we will never resume.
controller->Suspend();
controller.FlushForTesting();
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kSuspended,
GetState(&media_session_1));
// When we abandon the transient session then we will apply the last
// controller action.
media_session_2.AbandonAudioFocusFromClient();
EXPECT_EQ(mojom::MediaSessionInfo::SessionState::kSuspended,
GetState(&media_session_1));
}
} // namespace media_session