MediaSessionStack.java revision b69ffd4dc2c8fa85e0064151141ebeee90de471e
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.media; 18 19import android.media.session.PlaybackState; 20import android.media.session.MediaSession; 21import android.os.UserHandle; 22 23import java.io.PrintWriter; 24import java.util.ArrayList; 25 26/** 27 * Keeps track of media sessions and their priority for notifications, media 28 * button routing, etc. 29 */ 30public class MediaSessionStack { 31 /** 32 * These are states that usually indicate the user took an action and should 33 * bump priority regardless of the old state. 34 */ 35 private static final int[] ALWAYS_PRIORITY_STATES = { 36 PlaybackState.STATE_FAST_FORWARDING, 37 PlaybackState.STATE_REWINDING, 38 PlaybackState.STATE_SKIPPING_TO_PREVIOUS, 39 PlaybackState.STATE_SKIPPING_TO_NEXT }; 40 /** 41 * These are states that usually indicate the user took an action if they 42 * were entered from a non-priority state. 43 */ 44 private static final int[] TRANSITION_PRIORITY_STATES = { 45 PlaybackState.STATE_BUFFERING, 46 PlaybackState.STATE_CONNECTING, 47 PlaybackState.STATE_PLAYING }; 48 49 private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); 50 51 private MediaSessionRecord mGlobalPrioritySession; 52 53 private MediaSessionRecord mCachedButtonReceiver; 54 private MediaSessionRecord mCachedDefault; 55 private MediaSessionRecord mCachedVolumeDefault; 56 private ArrayList<MediaSessionRecord> mCachedActiveList; 57 private ArrayList<MediaSessionRecord> mCachedTransportControlList; 58 59 /** 60 * Add a record to the priority tracker. 61 * 62 * @param record The record to add. 63 */ 64 public void addSession(MediaSessionRecord record) { 65 mSessions.add(record); 66 if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { 67 mGlobalPrioritySession = record; 68 } 69 clearCache(); 70 } 71 72 /** 73 * Remove a record from the priority tracker. 74 * 75 * @param record The record to remove. 76 */ 77 public void removeSession(MediaSessionRecord record) { 78 mSessions.remove(record); 79 if (record == mGlobalPrioritySession) { 80 mGlobalPrioritySession = null; 81 } 82 clearCache(); 83 } 84 85 /** 86 * Notify the priority tracker that a session's state changed. 87 * 88 * @param record The record that changed. 89 * @param oldState Its old playback state. 90 * @param newState Its new playback state. 91 */ 92 public void onPlaystateChange(MediaSessionRecord record, int oldState, int newState) { 93 if (shouldUpdatePriority(oldState, newState)) { 94 mSessions.remove(record); 95 mSessions.add(0, record); 96 clearCache(); 97 } else if (newState == PlaybackState.STATE_PAUSED) { 98 // Just clear the volume cache in this case 99 mCachedVolumeDefault = null; 100 } 101 } 102 103 /** 104 * Handle any stack changes that need to occur in response to a session 105 * state change. TODO add the old and new session state as params 106 * 107 * @param record The record that changed. 108 */ 109 public void onSessionStateChange(MediaSessionRecord record) { 110 // For now just clear the cache. Eventually we'll selectively clear 111 // depending on what changed. 112 clearCache(); 113 } 114 115 /** 116 * Get the current priority sorted list of active sessions. The most 117 * important session is at index 0 and the least important at size - 1. 118 * 119 * @param userId The user to check. 120 * @return All the active sessions in priority order. 121 */ 122 public ArrayList<MediaSessionRecord> getActiveSessions(int userId) { 123 if (mCachedActiveList == null) { 124 mCachedActiveList = getPriorityListLocked(true, 0, userId); 125 } 126 return mCachedActiveList; 127 } 128 129 /** 130 * Get the current priority sorted list of active sessions that use 131 * transport controls. The most important session is at index 0 and the 132 * least important at size -1. 133 * 134 * @param userId The user to check. 135 * @return All the active sessions that handle transport controls in 136 * priority order. 137 */ 138 public ArrayList<MediaSessionRecord> getTransportControlSessions(int userId) { 139 if (mCachedTransportControlList == null) { 140 mCachedTransportControlList = getPriorityListLocked(true, 141 MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS, userId); 142 } 143 return mCachedTransportControlList; 144 } 145 146 /** 147 * Get the highest priority active session. 148 * 149 * @param userId The user to check. 150 * @return The current highest priority session or null. 151 */ 152 public MediaSessionRecord getDefaultSession(int userId) { 153 if (mCachedDefault != null) { 154 return mCachedDefault; 155 } 156 ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId); 157 if (records.size() > 0) { 158 return records.get(0); 159 } 160 return null; 161 } 162 163 /** 164 * Get the highest priority session that can handle media buttons. 165 * 166 * @param userId The user to check. 167 * @return The default media button session or null. 168 */ 169 public MediaSessionRecord getDefaultMediaButtonSession(int userId) { 170 if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) { 171 return mGlobalPrioritySession; 172 } 173 if (mCachedButtonReceiver != null) { 174 return mCachedButtonReceiver; 175 } 176 ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 177 MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, userId); 178 if (records.size() > 0) { 179 mCachedButtonReceiver = records.get(0); 180 } 181 return mCachedButtonReceiver; 182 } 183 184 public MediaSessionRecord getDefaultVolumeSession(int userId) { 185 if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) { 186 return mGlobalPrioritySession; 187 } 188 if (mCachedVolumeDefault != null) { 189 return mCachedVolumeDefault; 190 } 191 ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId); 192 int size = records.size(); 193 for (int i = 0; i < size; i++) { 194 MediaSessionRecord record = records.get(i); 195 if (record.isPlaybackActive(false)) { 196 mCachedVolumeDefault = record; 197 return record; 198 } 199 } 200 return null; 201 } 202 203 public void dump(PrintWriter pw, String prefix) { 204 ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0, 205 UserHandle.USER_ALL); 206 int count = sortedSessions.size(); 207 pw.println(prefix + "Sessions Stack - have " + count + " sessions:"); 208 String indent = prefix + " "; 209 for (int i = 0; i < count; i++) { 210 MediaSessionRecord record = sortedSessions.get(i); 211 record.dump(pw, indent); 212 pw.println(); 213 } 214 } 215 216 /** 217 * Get a priority sorted list of sessions. Can filter to only return active 218 * sessions or sessions with specific flags. 219 * 220 * @param activeOnly True to only return active sessions, false to return 221 * all sessions. 222 * @param withFlags Only return sessions with all the specified flags set. 0 223 * returns all sessions. 224 * @param userId The user to get sessions for. {@link UserHandle#USER_ALL} 225 * will return sessions for all users. 226 * @return The priority sorted list of sessions. 227 */ 228 private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags, 229 int userId) { 230 ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>(); 231 int lastLocalIndex = 0; 232 int lastActiveIndex = 0; 233 int lastPublishedIndex = 0; 234 235 int size = mSessions.size(); 236 for (int i = 0; i < size; i++) { 237 final MediaSessionRecord session = mSessions.get(i); 238 239 if (userId != UserHandle.USER_ALL && userId != session.getUserId()) { 240 // Filter out sessions for the wrong user 241 continue; 242 } 243 if ((session.getFlags() & withFlags) != withFlags) { 244 // Filter out sessions with the wrong flags 245 continue; 246 } 247 if (!session.isActive()) { 248 if (!activeOnly) { 249 // If we're getting unpublished as well always put them at 250 // the end 251 result.add(session); 252 } 253 continue; 254 } 255 256 if (session.isSystemPriority()) { 257 // System priority sessions are special and always go at the 258 // front. We expect there to only be one of these at a time. 259 result.add(0, session); 260 lastLocalIndex++; 261 lastActiveIndex++; 262 lastPublishedIndex++; 263 } else if (session.isPlaybackActive(true)) { 264 // TODO replace getRoute() == null with real local route check 265 if(session.getRoute() == null) { 266 // Active local sessions get top priority 267 result.add(lastLocalIndex, session); 268 lastLocalIndex++; 269 lastActiveIndex++; 270 lastPublishedIndex++; 271 } else { 272 // Then active remote sessions 273 result.add(lastActiveIndex, session); 274 lastActiveIndex++; 275 lastPublishedIndex++; 276 } 277 } else { 278 // inactive sessions go at the end in order of whoever last did 279 // something. 280 result.add(lastPublishedIndex, session); 281 lastPublishedIndex++; 282 } 283 } 284 285 return result; 286 } 287 288 private boolean shouldUpdatePriority(int oldState, int newState) { 289 if (containsState(newState, ALWAYS_PRIORITY_STATES)) { 290 return true; 291 } 292 if (!containsState(oldState, TRANSITION_PRIORITY_STATES) 293 && containsState(newState, TRANSITION_PRIORITY_STATES)) { 294 return true; 295 } 296 return false; 297 } 298 299 private boolean containsState(int state, int[] states) { 300 for (int i = 0; i < states.length; i++) { 301 if (states[i] == state) { 302 return true; 303 } 304 } 305 return false; 306 } 307 308 private void clearCache() { 309 mCachedDefault = null; 310 mCachedVolumeDefault = null; 311 mCachedButtonReceiver = null; 312 mCachedActiveList = null; 313 mCachedTransportControlList = null; 314 } 315} 316