| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/media/android/media_session.h" | 5 #include "content/browser/media/android/media_session.h" |
| 6 | 6 |
| 7 #include "base/android/jni_android.h" | 7 #include "base/android/jni_android.h" |
| 8 #include "content/browser/media/android/media_session_observer.h" | 8 #include "content/browser/media/android/media_session_observer.h" |
| 9 #include "content/browser/web_contents/web_contents_impl.h" | 9 #include "content/browser/web_contents/web_contents_impl.h" |
| 10 #include "content/public/browser/web_contents.h" | 10 #include "content/public/browser/web_contents.h" |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 45 if (!session) { | 45 if (!session) { |
| 46 CreateForWebContents(web_contents); | 46 CreateForWebContents(web_contents); |
| 47 session = FromWebContents(web_contents); | 47 session = FromWebContents(web_contents); |
| 48 session->Initialize(); | 48 session->Initialize(); |
| 49 } | 49 } |
| 50 return session; | 50 return session; |
| 51 } | 51 } |
| 52 | 52 |
| 53 MediaSession::~MediaSession() { | 53 MediaSession::~MediaSession() { |
| 54 DCHECK(players_.empty()); | 54 DCHECK(players_.empty()); |
| 55 DCHECK(audio_focus_state_ == State::Suspended); | 55 DCHECK(audio_focus_state_ == State::Inactive); |
| 56 } | 56 } |
| 57 | 57 |
| 58 bool MediaSession::AddPlayer(MediaSessionObserver* observer, | 58 bool MediaSession::AddPlayer(MediaSessionObserver* observer, |
| 59 int player_id, | 59 int player_id, |
| 60 Type type) { | 60 Type type) { |
| 61 // If the audio focus is already granted and is of type Content, there is | 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 | 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 | 63 // also transient, there is also nothing to do. Otherwise, the session needs |
| 64 // to request audio focus again. | 64 // to request audio focus again. |
| 65 if (audio_focus_state_ == State::Active && | 65 if (audio_focus_state_ == State::Active && |
| 66 (audio_focus_type_ == Type::Content || audio_focus_type_ == type)) { | 66 (audio_focus_type_ == Type::Content || audio_focus_type_ == type)) { |
| 67 players_.insert(PlayerIdentifier(observer, player_id)); | 67 players_.insert(PlayerIdentifier(observer, player_id)); |
| 68 return true; | 68 return true; |
| 69 } | 69 } |
| 70 | 70 |
| 71 State old_audio_focus_state = audio_focus_state_; | 71 State old_audio_focus_state = audio_focus_state_; |
| 72 audio_focus_state_ = RequestSystemAudioFocus(type) ? State::Active | 72 audio_focus_state_ = RequestSystemAudioFocus(type) ? State::Active |
| 73 : State::Suspended; | 73 : State::Inactive; |
| 74 audio_focus_type_ = type; | 74 audio_focus_type_ = type; |
| 75 | 75 |
| 76 if (audio_focus_state_ != State::Active) | 76 if (audio_focus_state_ != State::Active) |
| 77 return false; | 77 return false; |
| 78 | 78 |
| 79 // The session should be reset if a player is starting while all players are | 79 // The session should be reset if a player is starting while all players are |
| 80 // suspended. | 80 // suspended. |
| 81 if (old_audio_focus_state != State::Active) | 81 if (old_audio_focus_state != State::Active) |
| 82 players_.clear(); | 82 players_.clear(); |
| 83 | 83 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 101 if (it->observer == observer) | 101 if (it->observer == observer) |
| 102 players_.erase(it++); | 102 players_.erase(it++); |
| 103 else | 103 else |
| 104 ++it; | 104 ++it; |
| 105 } | 105 } |
| 106 | 106 |
| 107 AbandonSystemAudioFocusIfNeeded(); | 107 AbandonSystemAudioFocusIfNeeded(); |
| 108 } | 108 } |
| 109 | 109 |
| 110 void MediaSession::OnSuspend(JNIEnv* env, jobject obj, jboolean temporary) { | 110 void MediaSession::OnSuspend(JNIEnv* env, jobject obj, jboolean temporary) { |
| 111 OnSuspendInternal(temporary); | 111 if (audio_focus_state_ != State::Active) |
| 112 return; |
| 113 |
| 114 OnSuspendInternal(SuspendType::System); |
| 115 if (!temporary) |
| 116 audio_focus_state_ = State::Inactive; |
| 112 UpdateWebContents(); | 117 UpdateWebContents(); |
| 113 } | 118 } |
| 114 | 119 |
| 115 void MediaSession::OnResume(JNIEnv* env, jobject obj) { | 120 void MediaSession::OnResume(JNIEnv* env, jobject obj) { |
| 116 OnResumeInternal(); | 121 if (audio_focus_state_ != State::Suspended) |
| 122 return; |
| 123 |
| 124 OnResumeInternal(SuspendType::System); |
| 117 UpdateWebContents(); | 125 UpdateWebContents(); |
| 118 } | 126 } |
| 119 | 127 |
| 120 void MediaSession::Resume() { | 128 void MediaSession::Resume(SuspendType type) { |
| 121 DCHECK(IsSuspended()); | 129 DCHECK(IsSuspended()); |
| 122 | 130 |
| 123 OnResumeInternal(); | 131 OnResumeInternal(type); |
| 124 } | 132 } |
| 125 | 133 |
| 126 void MediaSession::Suspend() { | 134 void MediaSession::Suspend(SuspendType type) { |
| 127 DCHECK(!IsSuspended()); | 135 DCHECK(!IsSuspended()); |
| 128 | 136 |
| 129 // Since the playback can be resumed, it's a transient suspension. | 137 // Since the playback can be resumed, it's a transient suspension. |
| 130 OnSuspendInternal(true); | 138 OnSuspendInternal(type); |
| 131 } | 139 } |
| 132 | 140 |
| 133 bool MediaSession::IsSuspended() const { | 141 bool MediaSession::IsSuspended() const { |
| 142 // TODO(mlamouri): should be == State::Suspended. |
| 134 return audio_focus_state_ != State::Active; | 143 return audio_focus_state_ != State::Active; |
| 135 } | 144 } |
| 136 | 145 |
| 137 bool MediaSession::IsControllable() const { | 146 bool MediaSession::IsControllable() const { |
| 138 // Only content type media session can be controllable unless it's stopped. | 147 // Only content type media session can be controllable unless it is inactive. |
| 139 return audio_focus_state_ != State::Suspended && | 148 return audio_focus_state_ != State::Inactive && |
| 140 audio_focus_type_ == Type::Content; | 149 audio_focus_type_ == Type::Content; |
| 141 } | 150 } |
| 142 | 151 |
| 143 void MediaSession::ResetJavaRefForTest() { | 152 void MediaSession::ResetJavaRefForTest() { |
| 144 j_media_session_.Reset(); | 153 j_media_session_.Reset(); |
| 145 } | 154 } |
| 146 | 155 |
| 147 bool MediaSession::IsActiveForTest() const { | 156 bool MediaSession::IsActiveForTest() const { |
| 148 return audio_focus_state_ == State::Active; | 157 return audio_focus_state_ == State::Active; |
| 149 } | 158 } |
| 150 | 159 |
| 151 MediaSession::Type MediaSession::audio_focus_type_for_test() const { | 160 MediaSession::Type MediaSession::audio_focus_type_for_test() const { |
| 152 return audio_focus_type_; | 161 return audio_focus_type_; |
| 153 } | 162 } |
| 154 | 163 |
| 155 void MediaSession::RemoveAllPlayersForTest() { | 164 void MediaSession::RemoveAllPlayersForTest() { |
| 156 players_.clear(); | 165 players_.clear(); |
| 157 AbandonSystemAudioFocusIfNeeded(); | 166 AbandonSystemAudioFocusIfNeeded(); |
| 158 } | 167 } |
| 159 | 168 |
| 160 void MediaSession::OnSuspendInternal(bool temporary) { | 169 void MediaSession::OnSuspendInternal(SuspendType type) { |
| 161 if (temporary) | 170 audio_focus_state_ = State::Suspended; |
| 162 audio_focus_state_ = State::TemporarilySuspended; | 171 suspend_type_ = type; |
| 163 else | |
| 164 audio_focus_state_ = State::Suspended; | |
| 165 | 172 |
| 166 for (const auto& it : players_) | 173 for (const auto& it : players_) |
| 167 it.observer->OnSuspend(it.player_id); | 174 it.observer->OnSuspend(it.player_id); |
| 168 } | 175 } |
| 169 | 176 |
| 170 void MediaSession::OnResumeInternal() { | 177 void MediaSession::OnResumeInternal(SuspendType type) { |
| 178 if (suspend_type_ != type && type != SuspendType::UI) |
| 179 return; |
| 180 |
| 171 audio_focus_state_ = State::Active; | 181 audio_focus_state_ = State::Active; |
| 172 | 182 |
| 173 for (const auto& it : players_) | 183 for (const auto& it : players_) |
| 174 it.observer->OnResume(it.player_id); | 184 it.observer->OnResume(it.player_id); |
| 175 } | 185 } |
| 176 | 186 |
| 177 MediaSession::MediaSession(WebContents* web_contents) | 187 MediaSession::MediaSession(WebContents* web_contents) |
| 178 : WebContentsObserver(web_contents), | 188 : WebContentsObserver(web_contents), |
| 179 audio_focus_state_(State::Suspended), | 189 audio_focus_state_(State::Inactive), |
| 180 audio_focus_type_(Type::Transient) {} | 190 audio_focus_type_(Type::Transient) {} |
| 181 | 191 |
| 182 void MediaSession::Initialize() { | 192 void MediaSession::Initialize() { |
| 183 JNIEnv* env = base::android::AttachCurrentThread(); | 193 JNIEnv* env = base::android::AttachCurrentThread(); |
| 184 DCHECK(env); | 194 DCHECK(env); |
| 185 j_media_session_.Reset(Java_MediaSession_createMediaSession( | 195 j_media_session_.Reset(Java_MediaSession_createMediaSession( |
| 186 env, | 196 env, |
| 187 base::android::GetApplicationContext(), | 197 base::android::GetApplicationContext(), |
| 188 reinterpret_cast<intptr_t>(this))); | 198 reinterpret_cast<intptr_t>(this))); |
| 189 } | 199 } |
| 190 | 200 |
| 191 bool MediaSession::RequestSystemAudioFocus(Type type) { | 201 bool MediaSession::RequestSystemAudioFocus(Type type) { |
| 192 // During tests, j_media_session_ might be null. | 202 // During tests, j_media_session_ might be null. |
| 193 if (j_media_session_.is_null()) | 203 if (j_media_session_.is_null()) |
| 194 return true; | 204 return true; |
| 195 | 205 |
| 196 JNIEnv* env = base::android::AttachCurrentThread(); | 206 JNIEnv* env = base::android::AttachCurrentThread(); |
| 197 DCHECK(env); | 207 DCHECK(env); |
| 198 return Java_MediaSession_requestAudioFocus(env, j_media_session_.obj(), | 208 return Java_MediaSession_requestAudioFocus(env, j_media_session_.obj(), |
| 199 type == Type::Transient); | 209 type == Type::Transient); |
| 200 } | 210 } |
| 201 | 211 |
| 202 void MediaSession::AbandonSystemAudioFocusIfNeeded() { | 212 void MediaSession::AbandonSystemAudioFocusIfNeeded() { |
| 203 if (audio_focus_state_ == State::Suspended || !players_.empty()) | 213 if (audio_focus_state_ == State::Inactive || !players_.empty()) |
| 204 return; | 214 return; |
| 205 | 215 |
| 206 // During tests, j_media_session_ might be null. | 216 // During tests, j_media_session_ might be null. |
| 207 if (!j_media_session_.is_null()) { | 217 if (!j_media_session_.is_null()) { |
| 208 JNIEnv* env = base::android::AttachCurrentThread(); | 218 JNIEnv* env = base::android::AttachCurrentThread(); |
| 209 DCHECK(env); | 219 DCHECK(env); |
| 210 Java_MediaSession_abandonAudioFocus(env, j_media_session_.obj()); | 220 Java_MediaSession_abandonAudioFocus(env, j_media_session_.obj()); |
| 211 } | 221 } |
| 212 | 222 |
| 213 audio_focus_state_ = State::Suspended; | 223 audio_focus_state_ = State::Inactive; |
| 214 UpdateWebContents(); | 224 UpdateWebContents(); |
| 215 } | 225 } |
| 216 | 226 |
| 217 void MediaSession::UpdateWebContents() { | 227 void MediaSession::UpdateWebContents() { |
| 218 static_cast<WebContentsImpl*>(web_contents())->OnMediaSessionStateChanged(); | 228 static_cast<WebContentsImpl*>(web_contents())->OnMediaSessionStateChanged(); |
| 219 } | 229 } |
| 220 | 230 |
| 221 } // namespace content | 231 } // namespace content |
| OLD | NEW |