#ifndef SERVICES_MEDIA_SESSION_AUDIO_FOCUS_MANAGER_H_
#define SERVICES_MEDIA_SESSION_AUDIO_FOCUS_MANAGER_H_
#include <list>
#include <string>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "mojo/public/cpp/bindings/remote_set.h"
#include "services/media_session/media_controller.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
#include "services/media_session/public/mojom/media_controller.mojom.h"
namespace base {
class UnguessableToken;
}
namespace media_session {
namespace test {
class MockMediaSession;
}
class AudioFocusRequest;
class MediaController;
class MediaPowerDelegate;
struct EnforcementState {
bool should_duck = false;
bool should_stop = false;
bool should_suspend = false;
};
class AudioFocusManager : public mojom::AudioFocusManager,
public mojom::AudioFocusManagerDebug,
public mojom::MediaControllerManager {
public:
AudioFocusManager();
AudioFocusManager(const AudioFocusManager&) = delete;
AudioFocusManager& operator=(const AudioFocusManager&) = delete;
~AudioFocusManager() override;
using RequestId = base::UnguessableToken;
void RequestAudioFocus(
mojo::PendingReceiver<mojom::AudioFocusRequestClient> receiver,
mojo::PendingRemote<mojom::MediaSession> session,
mojom::MediaSessionInfoPtr session_info,
mojom::AudioFocusType type,
RequestAudioFocusCallback callback) override;
void RequestGroupedAudioFocus(
const base::UnguessableToken& request_id,
mojo::PendingReceiver<mojom::AudioFocusRequestClient> receiver,
mojo::PendingRemote<mojom::MediaSession> session,
mojom::MediaSessionInfoPtr session_info,
mojom::AudioFocusType type,
const base::UnguessableToken& group_id,
RequestGroupedAudioFocusCallback callback) override;
void GetFocusRequests(GetFocusRequestsCallback callback) override;
void AddObserver(
mojo::PendingRemote<mojom::AudioFocusObserver> observer) override;
void SetSource(const base::UnguessableToken& identity,
const std::string& name) override;
void SetEnforcementMode(mojom::EnforcementMode mode) override;
void AddSourceObserver(
const base::UnguessableToken& source_id,
mojo::PendingRemote<mojom::AudioFocusObserver> observer) override;
void GetSourceFocusRequests(const base::UnguessableToken& source_id,
GetFocusRequestsCallback callback) override;
void RequestIdReleased(const base::UnguessableToken& request_id) override;
void StartDuckingAllAudio(const std::optional<base::UnguessableToken>&
exempted_request_id) override;
void StopDuckingAllAudio() override;
void FlushForTesting(FlushForTestingCallback callback) override;
void GetDebugInfoForRequest(const RequestId& request_id,
GetDebugInfoForRequestCallback callback) override;
void CreateActiveMediaController(
mojo::PendingReceiver<mojom::MediaController> receiver) override;
void CreateMediaControllerForSession(
mojo::PendingReceiver<mojom::MediaController> receiver,
const base::UnguessableToken& receiver_id) override;
void SuspendAllSessions() override;
void BindToInterface(
mojo::PendingReceiver<mojom::AudioFocusManager> receiver);
void BindToDebugInterface(
mojo::PendingReceiver<mojom::AudioFocusManagerDebug> receiver);
void BindToControllerManagerInterface(
mojo::PendingReceiver<mojom::MediaControllerManager> receiver);
private:
friend class AudioFocusManagerTest;
friend class AudioFocusRequest;
friend class MediaControllerTest;
friend class test::MockMediaSession;
class SourceObserverHolder;
struct ReceiverContext {
std::string source_name;
base::UnguessableToken identity;
};
void RequestAudioFocusInternal(std::unique_ptr<AudioFocusRequest>,
mojom::AudioFocusType);
void AbandonAudioFocusInternal(RequestId);
void EnforceAudioFocus();
void MaybeUpdateActiveSession();
std::unique_ptr<AudioFocusRequest> RemoveFocusEntryIfPresent(RequestId id);
bool IsFocusEntryPresent(const base::UnguessableToken& id) const;
const std::string& GetBindingSourceName() const;
const base::UnguessableToken& GetBindingIdentity() const;
bool IsSessionOnTopOfAudioFocusStack(RequestId id,
mojom::AudioFocusType type) const;
bool ShouldSessionBeSuspended(const AudioFocusRequest* session,
const EnforcementState& state) const;
bool ShouldSessionBeDucked(const AudioFocusRequest* session,
const EnforcementState& state) const;
void EnforceSingleSession(AudioFocusRequest* session,
const EnforcementState& state);
void CleanupSourceObservers();
MediaController active_media_controller_;
mojo::ReceiverSet<mojom::AudioFocusManager, std::unique_ptr<ReceiverContext>>
receivers_;
mojo::ReceiverSet<mojom::AudioFocusManagerDebug> debug_receivers_;
mojo::ReceiverSet<mojom::MediaControllerManager> controller_receivers_;
mojo::RemoteSet<mojom::AudioFocusObserver> observers_;
std::vector<std::unique_ptr<SourceObserverHolder>> source_observers_;
std::list<std::unique_ptr<AudioFocusRequest>> audio_focus_stack_;
std::unique_ptr<MediaPowerDelegate> power_delegate_;
mojom::EnforcementMode enforcement_mode_;
bool ducking_all_audio_ = false;
std::optional<base::UnguessableToken> ducking_exempted_request_id_;
THREAD_CHECKER(thread_checker_);
base::WeakPtrFactory<AudioFocusManager> weak_ptr_factory_{this};
};
}
#endif