MediaSessionStack.java revision 98e4aafc8a5a7c39c538f504e59c3c2b5cbb8445
1a8f951462791a16f47e8c07e552232f31dcefac5RoboErik/* 2a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Copyright (C) 2014 The Android Open Source Project 3a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 4a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Licensed under the Apache License, Version 2.0 (the "License"); 5a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * you may not use this file except in compliance with the License. 6a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * You may obtain a copy of the License at 7a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 8a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * http://www.apache.org/licenses/LICENSE-2.0 9a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 10a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Unless required by applicable law or agreed to in writing, software 11a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * distributed under the License is distributed on an "AS IS" BASIS, 12a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * See the License for the specific language governing permissions and 14a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * limitations under the License. 15a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 16a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 17a8f951462791a16f47e8c07e552232f31dcefac5RoboErikpackage com.android.server.media; 18a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 19d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErikimport android.media.session.MediaController.PlaybackInfo; 2042ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikimport android.media.session.MediaSession; 2192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kimimport android.media.session.PlaybackState; 2292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kimimport android.os.Debug; 23a5b02329209be355eafadbdf9ee685ffa58d3148RoboErikimport android.os.UserHandle; 2492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kimimport android.util.IntArray; 2592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kimimport android.util.Log; 26aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kimimport android.util.SparseArray; 27a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 28a8f951462791a16f47e8c07e552232f31dcefac5RoboErikimport java.io.PrintWriter; 29a8f951462791a16f47e8c07e552232f31dcefac5RoboErikimport java.util.ArrayList; 3030be970a8ecd984ace75354e00a8d969577d18e9Insun Kangimport java.util.List; 31a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 32a8f951462791a16f47e8c07e552232f31dcefac5RoboErik/** 33a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Keeps track of media sessions and their priority for notifications, media 3401a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown * button dispatch, etc. 35a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim * <p>This class isn't thread-safe. The caller should take care of the synchronization. 36a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 378f729087dfde6b1ceb6a451a5950e80176a7da2dJaewan Kimclass MediaSessionStack { 3892dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim private static final boolean DEBUG = MediaSessionService.DEBUG; 3992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim private static final String TAG = "MediaSessionStack"; 4092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim 4192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim /** 42aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim * Listen the change in the media button session. 4392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim */ 4492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim interface OnMediaButtonSessionChangedListener { 4592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim /** 4692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * Called when the media button session is changed. 4792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim */ 4892dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession, 4992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim MediaSessionRecord newMediaButtonSession); 5092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 5192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim 52a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 53a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * These are states that usually indicate the user took an action and should 54a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * bump priority regardless of the old state. 55a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 56a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private static final int[] ALWAYS_PRIORITY_STATES = { 5779fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik PlaybackState.STATE_FAST_FORWARDING, 5879fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik PlaybackState.STATE_REWINDING, 5979fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik PlaybackState.STATE_SKIPPING_TO_PREVIOUS, 6079fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik PlaybackState.STATE_SKIPPING_TO_NEXT }; 61a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 62a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * These are states that usually indicate the user took an action if they 63a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * were entered from a non-priority state. 64a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 65a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private static final int[] TRANSITION_PRIORITY_STATES = { 6679fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik PlaybackState.STATE_BUFFERING, 6779fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik PlaybackState.STATE_CONNECTING, 6879fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik PlaybackState.STATE_PLAYING }; 69a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 7092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim /** 7192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * Sorted list of the media sessions. 7292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * The session of which PlaybackState is changed to ALWAYS_PRIORITY_STATES or 7392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * TRANSITION_PRIORITY_STATES comes first. 7492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * @see #shouldUpdatePriority 7592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim */ 7692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim private final List<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); 7792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim 7892dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim private final AudioPlaybackMonitor mAudioPlaybackMonitor; 7992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim private final OnMediaButtonSessionChangedListener mOnMediaButtonSessionChangedListener; 8092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim 8192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim /** 8292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * The media button session which receives media key events. 8392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * It could be null if the previous media buttion session is released. 8492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim */ 8592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim private MediaSessionRecord mMediaButtonSession; 86a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 87a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private MediaSessionRecord mCachedDefault; 88b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik private MediaSessionRecord mCachedVolumeDefault; 89aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim 90aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim /** 91aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim * Cache the result of the {@link #getActiveSessions} per user. 92aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim */ 93aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim private final SparseArray<ArrayList<MediaSessionRecord>> mCachedActiveLists = 94aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim new SparseArray<>(); 95a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 9692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim MediaSessionStack(AudioPlaybackMonitor monitor, OnMediaButtonSessionChangedListener listener) { 9792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim mAudioPlaybackMonitor = monitor; 9892dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim mOnMediaButtonSessionChangedListener = listener; 9930be970a8ecd984ace75354e00a8d969577d18e9Insun Kang } 10030be970a8ecd984ace75354e00a8d969577d18e9Insun Kang 10130be970a8ecd984ace75354e00a8d969577d18e9Insun Kang /** 102a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Add a record to the priority tracker. 103a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 104a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param record The record to add. 105a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 10692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim public void addSession(MediaSessionRecord record) { 107a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mSessions.add(record); 108aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim clearCache(record.getUserId()); 10992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim 11092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // Update the media button session. 11192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // The added session could be the session from the package with the audio playback. 11292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // This can happen if an app starts audio playback before creating media session. 11392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim updateMediaButtonSessionIfNeeded(); 114a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 115a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 116a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 117a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Remove a record from the priority tracker. 118a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 119a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param record The record to remove. 120a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 121a8f951462791a16f47e8c07e552232f31dcefac5RoboErik public void removeSession(MediaSessionRecord record) { 122a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mSessions.remove(record); 12392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim if (mMediaButtonSession == record) { 124fc1f7ab8ad2f6c01b804cdf41097766a30c89ca9Jaewan Kim // When the media button session is removed, nullify the media button session and do not 125fc1f7ab8ad2f6c01b804cdf41097766a30c89ca9Jaewan Kim // search for the alternative media session within the app. It's because the alternative 126fc1f7ab8ad2f6c01b804cdf41097766a30c89ca9Jaewan Kim // media session might be a dummy which isn't able to handle the media key events. 12798e4aafc8a5a7c39c538f504e59c3c2b5cbb8445Jaewan Kim updateMediaButtonSession(null); 12892dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 129aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim clearCache(record.getUserId()); 130a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 131a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 132a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 133a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim * Return if the record exists in the priority tracker. 134a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim */ 135a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim public boolean contains(MediaSessionRecord record) { 136a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim return mSessions.contains(record); 137a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim } 138a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim 139a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim /** 14092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * Notify the priority tracker that a session's playback state changed. 141a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 142a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param record The record that changed. 143a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param oldState Its old playback state. 144a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param newState Its new playback state. 145a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 14692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim public void onPlaystateChanged(MediaSessionRecord record, int oldState, int newState) { 147a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (shouldUpdatePriority(oldState, newState)) { 148a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mSessions.remove(record); 149a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mSessions.add(0, record); 150aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim clearCache(record.getUserId()); 15123b113592a5f461ec66026cbf8bce253cb8d3a46RoboErik } else if (!MediaSession.isActiveState(newState)) { 15223b113592a5f461ec66026cbf8bce253cb8d3a46RoboErik // Just clear the volume cache when a state goes inactive 153b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik mCachedVolumeDefault = null; 154a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 15592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim 15692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // In most cases, playback state isn't needed for finding media buttion session, 15792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // but we only use it as a hint if an app has multiple local media sessions. 15892dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // In that case, we pick the media session whose PlaybackState matches 15992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // the audio playback configuration. 16092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim if (mMediaButtonSession != null && mMediaButtonSession.getUid() == record.getUid()) { 161fc1f7ab8ad2f6c01b804cdf41097766a30c89ca9Jaewan Kim MediaSessionRecord newMediaButtonSession = 162fc1f7ab8ad2f6c01b804cdf41097766a30c89ca9Jaewan Kim findMediaButtonSession(mMediaButtonSession.getUid()); 163fc1f7ab8ad2f6c01b804cdf41097766a30c89ca9Jaewan Kim if (newMediaButtonSession != mMediaButtonSession) { 16498e4aafc8a5a7c39c538f504e59c3c2b5cbb8445Jaewan Kim updateMediaButtonSession(newMediaButtonSession); 165fc1f7ab8ad2f6c01b804cdf41097766a30c89ca9Jaewan Kim } 16692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 167a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 168a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 169a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 17092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * Handle the change in activeness for a session. 171a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 172a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param record The record that changed. 173a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 174a8f951462791a16f47e8c07e552232f31dcefac5RoboErik public void onSessionStateChange(MediaSessionRecord record) { 175a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // For now just clear the cache. Eventually we'll selectively clear 176a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // depending on what changed. 177aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim clearCache(record.getUserId()); 178a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 179a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 180a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 18192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * Update the media button session if needed. 18292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * <p>The media button session is the session that will receive the media button events. 18392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * <p>We send the media button events to the lastly played app. If the app has the media 18492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * session, the session will receive the media button events. 18592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim */ 18692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim public void updateMediaButtonSessionIfNeeded() { 18792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim if (DEBUG) { 18892dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2)); 18992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 19092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim IntArray audioPlaybackUids = mAudioPlaybackMonitor.getSortedAudioPlaybackClientUids(); 19192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim for (int i = 0; i < audioPlaybackUids.size(); i++) { 19292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim MediaSessionRecord mediaButtonSession = 19392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim findMediaButtonSession(audioPlaybackUids.get(i)); 19492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim if (mediaButtonSession != null) { 19592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // Found the media button session. 19692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim mAudioPlaybackMonitor.cleanUpAudioPlaybackUids(mediaButtonSession.getUid()); 19792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim if (mMediaButtonSession != mediaButtonSession) { 19898e4aafc8a5a7c39c538f504e59c3c2b5cbb8445Jaewan Kim updateMediaButtonSession(mediaButtonSession); 19992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 20092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim return; 20192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 20292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 20392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 20492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim 20592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim /** 20692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * Find the media button session with the given {@param uid}. 20792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * If the app has multiple media sessions, the media session matches the audio playback state 20892dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * becomes the media button session. 20992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * 21092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * @return The media button session. Returns {@code null} if the app doesn't have a media 21192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * session. 21292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim */ 21392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim private MediaSessionRecord findMediaButtonSession(int uid) { 21492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim MediaSessionRecord mediaButtonSession = null; 21592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim for (MediaSessionRecord session : mSessions) { 216745e28cb0b52eaf93da752af2b70062f5ff3ca89Jaewan Kim if (uid == session.getUid()) { 21792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim if (session.isPlaybackActive() == 21892dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim mAudioPlaybackMonitor.isPlaybackActive(session.getUid())) { 21992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // If there's a media session whose PlaybackState matches 22092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // the audio playback state, return it immediately. 22192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim return session; 22292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 22392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim if (mediaButtonSession == null) { 22492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // Among the media sessions whose PlaybackState doesn't match 22592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // the audio playback state, pick the top priority. 22692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim mediaButtonSession = session; 22792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 22892dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 22992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 23092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim return mediaButtonSession; 23192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 23292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim 23392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim /** 234a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Get the current priority sorted list of active sessions. The most 235a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * important session is at index 0 and the least important at size - 1. 236a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 237aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim * @param userId The user to check. It can be {@link UserHandle#USER_ALL} to get all sessions 238aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim * for all users in this {@link MediaSessionStack}. 239a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @return All the active sessions in priority order. 240a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 241a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik public ArrayList<MediaSessionRecord> getActiveSessions(int userId) { 242aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim ArrayList<MediaSessionRecord> cachedActiveList = mCachedActiveLists.get(userId); 243aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim if (cachedActiveList == null) { 244aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim cachedActiveList = getPriorityList(true, userId); 245aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim mCachedActiveLists.put(userId, cachedActiveList); 246a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 247aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim return cachedActiveList; 248a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 249a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 250a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 25192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * Get the media button session which receives the media button events. 252a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 25392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * @return The media button session or null. 254a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 25592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim public MediaSessionRecord getMediaButtonSession() { 25692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim return mMediaButtonSession; 257a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 258a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 25998e4aafc8a5a7c39c538f504e59c3c2b5cbb8445Jaewan Kim private void updateMediaButtonSession(MediaSessionRecord newMediaButtonSession) { 26098e4aafc8a5a7c39c538f504e59c3c2b5cbb8445Jaewan Kim MediaSessionRecord oldMediaButtonSession = mMediaButtonSession; 26198e4aafc8a5a7c39c538f504e59c3c2b5cbb8445Jaewan Kim mMediaButtonSession = newMediaButtonSession; 26298e4aafc8a5a7c39c538f504e59c3c2b5cbb8445Jaewan Kim mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged( 26398e4aafc8a5a7c39c538f504e59c3c2b5cbb8445Jaewan Kim oldMediaButtonSession, newMediaButtonSession); 26498e4aafc8a5a7c39c538f504e59c3c2b5cbb8445Jaewan Kim } 26598e4aafc8a5a7c39c538f504e59c3c2b5cbb8445Jaewan Kim 266a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim public MediaSessionRecord getDefaultVolumeSession() { 267b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik if (mCachedVolumeDefault != null) { 268b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik return mCachedVolumeDefault; 269b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik } 27092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim ArrayList<MediaSessionRecord> records = getPriorityList(true, UserHandle.USER_ALL); 271b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik int size = records.size(); 272b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik for (int i = 0; i < size; i++) { 273b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik MediaSessionRecord record = records.get(i); 27492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim if (record.isPlaybackActive()) { 275b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik mCachedVolumeDefault = record; 276b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik return record; 277b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik } 278b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik } 279b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik return null; 280b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik } 281b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik 28219c9518f6a817d53d5234de0020313cab6950b2fRoboErik public MediaSessionRecord getDefaultRemoteSession(int userId) { 28392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim ArrayList<MediaSessionRecord> records = getPriorityList(true, userId); 28419c9518f6a817d53d5234de0020313cab6950b2fRoboErik 28519c9518f6a817d53d5234de0020313cab6950b2fRoboErik int size = records.size(); 28619c9518f6a817d53d5234de0020313cab6950b2fRoboErik for (int i = 0; i < size; i++) { 28719c9518f6a817d53d5234de0020313cab6950b2fRoboErik MediaSessionRecord record = records.get(i); 288d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik if (record.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE) { 28919c9518f6a817d53d5234de0020313cab6950b2fRoboErik return record; 29019c9518f6a817d53d5234de0020313cab6950b2fRoboErik } 29119c9518f6a817d53d5234de0020313cab6950b2fRoboErik } 29219c9518f6a817d53d5234de0020313cab6950b2fRoboErik return null; 29319c9518f6a817d53d5234de0020313cab6950b2fRoboErik } 29419c9518f6a817d53d5234de0020313cab6950b2fRoboErik 295a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik public void dump(PrintWriter pw, String prefix) { 29692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim ArrayList<MediaSessionRecord> sortedSessions = getPriorityList(false, 297a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik UserHandle.USER_ALL); 298a8f951462791a16f47e8c07e552232f31dcefac5RoboErik int count = sortedSessions.size(); 29992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim pw.println(prefix + "Media button session is " + mMediaButtonSession); 300a8f951462791a16f47e8c07e552232f31dcefac5RoboErik pw.println(prefix + "Sessions Stack - have " + count + " sessions:"); 301a8f951462791a16f47e8c07e552232f31dcefac5RoboErik String indent = prefix + " "; 302a8f951462791a16f47e8c07e552232f31dcefac5RoboErik for (int i = 0; i < count; i++) { 303a8f951462791a16f47e8c07e552232f31dcefac5RoboErik MediaSessionRecord record = sortedSessions.get(i); 304a8f951462791a16f47e8c07e552232f31dcefac5RoboErik record.dump(pw, indent); 305a8f951462791a16f47e8c07e552232f31dcefac5RoboErik pw.println(); 306a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 307a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 308a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 309a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 310a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Get a priority sorted list of sessions. Can filter to only return active 31192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * sessions or sessions. 31292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * <p>Here's the priority order. 31392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * <li>System priority session (session with FLAG_EXCLUSIVE_GLOBAL_PRIORITY)</li> 31492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * <li>Active sessions whose PlaybackState is active</li> 31592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * <li>Active sessions whose PlaybackState is inactive</li> 31692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * <li>Inactive sessions</li> 317a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 318a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param activeOnly True to only return active sessions, false to return 319a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * all sessions. 320a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim * @param userId The user to get sessions for. {@link UserHandle#USER_ALL} 321a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * will return sessions for all users. 322a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @return The priority sorted list of sessions. 323a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 32492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim public ArrayList<MediaSessionRecord> getPriorityList(boolean activeOnly, int userId) { 325a8f951462791a16f47e8c07e552232f31dcefac5RoboErik ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>(); 32692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim int lastPlaybackActiveIndex = 0; 327a8f951462791a16f47e8c07e552232f31dcefac5RoboErik int lastActiveIndex = 0; 328a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 329a8f951462791a16f47e8c07e552232f31dcefac5RoboErik int size = mSessions.size(); 330a8f951462791a16f47e8c07e552232f31dcefac5RoboErik for (int i = 0; i < size; i++) { 331a8f951462791a16f47e8c07e552232f31dcefac5RoboErik final MediaSessionRecord session = mSessions.get(i); 332a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 333a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim if (userId != UserHandle.USER_ALL && userId != session.getUserId()) { 334a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik // Filter out sessions for the wrong user 335a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik continue; 336a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik } 33792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim 338a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (!session.isActive()) { 339a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (!activeOnly) { 340a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // If we're getting unpublished as well always put them at 341a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // the end 342a8f951462791a16f47e8c07e552232f31dcefac5RoboErik result.add(session); 343a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 344a8f951462791a16f47e8c07e552232f31dcefac5RoboErik continue; 345a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 346a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 347a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (session.isSystemPriority()) { 348a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // System priority sessions are special and always go at the 349a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // front. We expect there to only be one of these at a time. 350a8f951462791a16f47e8c07e552232f31dcefac5RoboErik result.add(0, session); 35192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim lastPlaybackActiveIndex++; 35292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim lastActiveIndex++; 35392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } else if (session.isPlaybackActive()) { 35492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim result.add(lastPlaybackActiveIndex++, session); 355a8f951462791a16f47e8c07e552232f31dcefac5RoboErik lastActiveIndex++; 356a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } else { 35792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim result.add(lastActiveIndex++, session); 358a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 359a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 360a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 361a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return result; 362a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 363a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 364a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private boolean shouldUpdatePriority(int oldState, int newState) { 365a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (containsState(newState, ALWAYS_PRIORITY_STATES)) { 366a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return true; 367a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 368a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (!containsState(oldState, TRANSITION_PRIORITY_STATES) 369a8f951462791a16f47e8c07e552232f31dcefac5RoboErik && containsState(newState, TRANSITION_PRIORITY_STATES)) { 370a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return true; 371a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 372a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return false; 373a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 374a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 375a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private boolean containsState(int state, int[] states) { 376a8f951462791a16f47e8c07e552232f31dcefac5RoboErik for (int i = 0; i < states.length; i++) { 377a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (states[i] == state) { 378a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return true; 379a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 380a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 381a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return false; 382a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 383a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 384aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim private void clearCache(int userId) { 385a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mCachedDefault = null; 386b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik mCachedVolumeDefault = null; 387aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim mCachedActiveLists.remove(userId); 388aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim // mCachedActiveLists may also include the list of sessions for UserHandle.USER_ALL, 389aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim // so they also need to be cleared. 390aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim mCachedActiveLists.remove(UserHandle.USER_ALL); 391a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 392a8f951462791a16f47e8c07e552232f31dcefac5RoboErik} 393