1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/media/android/media_session.h"
7 #include "base/android/jni_android.h"
8 #include "content/browser/media/android/media_session_observer.h"
9 #include "content/browser/web_contents/web_contents_impl.h"
10 #include "content/public/browser/web_contents.h"
11 #include "content/public/browser/web_contents_delegate.h"
12 #include "jni/MediaSession_jni.h"
16 DEFINE_WEB_CONTENTS_USER_DATA_KEY(MediaSession
);
18 MediaSession::PlayerIdentifier::PlayerIdentifier(MediaSessionObserver
* observer
,
21 player_id(player_id
) {
24 bool MediaSession::PlayerIdentifier::operator==(
25 const PlayerIdentifier
& other
) const {
26 return this->observer
== other
.observer
&& this->player_id
== other
.player_id
;
29 size_t MediaSession::PlayerIdentifier::Hash::operator()(
30 const PlayerIdentifier
& player_identifier
) const {
31 size_t hash
= BASE_HASH_NAMESPACE::hash
<MediaSessionObserver
*>()(
32 player_identifier
.observer
);
33 hash
+= BASE_HASH_NAMESPACE::hash
<int>()(player_identifier
.player_id
);
38 bool content::MediaSession::RegisterMediaSession(JNIEnv
* env
) {
39 return RegisterNativesImpl(env
);
43 MediaSession
* MediaSession::Get(WebContents
* web_contents
) {
44 MediaSession
* session
= FromWebContents(web_contents
);
46 CreateForWebContents(web_contents
);
47 session
= FromWebContents(web_contents
);
48 session
->Initialize();
53 MediaSession::~MediaSession() {
54 DCHECK(players_
.empty());
55 DCHECK(audio_focus_state_
== State::INACTIVE
);
58 bool MediaSession::AddPlayer(MediaSessionObserver
* observer
,
61 // If the audio focus is already granted and is of type Content, there is
62 // nothing to do. If it is granted of type Transient the requested type is
63 // also transient, there is also nothing to do. Otherwise, the session needs
64 // to request audio focus again.
65 if (audio_focus_state_
== State::ACTIVE
&&
66 (audio_focus_type_
== Type::Content
|| audio_focus_type_
== type
)) {
67 players_
.insert(PlayerIdentifier(observer
, player_id
));
71 State old_audio_focus_state
= audio_focus_state_
;
72 audio_focus_state_
= RequestSystemAudioFocus(type
) ? State::ACTIVE
74 audio_focus_type_
= type
;
76 if (audio_focus_state_
!= State::ACTIVE
)
79 // The session should be reset if a player is starting while all players are
81 if (old_audio_focus_state
!= State::ACTIVE
)
84 players_
.insert(PlayerIdentifier(observer
, player_id
));
90 void MediaSession::RemovePlayer(MediaSessionObserver
* observer
,
92 auto it
= players_
.find(PlayerIdentifier(observer
, player_id
));
93 if (it
!= players_
.end())
96 AbandonSystemAudioFocusIfNeeded();
99 void MediaSession::RemovePlayers(MediaSessionObserver
* observer
) {
100 for (auto it
= players_
.begin(); it
!= players_
.end();) {
101 if (it
->observer
== observer
)
102 players_
.erase(it
++);
107 AbandonSystemAudioFocusIfNeeded();
110 void MediaSession::OnSuspend(JNIEnv
* env
, jobject obj
, jboolean temporary
) {
111 if (audio_focus_state_
!= State::ACTIVE
)
114 OnSuspendInternal(SuspendType::SYSTEM
);
116 audio_focus_state_
= State::INACTIVE
;
120 void MediaSession::OnResume(JNIEnv
* env
, jobject obj
) {
121 if (audio_focus_state_
!= State::SUSPENDED
)
124 OnResumeInternal(SuspendType::SYSTEM
);
128 void MediaSession::Resume() {
129 DCHECK(IsSuspended());
131 // Request audio focus again in case we lost it because another app started
132 // playing while the playback was paused.
133 audio_focus_state_
= RequestSystemAudioFocus(audio_focus_type_
)
136 if (audio_focus_state_
!= State::ACTIVE
)
139 OnResumeInternal(SuspendType::UI
);
142 void MediaSession::Suspend() {
143 DCHECK(!IsSuspended());
145 OnSuspendInternal(SuspendType::UI
);
148 bool MediaSession::IsSuspended() const {
149 // TODO(mlamouri): should be == State::SUSPENDED.
150 return audio_focus_state_
!= State::ACTIVE
;
153 bool MediaSession::IsControllable() const {
154 // Only content type media session can be controllable unless it is inactive.
155 return audio_focus_state_
!= State::INACTIVE
&&
156 audio_focus_type_
== Type::Content
;
159 void MediaSession::ResetJavaRefForTest() {
160 j_media_session_
.Reset();
163 bool MediaSession::IsActiveForTest() const {
164 return audio_focus_state_
== State::ACTIVE
;
167 MediaSession::Type
MediaSession::audio_focus_type_for_test() const {
168 return audio_focus_type_
;
171 void MediaSession::RemoveAllPlayersForTest() {
173 AbandonSystemAudioFocusIfNeeded();
176 void MediaSession::OnSuspendInternal(SuspendType type
) {
177 audio_focus_state_
= State::SUSPENDED
;
178 suspend_type_
= type
;
180 for (const auto& it
: players_
)
181 it
.observer
->OnSuspend(it
.player_id
);
184 void MediaSession::OnResumeInternal(SuspendType type
) {
185 if (suspend_type_
!= type
&& type
!= SuspendType::UI
)
188 audio_focus_state_
= State::ACTIVE
;
190 for (const auto& it
: players_
)
191 it
.observer
->OnResume(it
.player_id
);
194 MediaSession::MediaSession(WebContents
* web_contents
)
195 : WebContentsObserver(web_contents
),
196 audio_focus_state_(State::INACTIVE
),
197 audio_focus_type_(Type::Transient
) {}
199 void MediaSession::Initialize() {
200 JNIEnv
* env
= base::android::AttachCurrentThread();
202 j_media_session_
.Reset(Java_MediaSession_createMediaSession(
204 base::android::GetApplicationContext(),
205 reinterpret_cast<intptr_t>(this)));
208 bool MediaSession::RequestSystemAudioFocus(Type type
) {
209 // During tests, j_media_session_ might be null.
210 if (j_media_session_
.is_null())
213 JNIEnv
* env
= base::android::AttachCurrentThread();
215 return Java_MediaSession_requestAudioFocus(env
, j_media_session_
.obj(),
216 type
== Type::Transient
);
219 void MediaSession::AbandonSystemAudioFocusIfNeeded() {
220 if (audio_focus_state_
== State::INACTIVE
|| !players_
.empty())
223 // During tests, j_media_session_ might be null.
224 if (!j_media_session_
.is_null()) {
225 JNIEnv
* env
= base::android::AttachCurrentThread();
227 Java_MediaSession_abandonAudioFocus(env
, j_media_session_
.obj());
230 audio_focus_state_
= State::INACTIVE
;
234 void MediaSession::UpdateWebContents() {
235 static_cast<WebContentsImpl
*>(web_contents())->OnMediaSessionStateChanged();
238 } // namespace content