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 78875e697f9c6624f34851e09b4bf1ccef810f59e9Sungsoo Lim private final AudioPlayerStateMonitor mAudioPlayerStateMonitor; 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 87b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik private MediaSessionRecord mCachedVolumeDefault; 88aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim 89aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim /** 90aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim * Cache the result of the {@link #getActiveSessions} per user. 91aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim */ 92aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim private final SparseArray<ArrayList<MediaSessionRecord>> mCachedActiveLists = 93aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim new SparseArray<>(); 94a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 95875e697f9c6624f34851e09b4bf1ccef810f59e9Sungsoo Lim MediaSessionStack(AudioPlayerStateMonitor monitor, OnMediaButtonSessionChangedListener listener) { 96875e697f9c6624f34851e09b4bf1ccef810f59e9Sungsoo Lim mAudioPlayerStateMonitor = monitor; 9792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim mOnMediaButtonSessionChangedListener = listener; 9830be970a8ecd984ace75354e00a8d969577d18e9Insun Kang } 9930be970a8ecd984ace75354e00a8d969577d18e9Insun Kang 10030be970a8ecd984ace75354e00a8d969577d18e9Insun Kang /** 101a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Add a record to the priority tracker. 102a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 103a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param record The record to add. 104a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 10592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim public void addSession(MediaSessionRecord record) { 106a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mSessions.add(record); 107aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim clearCache(record.getUserId()); 10892dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim 10992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // Update the media button session. 11092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // The added session could be the session from the package with the audio playback. 11192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // This can happen if an app starts audio playback before creating media session. 11292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim updateMediaButtonSessionIfNeeded(); 113a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 114a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 115a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 116a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Remove a record from the priority tracker. 117a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 118a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param record The record to remove. 119a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 120a8f951462791a16f47e8c07e552232f31dcefac5RoboErik public void removeSession(MediaSessionRecord record) { 121a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mSessions.remove(record); 12292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim if (mMediaButtonSession == record) { 123fc1f7ab8ad2f6c01b804cdf41097766a30c89ca9Jaewan Kim // When the media button session is removed, nullify the media button session and do not 124fc1f7ab8ad2f6c01b804cdf41097766a30c89ca9Jaewan Kim // search for the alternative media session within the app. It's because the alternative 125fc1f7ab8ad2f6c01b804cdf41097766a30c89ca9Jaewan Kim // media session might be a dummy which isn't able to handle the media key events. 12698e4aafc8a5a7c39c538f504e59c3c2b5cbb8445Jaewan Kim updateMediaButtonSession(null); 12792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 128aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim clearCache(record.getUserId()); 129a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 130a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 131a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 132a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim * Return if the record exists in the priority tracker. 133a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim */ 134a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim public boolean contains(MediaSessionRecord record) { 135a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim return mSessions.contains(record); 136a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim } 137a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim 138a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim /** 13992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * Notify the priority tracker that a session's playback state changed. 140a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 141a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param record The record that changed. 142a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param oldState Its old playback state. 143a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param newState Its new playback state. 144a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 14592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim public void onPlaystateChanged(MediaSessionRecord record, int oldState, int newState) { 146a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (shouldUpdatePriority(oldState, newState)) { 147a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mSessions.remove(record); 148a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mSessions.add(0, record); 149aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim clearCache(record.getUserId()); 15023b113592a5f461ec66026cbf8bce253cb8d3a46RoboErik } else if (!MediaSession.isActiveState(newState)) { 15123b113592a5f461ec66026cbf8bce253cb8d3a46RoboErik // Just clear the volume cache when a state goes inactive 152b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik mCachedVolumeDefault = null; 153a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 15492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim 15539f479fe7e35028663e9663a9684a5d3600e8198Sungsoo // In most cases, playback state isn't needed for finding media button session, 15692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // but we only use it as a hint if an app has multiple local media sessions. 15792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // In that case, we pick the media session whose PlaybackState matches 15892dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // the audio playback configuration. 15992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim if (mMediaButtonSession != null && mMediaButtonSession.getUid() == record.getUid()) { 160fc1f7ab8ad2f6c01b804cdf41097766a30c89ca9Jaewan Kim MediaSessionRecord newMediaButtonSession = 161fc1f7ab8ad2f6c01b804cdf41097766a30c89ca9Jaewan Kim findMediaButtonSession(mMediaButtonSession.getUid()); 162fc1f7ab8ad2f6c01b804cdf41097766a30c89ca9Jaewan Kim if (newMediaButtonSession != mMediaButtonSession) { 16398e4aafc8a5a7c39c538f504e59c3c2b5cbb8445Jaewan Kim updateMediaButtonSession(newMediaButtonSession); 164fc1f7ab8ad2f6c01b804cdf41097766a30c89ca9Jaewan Kim } 16592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 166a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 167a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 168a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 16992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * Handle the change in activeness for a session. 170a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 171a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param record The record that changed. 172a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 173a8f951462791a16f47e8c07e552232f31dcefac5RoboErik public void onSessionStateChange(MediaSessionRecord record) { 174a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // For now just clear the cache. Eventually we'll selectively clear 175a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // depending on what changed. 176aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim clearCache(record.getUserId()); 177a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 178a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 179a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 18092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * Update the media button session if needed. 18192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * <p>The media button session is the session that will receive the media button events. 18292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * <p>We send the media button events to the lastly played app. If the app has the media 18392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * session, the session will receive the media button events. 18492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim */ 18592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim public void updateMediaButtonSessionIfNeeded() { 18692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim if (DEBUG) { 18792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim Log.d(TAG, "updateMediaButtonSessionIfNeeded, callers=" + Debug.getCallers(2)); 18892dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 189875e697f9c6624f34851e09b4bf1ccef810f59e9Sungsoo Lim IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids(); 19092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim for (int i = 0; i < audioPlaybackUids.size(); i++) { 19192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim MediaSessionRecord mediaButtonSession = 19292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim findMediaButtonSession(audioPlaybackUids.get(i)); 19392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim if (mediaButtonSession != null) { 19492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim // Found the media button session. 195875e697f9c6624f34851e09b4bf1ccef810f59e9Sungsoo Lim mAudioPlayerStateMonitor.cleanUpAudioPlaybackUids(mediaButtonSession.getUid()); 19692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim if (mMediaButtonSession != mediaButtonSession) { 19798e4aafc8a5a7c39c538f504e59c3c2b5cbb8445Jaewan Kim updateMediaButtonSession(mediaButtonSession); 19892dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 19992dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim return; 20092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 20192dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 20292dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim } 20392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim 20492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim /** 20592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * Find the media button session with the given {@param uid}. 20639f479fe7e35028663e9663a9684a5d3600e8198Sungsoo * If the app has multiple media sessions, the media session whose playback state is not null 20739f479fe7e35028663e9663a9684a5d3600e8198Sungsoo * and matches the audio playback state becomes the media button session. Otherwise the top 20839f479fe7e35028663e9663a9684a5d3600e8198Sungsoo * priority session 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()) { 21739f479fe7e35028663e9663a9684a5d3600e8198Sungsoo if (session.getPlaybackState() != null && session.isPlaybackActive() == 218875e697f9c6624f34851e09b4bf1ccef810f59e9Sungsoo Lim mAudioPlayerStateMonitor.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>Active sessions whose PlaybackState is active</li> 31492dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * <li>Active sessions whose PlaybackState is inactive</li> 31592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim * <li>Inactive sessions</li> 316a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 317a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param activeOnly True to only return active sessions, false to return 318a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * all sessions. 319a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim * @param userId The user to get sessions for. {@link UserHandle#USER_ALL} 320a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * will return sessions for all users. 321a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @return The priority sorted list of sessions. 322a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 32392dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim public ArrayList<MediaSessionRecord> getPriorityList(boolean activeOnly, int userId) { 324a8f951462791a16f47e8c07e552232f31dcefac5RoboErik ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>(); 32592dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim int lastPlaybackActiveIndex = 0; 326a8f951462791a16f47e8c07e552232f31dcefac5RoboErik int lastActiveIndex = 0; 327a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 328a8f951462791a16f47e8c07e552232f31dcefac5RoboErik int size = mSessions.size(); 329a8f951462791a16f47e8c07e552232f31dcefac5RoboErik for (int i = 0; i < size; i++) { 330a8f951462791a16f47e8c07e552232f31dcefac5RoboErik final MediaSessionRecord session = mSessions.get(i); 331a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 332a7dce19b613b52894d36044ae6e88a57572d79e5Jaewan Kim if (userId != UserHandle.USER_ALL && userId != session.getUserId()) { 333a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik // Filter out sessions for the wrong user 334a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik continue; 335a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik } 33692dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim 337a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (!session.isActive()) { 338a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (!activeOnly) { 339a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // If we're getting unpublished as well always put them at 340a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // the end 341a8f951462791a16f47e8c07e552232f31dcefac5RoboErik result.add(session); 342a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 343a8f951462791a16f47e8c07e552232f31dcefac5RoboErik continue; 344a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 345a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 346101b4d5e74f5840a9ac3a6cbfa9f62f2ce048a06Jaewan Kim if (session.isPlaybackActive()) { 34792dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim result.add(lastPlaybackActiveIndex++, session); 348a8f951462791a16f47e8c07e552232f31dcefac5RoboErik lastActiveIndex++; 349a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } else { 35092dea33bfebed04533264b06e036d04cc16b9608Jaewan Kim result.add(lastActiveIndex++, session); 351a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 352a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 353a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 354a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return result; 355a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 356a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 357a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private boolean shouldUpdatePriority(int oldState, int newState) { 358a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (containsState(newState, ALWAYS_PRIORITY_STATES)) { 359a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return true; 360a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 361a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (!containsState(oldState, TRANSITION_PRIORITY_STATES) 362a8f951462791a16f47e8c07e552232f31dcefac5RoboErik && containsState(newState, TRANSITION_PRIORITY_STATES)) { 363a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return true; 364a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 365a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return false; 366a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 367a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 368a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private boolean containsState(int state, int[] states) { 369a8f951462791a16f47e8c07e552232f31dcefac5RoboErik for (int i = 0; i < states.length; i++) { 370a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (states[i] == state) { 371a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return true; 372a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 373a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 374a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return false; 375a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 376a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 377aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim private void clearCache(int userId) { 378b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik mCachedVolumeDefault = null; 379aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim mCachedActiveLists.remove(userId); 380aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim // mCachedActiveLists may also include the list of sessions for UserHandle.USER_ALL, 381aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim // so they also need to be cleared. 382aceef919807a7a3205d09470f23b91c0aa41aa7bJaewan Kim mCachedActiveLists.remove(UserHandle.USER_ALL); 383a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 384a8f951462791a16f47e8c07e552232f31dcefac5RoboErik} 385