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 1930be970a8ecd984ace75354e00a8d969577d18e9Insun Kangimport android.app.ActivityManager; 2030be970a8ecd984ace75354e00a8d969577d18e9Insun Kangimport android.app.ActivityManagerNative; 21d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErikimport android.media.session.MediaController.PlaybackInfo; 22a8f951462791a16f47e8c07e552232f31dcefac5RoboErikimport android.media.session.PlaybackState; 2342ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErikimport android.media.session.MediaSession; 2430be970a8ecd984ace75354e00a8d969577d18e9Insun Kangimport android.os.RemoteException; 25a5b02329209be355eafadbdf9ee685ffa58d3148RoboErikimport android.os.UserHandle; 26a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 27a8f951462791a16f47e8c07e552232f31dcefac5RoboErikimport java.io.PrintWriter; 28a8f951462791a16f47e8c07e552232f31dcefac5RoboErikimport java.util.ArrayList; 2930be970a8ecd984ace75354e00a8d969577d18e9Insun Kangimport java.util.List; 30a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 31a8f951462791a16f47e8c07e552232f31dcefac5RoboErik/** 32a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Keeps track of media sessions and their priority for notifications, media 3301a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown * button dispatch, etc. 34a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 35a8f951462791a16f47e8c07e552232f31dcefac5RoboErikpublic class MediaSessionStack { 36a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 37a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * These are states that usually indicate the user took an action and should 38a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * bump priority regardless of the old state. 39a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 40a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private static final int[] ALWAYS_PRIORITY_STATES = { 4179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik PlaybackState.STATE_FAST_FORWARDING, 4279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik PlaybackState.STATE_REWINDING, 4379fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik PlaybackState.STATE_SKIPPING_TO_PREVIOUS, 4479fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik PlaybackState.STATE_SKIPPING_TO_NEXT }; 45a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 46a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * These are states that usually indicate the user took an action if they 47a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * were entered from a non-priority state. 48a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 49a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private static final int[] TRANSITION_PRIORITY_STATES = { 5079fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik PlaybackState.STATE_BUFFERING, 5179fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik PlaybackState.STATE_CONNECTING, 5279fa4630bbca7c6c251eea99fe8997e4b45beceeRoboErik PlaybackState.STATE_PLAYING }; 53a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 54a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); 55a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 564646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik private MediaSessionRecord mGlobalPrioritySession; 574646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik 58870c5a6593f723211a4a90766be9191c69f38570RoboErik // The last record that either entered one of the playing states or was 59870c5a6593f723211a4a90766be9191c69f38570RoboErik // added. 60870c5a6593f723211a4a90766be9191c69f38570RoboErik private MediaSessionRecord mLastInterestingRecord; 61a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private MediaSessionRecord mCachedButtonReceiver; 62a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private MediaSessionRecord mCachedDefault; 63b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik private MediaSessionRecord mCachedVolumeDefault; 64a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private ArrayList<MediaSessionRecord> mCachedActiveList; 65a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private ArrayList<MediaSessionRecord> mCachedTransportControlList; 66a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 67a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 6830be970a8ecd984ace75354e00a8d969577d18e9Insun Kang * Checks if a media session is created from the most recent app. 6930be970a8ecd984ace75354e00a8d969577d18e9Insun Kang * 7030be970a8ecd984ace75354e00a8d969577d18e9Insun Kang * @param record A media session record to be examined. 7130be970a8ecd984ace75354e00a8d969577d18e9Insun Kang * @return true if the media session's package name equals to the most recent app, false 7230be970a8ecd984ace75354e00a8d969577d18e9Insun Kang * otherwise. 7330be970a8ecd984ace75354e00a8d969577d18e9Insun Kang */ 7430be970a8ecd984ace75354e00a8d969577d18e9Insun Kang private static boolean isFromMostRecentApp(MediaSessionRecord record) { 7530be970a8ecd984ace75354e00a8d969577d18e9Insun Kang if (ActivityManager.getCurrentUser() != record.getUserId()) { 7630be970a8ecd984ace75354e00a8d969577d18e9Insun Kang return false; 7730be970a8ecd984ace75354e00a8d969577d18e9Insun Kang } 7830be970a8ecd984ace75354e00a8d969577d18e9Insun Kang try { 7930be970a8ecd984ace75354e00a8d969577d18e9Insun Kang List<ActivityManager.RecentTaskInfo> tasks = 8030be970a8ecd984ace75354e00a8d969577d18e9Insun Kang ActivityManagerNative.getDefault().getRecentTasks(1, 8130be970a8ecd984ace75354e00a8d969577d18e9Insun Kang ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS | 8230be970a8ecd984ace75354e00a8d969577d18e9Insun Kang ActivityManager.RECENT_IGNORE_UNAVAILABLE | 8330be970a8ecd984ace75354e00a8d969577d18e9Insun Kang ActivityManager.RECENT_INCLUDE_PROFILES | 849536c809dda20bd01c3d5c30df046b271ba2ec4fJeff Sharkey ActivityManager.RECENT_WITH_EXCLUDED, record.getUserId()).getList(); 8530be970a8ecd984ace75354e00a8d969577d18e9Insun Kang if (tasks != null && !tasks.isEmpty()) { 8630be970a8ecd984ace75354e00a8d969577d18e9Insun Kang ActivityManager.RecentTaskInfo recentTask = tasks.get(0); 8730be970a8ecd984ace75354e00a8d969577d18e9Insun Kang if (recentTask.baseIntent != null) 8830be970a8ecd984ace75354e00a8d969577d18e9Insun Kang return recentTask.baseIntent.getComponent().getPackageName() 8930be970a8ecd984ace75354e00a8d969577d18e9Insun Kang .equals(record.getPackageName()); 9030be970a8ecd984ace75354e00a8d969577d18e9Insun Kang } 9130be970a8ecd984ace75354e00a8d969577d18e9Insun Kang } catch (RemoteException e) { 9230be970a8ecd984ace75354e00a8d969577d18e9Insun Kang return false; 9330be970a8ecd984ace75354e00a8d969577d18e9Insun Kang } 9430be970a8ecd984ace75354e00a8d969577d18e9Insun Kang return false; 9530be970a8ecd984ace75354e00a8d969577d18e9Insun Kang } 9630be970a8ecd984ace75354e00a8d969577d18e9Insun Kang 9730be970a8ecd984ace75354e00a8d969577d18e9Insun Kang /** 98a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Add a record to the priority tracker. 99a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 100a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param record The record to add. 101a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 102a8f951462791a16f47e8c07e552232f31dcefac5RoboErik public void addSession(MediaSessionRecord record) { 103a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mSessions.add(record); 104a8f951462791a16f47e8c07e552232f31dcefac5RoboErik clearCache(); 10530be970a8ecd984ace75354e00a8d969577d18e9Insun Kang if (isFromMostRecentApp(record)) { 10630be970a8ecd984ace75354e00a8d969577d18e9Insun Kang mLastInterestingRecord = record; 10730be970a8ecd984ace75354e00a8d969577d18e9Insun Kang } 108a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 109a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 110a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 111a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Remove a record from the priority tracker. 112a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 113a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param record The record to remove. 114a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 115a8f951462791a16f47e8c07e552232f31dcefac5RoboErik public void removeSession(MediaSessionRecord record) { 116a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mSessions.remove(record); 1174646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik if (record == mGlobalPrioritySession) { 1184646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik mGlobalPrioritySession = null; 1194646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik } 120a8f951462791a16f47e8c07e552232f31dcefac5RoboErik clearCache(); 121a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 122a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 123a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 124a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Notify the priority tracker that a session's state changed. 125a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 126a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param record The record that changed. 127a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param oldState Its old playback state. 128a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param newState Its new playback state. 1292e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik * @return true if the priority order was updated, false otherwise. 130a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 1312e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik public boolean onPlaystateChange(MediaSessionRecord record, int oldState, int newState) { 132a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (shouldUpdatePriority(oldState, newState)) { 133a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mSessions.remove(record); 134a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mSessions.add(0, record); 135a8f951462791a16f47e8c07e552232f31dcefac5RoboErik clearCache(); 136870c5a6593f723211a4a90766be9191c69f38570RoboErik // This becomes the last interesting record since it entered a 137870c5a6593f723211a4a90766be9191c69f38570RoboErik // playing state 138870c5a6593f723211a4a90766be9191c69f38570RoboErik mLastInterestingRecord = record; 1392e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik return true; 14023b113592a5f461ec66026cbf8bce253cb8d3a46RoboErik } else if (!MediaSession.isActiveState(newState)) { 14123b113592a5f461ec66026cbf8bce253cb8d3a46RoboErik // Just clear the volume cache when a state goes inactive 142b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik mCachedVolumeDefault = null; 143a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 1442e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik return false; 145a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 146a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 147a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 148a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Handle any stack changes that need to occur in response to a session 149a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * state change. TODO add the old and new session state as params 150a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 151a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param record The record that changed. 152a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 153a8f951462791a16f47e8c07e552232f31dcefac5RoboErik public void onSessionStateChange(MediaSessionRecord record) { 1544d26598677095e74e30fc12f46a769cac800f946RoboErik if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { 1554d26598677095e74e30fc12f46a769cac800f946RoboErik mGlobalPrioritySession = record; 1564d26598677095e74e30fc12f46a769cac800f946RoboErik } 157a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // For now just clear the cache. Eventually we'll selectively clear 158a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // depending on what changed. 159a8f951462791a16f47e8c07e552232f31dcefac5RoboErik clearCache(); 160a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 161a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 162a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 163a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Get the current priority sorted list of active sessions. The most 164a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * important session is at index 0 and the least important at size - 1. 165a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 166a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * @param userId The user to check. 167a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @return All the active sessions in priority order. 168a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 169a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik public ArrayList<MediaSessionRecord> getActiveSessions(int userId) { 170a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (mCachedActiveList == null) { 171a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik mCachedActiveList = getPriorityListLocked(true, 0, userId); 172a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 173a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return mCachedActiveList; 174a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 175a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 176a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 177a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Get the current priority sorted list of active sessions that use 178a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * transport controls. The most important session is at index 0 and the 179a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * least important at size -1. 180a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 181a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * @param userId The user to check. 182a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @return All the active sessions that handle transport controls in 183a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * priority order. 184a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 185a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik public ArrayList<MediaSessionRecord> getTransportControlSessions(int userId) { 186a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (mCachedTransportControlList == null) { 187a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mCachedTransportControlList = getPriorityListLocked(true, 18842ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS, userId); 189a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 190a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return mCachedTransportControlList; 191a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 192a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 193a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 194a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Get the highest priority active session. 195a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 196a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * @param userId The user to check. 197a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @return The current highest priority session or null. 198a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 199a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik public MediaSessionRecord getDefaultSession(int userId) { 200a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (mCachedDefault != null) { 201a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return mCachedDefault; 202a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 203a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId); 204a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (records.size() > 0) { 205a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return records.get(0); 206a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 207a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return null; 208a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 209a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 210a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 211a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Get the highest priority session that can handle media buttons. 212a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 213a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * @param userId The user to check. 214870c5a6593f723211a4a90766be9191c69f38570RoboErik * @param includeNotPlaying Return a non-playing session if nothing else is 215870c5a6593f723211a4a90766be9191c69f38570RoboErik * available 216a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @return The default media button session or null. 217a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 218870c5a6593f723211a4a90766be9191c69f38570RoboErik public MediaSessionRecord getDefaultMediaButtonSession(int userId, boolean includeNotPlaying) { 2194646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) { 2204646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik return mGlobalPrioritySession; 2214646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik } 222a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (mCachedButtonReceiver != null) { 223a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return mCachedButtonReceiver; 224a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 225a8f951462791a16f47e8c07e552232f31dcefac5RoboErik ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 22642ea7eecd149161ed192d3029f0d77d1d08a4aa5RoboErik MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, userId); 227a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (records.size() > 0) { 228870c5a6593f723211a4a90766be9191c69f38570RoboErik MediaSessionRecord record = records.get(0); 229870c5a6593f723211a4a90766be9191c69f38570RoboErik if (record.isPlaybackActive(false)) { 230870c5a6593f723211a4a90766be9191c69f38570RoboErik // Since we're going to send a button event to this record make 231870c5a6593f723211a4a90766be9191c69f38570RoboErik // it the last interesting one. 232870c5a6593f723211a4a90766be9191c69f38570RoboErik mLastInterestingRecord = record; 233870c5a6593f723211a4a90766be9191c69f38570RoboErik mCachedButtonReceiver = record; 234870c5a6593f723211a4a90766be9191c69f38570RoboErik } else if (mLastInterestingRecord != null) { 235870c5a6593f723211a4a90766be9191c69f38570RoboErik if (records.contains(mLastInterestingRecord)) { 236870c5a6593f723211a4a90766be9191c69f38570RoboErik mCachedButtonReceiver = mLastInterestingRecord; 237870c5a6593f723211a4a90766be9191c69f38570RoboErik } else { 238870c5a6593f723211a4a90766be9191c69f38570RoboErik // That record is no longer used. Clear its reference. 239870c5a6593f723211a4a90766be9191c69f38570RoboErik mLastInterestingRecord = null; 240870c5a6593f723211a4a90766be9191c69f38570RoboErik } 241870c5a6593f723211a4a90766be9191c69f38570RoboErik } 242870c5a6593f723211a4a90766be9191c69f38570RoboErik if (includeNotPlaying && mCachedButtonReceiver == null) { 243870c5a6593f723211a4a90766be9191c69f38570RoboErik // If we really want a record and we didn't find one yet use the 244870c5a6593f723211a4a90766be9191c69f38570RoboErik // highest priority session even if it's not playing. 245870c5a6593f723211a4a90766be9191c69f38570RoboErik mCachedButtonReceiver = record; 246870c5a6593f723211a4a90766be9191c69f38570RoboErik } 247a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 248a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return mCachedButtonReceiver; 249a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 250a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 251b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik public MediaSessionRecord getDefaultVolumeSession(int userId) { 252b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) { 253b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik return mGlobalPrioritySession; 254b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik } 255b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik if (mCachedVolumeDefault != null) { 256b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik return mCachedVolumeDefault; 257b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik } 258b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId); 259b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik int size = records.size(); 260b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik for (int i = 0; i < size; i++) { 261b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik MediaSessionRecord record = records.get(i); 262b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik if (record.isPlaybackActive(false)) { 263b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik mCachedVolumeDefault = record; 264b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik return record; 265b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik } 266b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik } 267b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik return null; 268b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik } 269b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik 27019c9518f6a817d53d5234de0020313cab6950b2fRoboErik public MediaSessionRecord getDefaultRemoteSession(int userId) { 27119c9518f6a817d53d5234de0020313cab6950b2fRoboErik ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId); 27219c9518f6a817d53d5234de0020313cab6950b2fRoboErik 27319c9518f6a817d53d5234de0020313cab6950b2fRoboErik int size = records.size(); 27419c9518f6a817d53d5234de0020313cab6950b2fRoboErik for (int i = 0; i < size; i++) { 27519c9518f6a817d53d5234de0020313cab6950b2fRoboErik MediaSessionRecord record = records.get(i); 276d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErik if (record.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE) { 27719c9518f6a817d53d5234de0020313cab6950b2fRoboErik return record; 27819c9518f6a817d53d5234de0020313cab6950b2fRoboErik } 27919c9518f6a817d53d5234de0020313cab6950b2fRoboErik } 28019c9518f6a817d53d5234de0020313cab6950b2fRoboErik return null; 28119c9518f6a817d53d5234de0020313cab6950b2fRoboErik } 28219c9518f6a817d53d5234de0020313cab6950b2fRoboErik 283de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik public boolean isGlobalPriorityActive() { 284de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik return mGlobalPrioritySession == null ? false : mGlobalPrioritySession.isActive(); 285de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik } 286de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik 287a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik public void dump(PrintWriter pw, String prefix) { 288a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0, 289a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik UserHandle.USER_ALL); 290a8f951462791a16f47e8c07e552232f31dcefac5RoboErik int count = sortedSessions.size(); 2914d26598677095e74e30fc12f46a769cac800f946RoboErik pw.println(prefix + "Global priority session is " + mGlobalPrioritySession); 292a8f951462791a16f47e8c07e552232f31dcefac5RoboErik pw.println(prefix + "Sessions Stack - have " + count + " sessions:"); 293a8f951462791a16f47e8c07e552232f31dcefac5RoboErik String indent = prefix + " "; 294a8f951462791a16f47e8c07e552232f31dcefac5RoboErik for (int i = 0; i < count; i++) { 295a8f951462791a16f47e8c07e552232f31dcefac5RoboErik MediaSessionRecord record = sortedSessions.get(i); 296a8f951462791a16f47e8c07e552232f31dcefac5RoboErik record.dump(pw, indent); 297a8f951462791a16f47e8c07e552232f31dcefac5RoboErik pw.println(); 298a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 299a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 300a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 301a8f951462791a16f47e8c07e552232f31dcefac5RoboErik /** 302a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * Get a priority sorted list of sessions. Can filter to only return active 303a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * sessions or sessions with specific flags. 304a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * 305a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param activeOnly True to only return active sessions, false to return 306a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * all sessions. 307a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @param withFlags Only return sessions with all the specified flags set. 0 308a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * returns all sessions. 309a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * @param userId The user to get sessions for. {@link UserHandle#USER_ALL} 310a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik * will return sessions for all users. 311a8f951462791a16f47e8c07e552232f31dcefac5RoboErik * @return The priority sorted list of sessions. 312a8f951462791a16f47e8c07e552232f31dcefac5RoboErik */ 313a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags, 314a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik int userId) { 315a8f951462791a16f47e8c07e552232f31dcefac5RoboErik ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>(); 316a8f951462791a16f47e8c07e552232f31dcefac5RoboErik int lastLocalIndex = 0; 317a8f951462791a16f47e8c07e552232f31dcefac5RoboErik int lastActiveIndex = 0; 318a8f951462791a16f47e8c07e552232f31dcefac5RoboErik int lastPublishedIndex = 0; 319a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 320a8f951462791a16f47e8c07e552232f31dcefac5RoboErik int size = mSessions.size(); 321a8f951462791a16f47e8c07e552232f31dcefac5RoboErik for (int i = 0; i < size; i++) { 322a8f951462791a16f47e8c07e552232f31dcefac5RoboErik final MediaSessionRecord session = mSessions.get(i); 323a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 324a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik if (userId != UserHandle.USER_ALL && userId != session.getUserId()) { 325a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik // Filter out sessions for the wrong user 326a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik continue; 327a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik } 328a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if ((session.getFlags() & withFlags) != withFlags) { 329a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik // Filter out sessions with the wrong flags 330a8f951462791a16f47e8c07e552232f31dcefac5RoboErik continue; 331a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 332a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (!session.isActive()) { 333a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (!activeOnly) { 334a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // If we're getting unpublished as well always put them at 335a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // the end 336a8f951462791a16f47e8c07e552232f31dcefac5RoboErik result.add(session); 337a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 338a8f951462791a16f47e8c07e552232f31dcefac5RoboErik continue; 339a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 340a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 341a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (session.isSystemPriority()) { 342a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // System priority sessions are special and always go at the 343a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // front. We expect there to only be one of these at a time. 344a8f951462791a16f47e8c07e552232f31dcefac5RoboErik result.add(0, session); 345a8f951462791a16f47e8c07e552232f31dcefac5RoboErik lastLocalIndex++; 346a8f951462791a16f47e8c07e552232f31dcefac5RoboErik lastActiveIndex++; 347a8f951462791a16f47e8c07e552232f31dcefac5RoboErik lastPublishedIndex++; 348b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik } else if (session.isPlaybackActive(true)) { 34901a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown // TODO this with real local route check 35001a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown if (true) { 351a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // Active local sessions get top priority 352a8f951462791a16f47e8c07e552232f31dcefac5RoboErik result.add(lastLocalIndex, session); 353a8f951462791a16f47e8c07e552232f31dcefac5RoboErik lastLocalIndex++; 354a8f951462791a16f47e8c07e552232f31dcefac5RoboErik lastActiveIndex++; 355a8f951462791a16f47e8c07e552232f31dcefac5RoboErik lastPublishedIndex++; 356a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } else { 357a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // Then active remote sessions 358a8f951462791a16f47e8c07e552232f31dcefac5RoboErik result.add(lastActiveIndex, session); 359a8f951462791a16f47e8c07e552232f31dcefac5RoboErik lastActiveIndex++; 360a8f951462791a16f47e8c07e552232f31dcefac5RoboErik lastPublishedIndex++; 361a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 362a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } else { 363a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // inactive sessions go at the end in order of whoever last did 364a8f951462791a16f47e8c07e552232f31dcefac5RoboErik // something. 365a8f951462791a16f47e8c07e552232f31dcefac5RoboErik result.add(lastPublishedIndex, session); 366a8f951462791a16f47e8c07e552232f31dcefac5RoboErik lastPublishedIndex++; 367a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 368a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 369a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 370a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return result; 371a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 372a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 373a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private boolean shouldUpdatePriority(int oldState, int newState) { 374a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (containsState(newState, ALWAYS_PRIORITY_STATES)) { 375a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return true; 376a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 377a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (!containsState(oldState, TRANSITION_PRIORITY_STATES) 378a8f951462791a16f47e8c07e552232f31dcefac5RoboErik && containsState(newState, TRANSITION_PRIORITY_STATES)) { 379a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return true; 380a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 381a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return false; 382a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 383a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 384a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private boolean containsState(int state, int[] states) { 385a8f951462791a16f47e8c07e552232f31dcefac5RoboErik for (int i = 0; i < states.length; i++) { 386a8f951462791a16f47e8c07e552232f31dcefac5RoboErik if (states[i] == state) { 387a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return true; 388a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 389a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 390a8f951462791a16f47e8c07e552232f31dcefac5RoboErik return false; 391a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 392a8f951462791a16f47e8c07e552232f31dcefac5RoboErik 393a8f951462791a16f47e8c07e552232f31dcefac5RoboErik private void clearCache() { 394a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mCachedDefault = null; 395b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik mCachedVolumeDefault = null; 396a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mCachedButtonReceiver = null; 397a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mCachedActiveList = null; 398a8f951462791a16f47e8c07e552232f31dcefac5RoboErik mCachedTransportControlList = null; 399a8f951462791a16f47e8c07e552232f31dcefac5RoboErik } 400a8f951462791a16f47e8c07e552232f31dcefac5RoboErik} 401