#include "services/media_session/audio_focus_request.h"
#include "base/task/single_thread_task_runner.h"
#include "services/media_session/audio_focus_manager.h"
namespace media_session {
AudioFocusRequest::AudioFocusRequest(
base::WeakPtr<AudioFocusManager> owner,
mojo::PendingReceiver<mojom::AudioFocusRequestClient> receiver,
mojo::PendingRemote<mojom::MediaSession> session,
mojom::MediaSessionInfoPtr session_info,
mojom::AudioFocusType audio_focus_type,
const base::UnguessableToken& id,
const std::string& source_name,
const base::UnguessableToken& group_id,
const base::UnguessableToken& identity)
: session_(std::move(session)),
session_info_(std::move(session_info)),
audio_focus_type_(audio_focus_type),
receiver_(this, std::move(receiver)),
id_(id),
source_name_(source_name),
group_id_(group_id),
identity_(identity),
owner_(std::move(owner)) {
receiver_.set_disconnect_handler(base::BindOnce(
&AudioFocusRequest::OnConnectionError, base::Unretained(this)));
session_.set_disconnect_handler(base::BindOnce(
&AudioFocusRequest::OnConnectionError, base::Unretained(this)));
}
AudioFocusRequest::~AudioFocusRequest() = default;
void AudioFocusRequest::RequestAudioFocus(
mojom::MediaSessionInfoPtr session_info,
mojom::AudioFocusType type,
RequestAudioFocusCallback callback) {
SetSessionInfo(std::move(session_info));
if (session_info_->state == mojom::MediaSessionInfo::SessionState::kActive &&
owner_->IsSessionOnTopOfAudioFocusStack(id(), type)) {
std::move(callback).Run();
return;
}
std::unique_ptr<AudioFocusRequest> row =
owner_->RemoveFocusEntryIfPresent(id());
DCHECK(row);
owner_->RequestAudioFocusInternal(std::move(row), type);
std::move(callback).Run();
}
void AudioFocusRequest::AbandonAudioFocus() {
owner_->AbandonAudioFocusInternal(id_);
}
void AudioFocusRequest::MediaSessionInfoChanged(
mojom::MediaSessionInfoPtr info) {
bool suspended_change =
(info->state == mojom::MediaSessionInfo::SessionState::kSuspended ||
IsSuspended()) &&
info->state != session_info_->state;
SetSessionInfo(std::move(info));
if (suspended_change) {
owner_->EnforceAudioFocus();
}
}
bool AudioFocusRequest::IsSuspended() const {
return session_info_->state ==
mojom::MediaSessionInfo::SessionState::kSuspended;
}
mojom::AudioFocusRequestStatePtr AudioFocusRequest::ToAudioFocusRequestState()
const {
auto request = mojom::AudioFocusRequestState::New();
request->session_info = session_info_.Clone();
request->audio_focus_type = audio_focus_type_;
request->request_id = id_;
request->source_name = source_name_;
request->source_id =
identity_.is_empty() ? std::nullopt : std::make_optional(identity_);
return request;
}
void AudioFocusRequest::BindToMediaController(
mojo::PendingReceiver<mojom::MediaController> receiver) {
if (!controller_) {
controller_ = std::make_unique<MediaController>();
controller_->SetMediaSession(this);
}
controller_->BindToInterface(std::move(receiver));
}
void AudioFocusRequest::Suspend(const EnforcementState& state) {
DCHECK(!session_info_->force_duck);
if (state.should_stop && session_info_->prefer_stop_for_gain_focus_loss) {
session_->Stop(mojom::MediaSession::SuspendType::kSystem);
} else {
was_suspended_ = was_suspended_ || state.should_suspend;
session_->Suspend(mojom::MediaSession::SuspendType::kSystem);
}
}
void AudioFocusRequest::ReleaseTransientHold() {
DCHECK(!session_info_->force_duck);
if (!was_suspended_) {
return;
}
was_suspended_ = false;
if (delayed_action_) {
PerformUIAction(*delayed_action_);
delayed_action_.reset();
return;
}
session_->Resume(mojom::MediaSession::SuspendType::kSystem);
}
void AudioFocusRequest::PerformUIAction(mojom::MediaSessionAction action) {
if (was_suspended_) {
delayed_action_ = action;
return;
}
switch (action) {
case mojom::MediaSessionAction::kPause:
session_->Suspend(mojom::MediaSession::SuspendType::kUI);
break;
case mojom::MediaSessionAction::kPlay:
session_->Resume(mojom::MediaSession::SuspendType::kUI);
break;
case mojom::MediaSessionAction::kStop:
session_->Stop(mojom::MediaSession::SuspendType::kUI);
break;
default:
NOTREACHED();
}
}
void AudioFocusRequest::GetMediaImageBitmap(
const MediaImage& image,
int minimum_size_px,
int desired_size_px,
GetMediaImageBitmapCallback callback) {
session_->GetMediaImageBitmap(
image, minimum_size_px, desired_size_px,
base::BindOnce(&AudioFocusRequest::OnImageDownloaded,
base::Unretained(this), std::move(callback)));
}
void AudioFocusRequest::FlushForTesting() {
session_.FlushForTesting();
}
void AudioFocusRequest::SetSessionInfo(
mojom::MediaSessionInfoPtr session_info) {
bool is_controllable_changed =
session_info_->is_controllable != session_info->is_controllable;
session_info_ = std::move(session_info);
if (is_controllable_changed) {
owner_->MaybeUpdateActiveSession();
}
}
void AudioFocusRequest::OnConnectionError() {
if (encountered_error_) {
return;
}
encountered_error_ = true;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&AudioFocusManager::AbandonAudioFocusInternal,
owner_, id_));
}
void AudioFocusRequest::OnImageDownloaded(GetMediaImageBitmapCallback callback,
const SkBitmap& bitmap) {
std::move(callback).Run(bitmap);
}
}