Process Alt-Svc headers.
[chromium-blink-merge.git] / content / browser / media / android / media_session.cc
blobb52e67a8c697127a2fa151afa85ce62bd685c36a
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"
14 namespace content {
16 DEFINE_WEB_CONTENTS_USER_DATA_KEY(MediaSession);
18 MediaSession::PlayerIdentifier::PlayerIdentifier(MediaSessionObserver* observer,
19 int player_id)
20 : observer(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);
34 return hash;
37 // static
38 bool content::MediaSession::RegisterMediaSession(JNIEnv* env) {
39 return RegisterNativesImpl(env);
42 // static
43 MediaSession* MediaSession::Get(WebContents* web_contents) {
44 MediaSession* session = FromWebContents(web_contents);
45 if (!session) {
46 CreateForWebContents(web_contents);
47 session = FromWebContents(web_contents);
48 session->Initialize();
50 return session;
53 MediaSession::~MediaSession() {
54 DCHECK(players_.empty());
55 DCHECK(audio_focus_state_ == State::INACTIVE);
58 bool MediaSession::AddPlayer(MediaSessionObserver* observer,
59 int player_id,
60 Type type) {
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));
68 return true;
71 State old_audio_focus_state = audio_focus_state_;
72 audio_focus_state_ = RequestSystemAudioFocus(type) ? State::ACTIVE
73 : State::INACTIVE;
74 audio_focus_type_ = type;
76 if (audio_focus_state_ != State::ACTIVE)
77 return false;
79 // The session should be reset if a player is starting while all players are
80 // suspended.
81 if (old_audio_focus_state != State::ACTIVE)
82 players_.clear();
84 players_.insert(PlayerIdentifier(observer, player_id));
85 UpdateWebContents();
87 return true;
90 void MediaSession::RemovePlayer(MediaSessionObserver* observer,
91 int player_id) {
92 auto it = players_.find(PlayerIdentifier(observer, player_id));
93 if (it != players_.end())
94 players_.erase(it);
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++);
103 else
104 ++it;
107 AbandonSystemAudioFocusIfNeeded();
110 void MediaSession::OnSuspend(JNIEnv* env, jobject obj, jboolean temporary) {
111 if (audio_focus_state_ != State::ACTIVE)
112 return;
114 OnSuspendInternal(SuspendType::SYSTEM);
115 if (!temporary)
116 audio_focus_state_ = State::INACTIVE;
117 UpdateWebContents();
120 void MediaSession::OnResume(JNIEnv* env, jobject obj) {
121 if (audio_focus_state_ != State::SUSPENDED)
122 return;
124 OnResumeInternal(SuspendType::SYSTEM);
125 UpdateWebContents();
128 void MediaSession::Resume() {
129 DCHECK(IsSuspended());
131 OnResumeInternal(SuspendType::UI);
134 void MediaSession::Suspend() {
135 DCHECK(!IsSuspended());
137 OnSuspendInternal(SuspendType::UI);
140 bool MediaSession::IsSuspended() const {
141 // TODO(mlamouri): should be == State::SUSPENDED.
142 return audio_focus_state_ != State::ACTIVE;
145 bool MediaSession::IsControllable() const {
146 // Only content type media session can be controllable unless it is inactive.
147 return audio_focus_state_ != State::INACTIVE &&
148 audio_focus_type_ == Type::Content;
151 void MediaSession::ResetJavaRefForTest() {
152 j_media_session_.Reset();
155 bool MediaSession::IsActiveForTest() const {
156 return audio_focus_state_ == State::ACTIVE;
159 MediaSession::Type MediaSession::audio_focus_type_for_test() const {
160 return audio_focus_type_;
163 void MediaSession::RemoveAllPlayersForTest() {
164 players_.clear();
165 AbandonSystemAudioFocusIfNeeded();
168 void MediaSession::OnSuspendInternal(SuspendType type) {
169 audio_focus_state_ = State::SUSPENDED;
170 suspend_type_ = type;
172 for (const auto& it : players_)
173 it.observer->OnSuspend(it.player_id);
176 void MediaSession::OnResumeInternal(SuspendType type) {
177 if (suspend_type_ != type && type != SuspendType::UI)
178 return;
180 audio_focus_state_ = State::ACTIVE;
182 for (const auto& it : players_)
183 it.observer->OnResume(it.player_id);
186 MediaSession::MediaSession(WebContents* web_contents)
187 : WebContentsObserver(web_contents),
188 audio_focus_state_(State::INACTIVE),
189 audio_focus_type_(Type::Transient) {}
191 void MediaSession::Initialize() {
192 JNIEnv* env = base::android::AttachCurrentThread();
193 DCHECK(env);
194 j_media_session_.Reset(Java_MediaSession_createMediaSession(
195 env,
196 base::android::GetApplicationContext(),
197 reinterpret_cast<intptr_t>(this)));
200 bool MediaSession::RequestSystemAudioFocus(Type type) {
201 // During tests, j_media_session_ might be null.
202 if (j_media_session_.is_null())
203 return true;
205 JNIEnv* env = base::android::AttachCurrentThread();
206 DCHECK(env);
207 return Java_MediaSession_requestAudioFocus(env, j_media_session_.obj(),
208 type == Type::Transient);
211 void MediaSession::AbandonSystemAudioFocusIfNeeded() {
212 if (audio_focus_state_ == State::INACTIVE || !players_.empty())
213 return;
215 // During tests, j_media_session_ might be null.
216 if (!j_media_session_.is_null()) {
217 JNIEnv* env = base::android::AttachCurrentThread();
218 DCHECK(env);
219 Java_MediaSession_abandonAudioFocus(env, j_media_session_.obj());
222 audio_focus_state_ = State::INACTIVE;
223 UpdateWebContents();
226 void MediaSession::UpdateWebContents() {
227 static_cast<WebContentsImpl*>(web_contents())->OnMediaSessionStateChanged();
230 } // namespace content