diff --git a/apex/media/framework/java/android/media/MediaController2.java b/apex/media/framework/java/android/media/MediaController2.java index c3dd3fe4451c9..d059c670ccb66 100644 --- a/apex/media/framework/java/android/media/MediaController2.java +++ b/apex/media/framework/java/android/media/MediaController2.java @@ -141,6 +141,9 @@ public class MediaController2 implements AutoCloseable { // Note: unbindService() throws IllegalArgumentException when it's called twice. return; } + if (DEBUG) { + Log.d(TAG, "closing " + this); + } mClosed = true; if (mServiceConnection != null) { // Note: This should be called even when the bindService() has returned false. diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java new file mode 100644 index 0000000000000..f3241ee44569c --- /dev/null +++ b/services/core/java/com/android/server/media/MediaSession2Record.java @@ -0,0 +1,179 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media; + +import android.media.MediaController2; +import android.media.Session2CommandGroup; +import android.media.Session2Token; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.Looper; +import android.os.ResultReceiver; +import android.os.UserHandle; +import android.util.Log; +import android.view.KeyEvent; + +import com.android.internal.annotations.GuardedBy; + +import java.io.PrintWriter; + +/** + * Keeps the record of {@link Session2Token} helps to send command to the corresponding session. + */ +// TODO(jaewan): Do not call service method directly -- introduce listener instead. +public class MediaSession2Record implements MediaSessionRecordImpl { + private static final String TAG = "MediaSession2Record"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private final Session2Token mSessionToken; + @GuardedBy("mLock") + private final HandlerExecutor mHandlerExecutor; + @GuardedBy("mLock") + private final MediaController2 mController; + @GuardedBy("mLock") + private final MediaSessionService mService; + @GuardedBy("mLock") + private boolean mIsConnected; + + public MediaSession2Record(Session2Token sessionToken, MediaSessionService service, + Looper handlerLooper) { + mSessionToken = sessionToken; + mService = service; + mHandlerExecutor = new HandlerExecutor(new Handler(handlerLooper)); + mController = new MediaController2.Builder(service.getContext(), sessionToken) + .setControllerCallback(mHandlerExecutor, new Controller2Callback()) + .build(); + } + + @Override + public String getPackageName() { + return mSessionToken.getPackageName(); + } + + public Session2Token getSession2Token() { + return mSessionToken; + } + + @Override + public int getUid() { + return mSessionToken.getUid(); + } + + @Override + public int getUserId() { + return UserHandle.getUserId(mSessionToken.getUid()); + } + + @Override + public boolean isSystemPriority() { + // System priority session is currently only allowed for telephony, and it's OK to stick to + // the media1 API at this moment. + return false; + } + + @Override + public void adjustVolume(String packageName, String opPackageName, int pid, int uid, + boolean asSystemService, int direction, int flags, boolean useSuggested) { + // TODO(jaewan): Add API to adjust volume. + } + + @Override + public boolean isActive() { + synchronized (mLock) { + return mIsConnected; + } + } + + @Override + public boolean checkPlaybackActiveState(boolean expected) { + synchronized (mLock) { + return mIsConnected && mController.isPlaybackActive() == expected; + } + } + + @Override + public boolean isPlaybackTypeLocal() { + // TODO(jaewan): Implement -- need API to know whether the playback is remote or local. + return true; + } + + @Override + public void close() { + synchronized (mLock) { + // Call close regardless of the mIsAvailable. This may be called when it's not yet + // connected. + mController.close(); + } + } + + @Override + public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, + KeyEvent ke, int sequenceId, ResultReceiver cb) { + // TODO(jaewan): Implement. + return false; + } + + @Override + public void dump(PrintWriter pw, String prefix) { + pw.println(prefix + "token=" + mSessionToken); + pw.println(prefix + "controller=" + mController); + + final String indent = prefix + " "; + pw.println(indent + "playbackActive=" + mController.isPlaybackActive()); + } + + @Override + public String toString() { + // TODO(jaewan): Also add getId(). + return getPackageName() + " (userId=" + getUserId() + ")"; + } + + private class Controller2Callback extends MediaController2.ControllerCallback { + @Override + public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) { + if (DEBUG) { + Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands); + } + synchronized (mLock) { + mIsConnected = true; + } + mService.onSessionActiveStateChanged(MediaSession2Record.this); + } + + @Override + public void onDisconnected(MediaController2 controller) { + if (DEBUG) { + Log.d(TAG, "disconnected from " + mSessionToken); + } + synchronized (mLock) { + mIsConnected = false; + } + mService.onSessionDied(MediaSession2Record.this); + } + + @Override + public void onPlaybackActiveChanged(MediaController2 controller, boolean playbackActive) { + if (DEBUG) { + Log.d(TAG, "playback active changed, " + mSessionToken + ", active=" + + playbackActive); + } + mService.onSessionPlaybackStateChanged(MediaSession2Record.this, playbackActive); + } + } +} diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index aa24ed26023a2..df115d0f2773c 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -56,13 +56,15 @@ import com.android.server.LocalServices; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** * This is the system implementation of a Session. Apps will interact with the * MediaSession wrapper class instead. */ -public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable { +// TODO(jaewan): Do not call service method directly -- introduce listener instead. +public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionRecordImpl { private static final String TAG = "MediaSessionRecord"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -72,6 +74,24 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable */ private static final int OPTIMISTIC_VOLUME_TIMEOUT = 1000; + /** + * These are states that usually indicate the user took an action and should + * bump priority regardless of the old state. + */ + private static final List ALWAYS_PRIORITY_STATES = Arrays.asList( + PlaybackState.STATE_FAST_FORWARDING, + PlaybackState.STATE_REWINDING, + PlaybackState.STATE_SKIPPING_TO_PREVIOUS, + PlaybackState.STATE_SKIPPING_TO_NEXT); + /** + * These are states that usually indicate the user took an action if they + * were entered from a non-priority state. + */ + private static final List TRANSITION_PRIORITY_STATES = Arrays.asList( + PlaybackState.STATE_BUFFERING, + PlaybackState.STATE_CONNECTING, + PlaybackState.STATE_PLAYING); + private final MessageHandler mHandler; private final int mOwnerPid; @@ -170,6 +190,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * * @return Info that identifies this session. */ + @Override public String getPackageName() { return mPackageName; } @@ -188,6 +209,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * * @return The UID for this session. */ + @Override public int getUid() { return mOwnerUid; } @@ -197,6 +219,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * * @return The user id for this session. */ + @Override public int getUserId() { return mUserId; } @@ -207,6 +230,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * * @return True if this is a system priority session, false otherwise */ + @Override public boolean isSystemPriority() { return (mFlags & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0; } @@ -220,7 +244,6 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * @param opPackageName The op package that made the original volume request. * @param pid The pid that made the original volume request. * @param uid The uid that made the original volume request. - * @param caller caller binder. can be {@code null} if it's from the volume key. * @param asSystemService {@code true} if the event sent to the session as if it was come from * the system service instead of the app process. This helps sessions to distinguish * between the key injection by the app and key events from the hardware devices. @@ -318,9 +341,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable /** * Check if this session has been set to active by the app. + *

+ * It's not used to prioritize sessions for dispatching media keys since API 26, but still used + * to filter session list in MediaSessionManager#getActiveSessions(). * * @return True if the session is active, false otherwise. */ + @Override public boolean isActive() { return mIsActive && !mDestroyed; } @@ -333,6 +360,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * @param expected True if playback is expected to be active. false otherwise. * @return True if the session's playback matches with the expectation. false otherwise. */ + @Override public boolean checkPlaybackActiveState(boolean expected) { if (mPlaybackState == null) { return false; @@ -345,13 +373,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * * @return {@code true} if the playback is local. */ - public boolean isPlaybackLocal() { + @Override + public boolean isPlaybackTypeLocal() { return mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL; } @Override public void binderDied() { - mService.sessionDied(this); + mService.onSessionDied(this); } /** @@ -383,7 +412,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable * @param sequenceId (optional) sequence id. Use this only when a wake lock is needed. * @param cb (optional) result receiver to receive callback. Use this only when a wake lock is * needed. - * @return {@code true} if the attempt to send media button was successfuly. + * @return {@code true} if the attempt to send media button was successfully. * {@code false} otherwise. */ public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, @@ -392,6 +421,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable cb); } + @Override public void dump(PrintWriter pw, String prefix) { pw.println(prefix + mTag + " " + this); @@ -712,7 +742,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable public void destroySession() throws RemoteException { final long token = Binder.clearCallingIdentity(); try { - mService.destroySession(MediaSessionRecord.this); + mService.onSessionDied(MediaSessionRecord.this); } finally { Binder.restoreCallingIdentity(token); } @@ -734,7 +764,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable mIsActive = active; final long token = Binder.clearCallingIdentity(); try { - mService.updateSession(MediaSessionRecord.this); + mService.onSessionActiveStateChanged(MediaSessionRecord.this); } finally { Binder.restoreCallingIdentity(token); } @@ -801,12 +831,16 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, AutoCloseable ? PlaybackState.STATE_NONE : mPlaybackState.getState(); int newState = state == null ? PlaybackState.STATE_NONE : state.getState(); + boolean shouldUpdatePriority = ALWAYS_PRIORITY_STATES.contains(newState) + || (!TRANSITION_PRIORITY_STATES.contains(oldState) + && TRANSITION_PRIORITY_STATES.contains(newState)); synchronized (mLock) { mPlaybackState = state; } final long token = Binder.clearCallingIdentity(); try { - mService.onSessionPlaystateChanged(MediaSessionRecord.this, oldState, newState); + mService.onSessionPlaybackStateChanged( + MediaSessionRecord.this, shouldUpdatePriority); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java new file mode 100644 index 0000000000000..2cde89a7a6f6a --- /dev/null +++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java @@ -0,0 +1,143 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media; + +import android.media.AudioManager; +import android.os.ResultReceiver; +import android.view.KeyEvent; + +import java.io.PrintWriter; + +/** + * Common interfaces between {@link MediaSessionRecord} and {@link MediaSession2Record}. + */ +public interface MediaSessionRecordImpl extends AutoCloseable { + + /** + * Get the info for this session. + * + * @return Info that identifies this session. + */ + String getPackageName(); + + /** + * Get the UID this session was created for. + * + * @return The UID for this session. + */ + int getUid(); + + /** + * Get the user id this session was created for. + * + * @return The user id for this session. + */ + int getUserId(); + + /** + * Check if this session has system priorty and should receive media buttons + * before any other sessions. + * + * @return True if this is a system priority session, false otherwise + */ + boolean isSystemPriority(); + + /** + * Send a volume adjustment to the session owner. Direction must be one of + * {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE}, + * {@link AudioManager#ADJUST_SAME}. + * + * @param packageName The package that made the original volume request. + * @param opPackageName The op package that made the original volume request. + * @param pid The pid that made the original volume request. + * @param uid The uid that made the original volume request. + * @param asSystemService {@code true} if the event sent to the session as if it was come from + * the system service instead of the app process. This helps sessions to distinguish + * between the key injection by the app and key events from the hardware devices. + * Should be used only when the volume key events aren't handled by foreground + * activity. {@code false} otherwise to tell session about the real caller. + * @param direction The direction to adjust volume in. + * @param flags Any of the flags from {@link AudioManager}. + * @param useSuggested True to use adjustSuggestedStreamVolume instead of + */ + void adjustVolume(String packageName, String opPackageName, int pid, int uid, + boolean asSystemService, int direction, int flags, boolean useSuggested); + + /** + * Check if this session has been set to active by the app. (i.e. ready to receive command and + * getters are available). + * + * @return True if the session is active, false otherwise. + */ + // TODO(jaewan): Find better naming, or remove this from the MediaSessionRecordImpl. + boolean isActive(); + + /** + * Check if the session's playback active state matches with the expectation. This always return + * {@code false} if the playback state is unknown (e.g. {@code null}), where we cannot know the + * actual playback state associated with the session. + * + * @param expected True if playback is expected to be active. false otherwise. + * @return True if the session's playback matches with the expectation. false otherwise. + */ + boolean checkPlaybackActiveState(boolean expected); + + /** + * Check whether the playback type is local or remote. + *

+ *

+ * + * @return {@code true} if the playback is local. {@code false} if the playback is remote. + */ + boolean isPlaybackTypeLocal(); + + /** + * Sends media button. + * + * @param packageName caller package name + * @param pid caller pid + * @param uid caller uid + * @param asSystemService {@code true} if the event sent to the session as if it was come from + * the system service instead of the app process. + * @param ke key events + * @param sequenceId (optional) sequence id. Use this only when a wake lock is needed. + * @param cb (optional) result receiver to receive callback. Use this only when a wake lock is + * needed. + * @return {@code true} if the attempt to send media button was successfully. + * {@code false} otherwise. + */ + boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, + KeyEvent ke, int sequenceId, ResultReceiver cb); + + /** + * Dumps internal state + * + * @param pw print writer + * @param prefix prefix + */ + void dump(PrintWriter pw, String prefix); + + /** + * Override {@link AutoCloseable#close} to tell not to throw exception. + */ + @Override + void close(); +} diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 0f059dba52749..f71fb582e3edf 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -40,10 +40,7 @@ import android.media.AudioManager; import android.media.AudioManagerInternal; import android.media.AudioPlaybackConfiguration; import android.media.AudioSystem; -import android.media.IAudioService; import android.media.IRemoteVolumeController; -import android.media.MediaController2; -import android.media.Session2CommandGroup; import android.media.Session2Token; import android.media.session.IActiveSessionsListener; import android.media.session.IOnMediaKeyEventDispatchedListener; @@ -61,7 +58,6 @@ import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Handler; -import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Message; import android.os.PowerManager; @@ -123,11 +119,6 @@ public class MediaSessionService extends SystemService implements Monitor { @GuardedBy("mLock") private final ArrayList mSessionsListeners = new ArrayList(); - // Map user id as index to list of Session2Tokens - // TODO: Keep session2 info in MediaSessionStack for prioritizing both session1 and session2 in - // one place. - @GuardedBy("mLock") - private final SparseArray> mSession2TokensPerUser = new SparseArray<>(); @GuardedBy("mLock") private final List mSession2TokensListenerRecords = new ArrayList<>(); @@ -189,16 +180,11 @@ public class MediaSessionService extends SystemService implements Monitor { updateUser(); } - private IAudioService getAudioService() { - IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); - return IAudioService.Stub.asInterface(b); - } - private boolean isGlobalPriorityActiveLocked() { return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive(); } - void updateSession(MediaSessionRecord record) { + void onSessionActiveStateChanged(MediaSessionRecordImpl record) { synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(record.getUserId()); if (user == null) { @@ -215,12 +201,14 @@ public class MediaSessionService extends SystemService implements Monitor { Log.w(TAG, "Unknown session updated. Ignoring."); return; } - user.mPriorityStack.onSessionStateChange(record); + user.mPriorityStack.onSessionActiveStateChanged(record); } - mHandler.postSessionsChanged(record.getUserId()); + + mHandler.postSessionsChanged(record); } } + // Currently only media1 can become global priority session. void setGlobalPrioritySession(MediaSessionRecord record) { synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(record.getUserId()); @@ -266,11 +254,13 @@ public class MediaSessionService extends SystemService implements Monitor { List getSession2TokensLocked(int userId) { List list = new ArrayList<>(); if (userId == USER_ALL) { - for (int i = 0; i < mSession2TokensPerUser.size(); i++) { - list.addAll(mSession2TokensPerUser.valueAt(i)); + int size = mUserRecords.size(); + for (int i = 0; i < size; i++) { + list.addAll(mUserRecords.valueAt(i).mPriorityStack.getSession2Tokens(userId)); } } else { - list.addAll(mSession2TokensPerUser.get(userId)); + FullUserRecord user = getFullUserRecordLocked(userId); + list.addAll(user.mPriorityStack.getSession2Tokens(userId)); } return list; } @@ -297,14 +287,15 @@ public class MediaSessionService extends SystemService implements Monitor { } } - void onSessionPlaystateChanged(MediaSessionRecord record, int oldState, int newState) { + void onSessionPlaybackStateChanged(MediaSessionRecordImpl record, + boolean shouldUpdatePriority) { synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(record.getUserId()); if (user == null || !user.mPriorityStack.contains(record)) { Log.d(TAG, "Unknown session changed playback state. Ignoring."); return; } - user.mPriorityStack.onPlaystateChanged(record, oldState, newState); + user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority); } } @@ -347,7 +338,6 @@ public class MediaSessionService extends SystemService implements Monitor { user.destroySessionsForUserLocked(userId); } } - mSession2TokensPerUser.remove(userId); updateUser(); } } @@ -366,13 +356,7 @@ public class MediaSessionService extends SystemService implements Monitor { } } - void sessionDied(MediaSessionRecord session) { - synchronized (mLock) { - destroySessionLocked(session); - } - } - - void destroySession(MediaSessionRecord session) { + void onSessionDied(MediaSessionRecordImpl session) { synchronized (mLock) { destroySessionLocked(session); } @@ -393,9 +377,6 @@ public class MediaSessionService extends SystemService implements Monitor { mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id)); } } - if (mSession2TokensPerUser.get(userInfo.id) == null) { - mSession2TokensPerUser.put(userInfo.id, new ArrayList<>()); - } } } // Ensure that the current full user exists. @@ -405,9 +386,6 @@ public class MediaSessionService extends SystemService implements Monitor { Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId); mCurrentFullUserRecord = new FullUserRecord(currentFullUserId); mUserRecords.put(currentFullUserId, mCurrentFullUserRecord); - if (mSession2TokensPerUser.get(currentFullUserId) == null) { - mSession2TokensPerUser.put(currentFullUserId, new ArrayList<>()); - } } mFullUserIds.put(currentFullUserId, currentFullUserId); } @@ -444,7 +422,7 @@ public class MediaSessionService extends SystemService implements Monitor { * 5. We need to unlink to death from the cb binder * 6. We need to tell the session to do any final cleanup (onDestroy) */ - private void destroySessionLocked(MediaSessionRecord session) { + private void destroySessionLocked(MediaSessionRecordImpl session) { if (DEBUG) { Log.d(TAG, "Destroying " + session); } @@ -461,7 +439,7 @@ public class MediaSessionService extends SystemService implements Monitor { } session.close(); - mHandler.postSessionsChanged(session.getUserId()); + mHandler.postSessionsChanged(session); } private void enforcePackageName(String packageName, int uid) { @@ -541,15 +519,6 @@ public class MediaSessionService extends SystemService implements Monitor { return false; } - private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, - String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) - throws RemoteException { - synchronized (mLock) { - return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, - tag, sessionInfo); - } - } - /* * When a session is created the following things need to happen. * 1. Its callback binder needs a link to death @@ -557,29 +526,31 @@ public class MediaSessionService extends SystemService implements Monitor { * 3. It needs to be added to the priority stack. * 4. It needs to be added to the relevant user record. */ - private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId, + private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, String callerPackageName, ISessionCallback cb, String tag, Bundle sessionInfo) { - FullUserRecord user = getFullUserRecordLocked(userId); - if (user == null) { - Log.w(TAG, "Request from invalid user: " + userId + ", pkg=" + callerPackageName); - throw new RuntimeException("Session request from invalid user."); - } + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null) { + Log.w(TAG, "Request from invalid user: " + userId + ", pkg=" + callerPackageName); + throw new RuntimeException("Session request from invalid user."); + } - final MediaSessionRecord session; - try { - session = new MediaSessionRecord(callerPid, callerUid, userId, - callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper()); - } catch (RemoteException e) { - throw new RuntimeException("Media Session owner died prematurely.", e); - } + final MediaSessionRecord session; + try { + session = new MediaSessionRecord(callerPid, callerUid, userId, + callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper()); + } catch (RemoteException e) { + throw new RuntimeException("Media Session owner died prematurely.", e); + } - user.mPriorityStack.addSession(session); - mHandler.postSessionsChanged(userId); + user.mPriorityStack.addSession(session); + mHandler.postSessionsChanged(session); - if (DEBUG) { - Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag); + if (DEBUG) { + Log.d(TAG, "Created session for " + callerPackageName + " with tag " + tag); + } + return session; } - return session; } private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) { @@ -600,16 +571,16 @@ public class MediaSessionService extends SystemService implements Monitor { return -1; } - private void pushSessionsChanged(int userId) { + private void pushSession1Changed(int userId) { synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(userId); if (user == null) { - Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId); + Log.w(TAG, "pushSession1ChangedOnHandler failed. No user with id=" + userId); return; } List records = getActiveSessionsLocked(userId); int size = records.size(); - ArrayList tokens = new ArrayList(); + ArrayList tokens = new ArrayList<>(); for (int i = 0; i < size; i++) { tokens.add(records.get(i).getSessionToken()); } @@ -629,6 +600,27 @@ public class MediaSessionService extends SystemService implements Monitor { } } + void pushSession2Changed(int userId) { + synchronized (mLock) { + List allSession2Tokens = getSession2TokensLocked(USER_ALL); + List session2Tokens = getSession2TokensLocked(userId); + + for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) { + Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i); + try { + if (listenerRecord.userId == USER_ALL) { + listenerRecord.listener.onSession2TokensChanged(allSession2Tokens); + } else if (listenerRecord.userId == userId) { + listenerRecord.listener.onSession2TokensChanged(session2Tokens); + } + } catch (RemoteException e) { + Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e); + mSession2TokensListenerRecords.remove(i); + } + } + } + } + private void pushRemoteVolumeUpdateLocked(int userId) { FullUserRecord user = getFullUserRecordLocked(userId); if (user == null) { @@ -638,8 +630,13 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { int size = mRemoteVolumeControllers.beginBroadcast(); - MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId); - MediaSession.Token token = record == null ? null : record.getSessionToken(); + MediaSessionRecordImpl record = user.mPriorityStack.getDefaultRemoteSession(userId); + if (record instanceof MediaSession2Record) { + // TODO(jaewan): Implement + return; + } + MediaSession.Token token = record == null + ? null : ((MediaSessionRecord) record).getSessionToken(); for (int i = size - 1; i >= 0; i--) { try { @@ -653,34 +650,15 @@ public class MediaSessionService extends SystemService implements Monitor { } } - void pushSession2TokensChangedLocked(int userId) { - List allSession2Tokens = getSession2TokensLocked(USER_ALL); - List session2Tokens = getSession2TokensLocked(userId); - - for (int i = mSession2TokensListenerRecords.size() - 1; i >= 0; i--) { - Session2TokensListenerRecord listenerRecord = mSession2TokensListenerRecords.get(i); - try { - if (listenerRecord.userId == USER_ALL) { - listenerRecord.listener.onSession2TokensChanged(allSession2Tokens); - } else if (listenerRecord.userId == userId) { - listenerRecord.listener.onSession2TokensChanged(session2Tokens); - } - } catch (RemoteException e) { - Log.w(TAG, "Failed to notify Session2Token change. Removing listener.", e); - mSession2TokensListenerRecords.remove(i); - } - } - } - /** * Called when the media button receiver for the {@code record} is changed. * * @param record the media session whose media button receiver is updated. */ - public void onMediaButtonReceiverChanged(MediaSessionRecord record) { + public void onMediaButtonReceiverChanged(MediaSessionRecordImpl record) { synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(record.getUserId()); - MediaSessionRecord mediaButtonSession = + MediaSessionRecordImpl mediaButtonSession = user.mPriorityStack.getMediaButtonSession(); if (record == mediaButtonSession) { user.rememberMediaButtonReceiverLocked(mediaButtonSession); @@ -868,39 +846,34 @@ public class MediaSessionService extends SystemService implements Monitor { pw.println(indent + "Restored MediaButtonReceiverComponentType: " + mRestoredMediaButtonReceiverComponentType); mPriorityStack.dump(pw, indent); - pw.println(indent + "Session2Tokens:"); - for (int i = 0; i < mSession2TokensPerUser.size(); i++) { - List list = mSession2TokensPerUser.valueAt(i); - if (list == null || list.size() == 0) { - continue; - } - for (Session2Token token : list) { - pw.println(indent + " " + token); - } - } } @Override - public void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession, - MediaSessionRecord newMediaButtonSession) { + public void onMediaButtonSessionChanged(MediaSessionRecordImpl oldMediaButtonSession, + MediaSessionRecordImpl newMediaButtonSession) { if (DEBUG_KEY_EVENT) { Log.d(TAG, "Media button session is changed to " + newMediaButtonSession); } synchronized (mLock) { if (oldMediaButtonSession != null) { - mHandler.postSessionsChanged(oldMediaButtonSession.getUserId()); + mHandler.postSessionsChanged(oldMediaButtonSession); } if (newMediaButtonSession != null) { rememberMediaButtonReceiverLocked(newMediaButtonSession); - mHandler.postSessionsChanged(newMediaButtonSession.getUserId()); + mHandler.postSessionsChanged(newMediaButtonSession); } pushAddressedPlayerChangedLocked(); } } // Remember media button receiver and keep it in the persistent storage. - public void rememberMediaButtonReceiverLocked(MediaSessionRecord record) { - PendingIntent receiver = record.getMediaButtonReceiver(); + public void rememberMediaButtonReceiverLocked(MediaSessionRecordImpl record) { + if (record instanceof MediaSession2Record) { + // TODO(jaewan): Implement + return; + } + MediaSessionRecord sessionRecord = (MediaSessionRecord) record; + PendingIntent receiver = sessionRecord.getMediaButtonReceiver(); mLastMediaButtonReceiver = receiver; mRestoredMediaButtonReceiver = null; mRestoredMediaButtonReceiverComponentType = COMPONENT_TYPE_INVALID; @@ -925,10 +898,15 @@ public class MediaSessionService extends SystemService implements Monitor { private void pushAddressedPlayerChangedLocked( IOnMediaKeyEventSessionChangedListener callback) { try { - MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked(); + MediaSessionRecordImpl mediaButtonSession = getMediaButtonSessionLocked(); if (mediaButtonSession != null) { - callback.onMediaKeyEventSessionChanged(mediaButtonSession.getPackageName(), - mediaButtonSession.getSessionToken()); + if (mediaButtonSession instanceof MediaSessionRecord) { + MediaSessionRecord session1 = (MediaSessionRecord) mediaButtonSession; + callback.onMediaKeyEventSessionChanged(session1.getPackageName(), + session1.getSessionToken()); + } else { + // TODO(jaewan): Implement + } } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { callback.onMediaKeyEventSessionChanged( mCurrentFullUserRecord.mLastMediaButtonReceiver @@ -951,7 +929,7 @@ public class MediaSessionService extends SystemService implements Monitor { } } - private MediaSessionRecord getMediaButtonSessionLocked() { + private MediaSessionRecordImpl getMediaButtonSessionLocked() { return isGlobalPriorityActiveLocked() ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession(); } @@ -1132,14 +1110,13 @@ public class MediaSessionService extends SystemService implements Monitor { throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid + " but actually=" + sessionToken.getUid()); } - Controller2Callback callback = new Controller2Callback(sessionToken); - // Note: It's safe not to keep controller here because it wouldn't be GC'ed until - // it's closed. - // TODO: Keep controller as well for better readability - // because the GC behavior isn't straightforward. - MediaController2 controller = new MediaController2.Builder(mContext, sessionToken) - .setControllerCallback(new HandlerExecutor(mHandler), callback) - .build(); + MediaSession2Record record = new MediaSession2Record( + sessionToken, MediaSessionService.this, mHandler.getLooper()); + synchronized (mLock) { + FullUserRecord user = getFullUserRecordLocked(record.getUserId()); + user.mPriorityStack.addSession(record); + } + // Do not immediately notify changes -- do so when framework can dispatch command } finally { Binder.restoreCallingIdentity(token); } @@ -1180,7 +1157,8 @@ public class MediaSessionService extends SystemService implements Monitor { null /* optional packageName */); List result; synchronized (mLock) { - result = getSession2TokensLocked(resolvedUserId); + FullUserRecord user = getFullUserRecordLocked(userId); + result = user.mPriorityStack.getSession2Tokens(resolvedUserId); } return new ParceledListSlice(result); } finally { @@ -2018,7 +1996,7 @@ public class MediaSessionService extends SystemService implements Monitor { private void dispatchAdjustVolumeLocked(String packageName, String opPackageName, int pid, int uid, boolean asSystemService, int suggestedStream, int direction, int flags) { - MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession + MediaSessionRecordImpl session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession(); boolean preferSuggestedStream = false; @@ -2109,7 +2087,13 @@ public class MediaSessionService extends SystemService implements Monitor { private void dispatchMediaKeyEventLocked(String packageName, int pid, int uid, boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) { - MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked(); + if (mCurrentFullUserRecord.getMediaButtonSessionLocked() + instanceof MediaSession2Record) { + // TODO(jaewan): Implement + return; + } + MediaSessionRecord session = + (MediaSessionRecord) mCurrentFullUserRecord.getMediaButtonSessionLocked(); if (session != null) { if (DEBUG_KEY_EVENT) { Log.d(TAG, "Sending " + keyEvent + " to " + session); @@ -2389,15 +2373,19 @@ public class MediaSessionService extends SystemService implements Monitor { } final class MessageHandler extends Handler { - private static final int MSG_SESSIONS_CHANGED = 1; - private static final int MSG_VOLUME_INITIAL_DOWN = 2; + private static final int MSG_SESSIONS_1_CHANGED = 1; + private static final int MSG_SESSIONS_2_CHANGED = 2; + private static final int MSG_VOLUME_INITIAL_DOWN = 3; private final SparseArray mIntegerCache = new SparseArray<>(); @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_SESSIONS_CHANGED: - pushSessionsChanged((int) msg.obj); + case MSG_SESSIONS_1_CHANGED: + pushSession1Changed((int) msg.obj); + break; + case MSG_SESSIONS_2_CHANGED: + pushSession2Changed((int) msg.obj); break; case MSG_VOLUME_INITIAL_DOWN: synchronized (mLock) { @@ -2412,41 +2400,19 @@ public class MediaSessionService extends SystemService implements Monitor { } } - public void postSessionsChanged(int userId) { + public void postSessionsChanged(MediaSessionRecordImpl record) { // Use object instead of the arguments when posting message to remove pending requests. - Integer userIdInteger = mIntegerCache.get(userId); + Integer userIdInteger = mIntegerCache.get(record.getUserId()); if (userIdInteger == null) { - userIdInteger = Integer.valueOf(userId); - mIntegerCache.put(userId, userIdInteger); + userIdInteger = Integer.valueOf(record.getUserId()); + mIntegerCache.put(record.getUserId(), userIdInteger); } - removeMessages(MSG_SESSIONS_CHANGED, userIdInteger); - obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget(); + + int msg = (record instanceof MediaSessionRecord) + ? MSG_SESSIONS_1_CHANGED : MSG_SESSIONS_2_CHANGED; + removeMessages(msg, userIdInteger); + obtainMessage(msg, userIdInteger).sendToTarget(); } } - private class Controller2Callback extends MediaController2.ControllerCallback { - private final Session2Token mToken; - - Controller2Callback(Session2Token token) { - mToken = token; - } - - @Override - public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) { - synchronized (mLock) { - int userId = UserHandle.getUserId(mToken.getUid()); - mSession2TokensPerUser.get(userId).add(mToken); - pushSession2TokensChangedLocked(userId); - } - } - - @Override - public void onDisconnected(MediaController2 controller) { - synchronized (mLock) { - int userId = UserHandle.getUserId(mToken.getUid()); - mSession2TokensPerUser.get(userId).remove(mToken); - pushSession2TokensChangedLocked(userId); - } - } - } } diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index 732563f6e05ef..7bb7cf4b74ada 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -16,8 +16,8 @@ package com.android.server.media; +import android.media.Session2Token; import android.media.session.MediaSession; -import android.media.session.PlaybackState; import android.os.Debug; import android.os.UserHandle; import android.util.IntArray; @@ -45,51 +45,30 @@ class MediaSessionStack { /** * Called when the media button session is changed. */ - void onMediaButtonSessionChanged(MediaSessionRecord oldMediaButtonSession, - MediaSessionRecord newMediaButtonSession); + void onMediaButtonSessionChanged(MediaSessionRecordImpl oldMediaButtonSession, + MediaSessionRecordImpl newMediaButtonSession); } /** - * These are states that usually indicate the user took an action and should - * bump priority regardless of the old state. + * Sorted list of the media sessions */ - private static final int[] ALWAYS_PRIORITY_STATES = { - PlaybackState.STATE_FAST_FORWARDING, - PlaybackState.STATE_REWINDING, - PlaybackState.STATE_SKIPPING_TO_PREVIOUS, - PlaybackState.STATE_SKIPPING_TO_NEXT }; - /** - * These are states that usually indicate the user took an action if they - * were entered from a non-priority state. - */ - private static final int[] TRANSITION_PRIORITY_STATES = { - PlaybackState.STATE_BUFFERING, - PlaybackState.STATE_CONNECTING, - PlaybackState.STATE_PLAYING }; - - /** - * Sorted list of the media sessions. - * The session of which PlaybackState is changed to ALWAYS_PRIORITY_STATES or - * TRANSITION_PRIORITY_STATES comes first. - * @see #shouldUpdatePriority - */ - private final List mSessions = new ArrayList(); + private final List mSessions = new ArrayList<>(); private final AudioPlayerStateMonitor mAudioPlayerStateMonitor; private final OnMediaButtonSessionChangedListener mOnMediaButtonSessionChangedListener; /** * The media button session which receives media key events. - * It could be null if the previous media buttion session is released. + * It could be null if the previous media button session is released. */ - private MediaSessionRecord mMediaButtonSession; + private MediaSessionRecordImpl mMediaButtonSession; - private MediaSessionRecord mCachedVolumeDefault; + private MediaSessionRecordImpl mCachedVolumeDefault; /** * Cache the result of the {@link #getActiveSessions} per user. */ - private final SparseArray> mCachedActiveLists = + private final SparseArray> mCachedActiveLists = new SparseArray<>(); MediaSessionStack(AudioPlayerStateMonitor monitor, OnMediaButtonSessionChangedListener listener) { @@ -102,7 +81,7 @@ class MediaSessionStack { * * @param record The record to add. */ - public void addSession(MediaSessionRecord record) { + public void addSession(MediaSessionRecordImpl record) { mSessions.add(record); clearCache(record.getUserId()); @@ -117,7 +96,7 @@ class MediaSessionStack { * * @param record The record to remove. */ - public void removeSession(MediaSessionRecord record) { + public void removeSession(MediaSessionRecordImpl record) { mSessions.remove(record); if (mMediaButtonSession == record) { // When the media button session is removed, nullify the media button session and do not @@ -131,7 +110,7 @@ class MediaSessionStack { /** * Return if the record exists in the priority tracker. */ - public boolean contains(MediaSessionRecord record) { + public boolean contains(MediaSessionRecordImpl record) { return mSessions.contains(record); } @@ -142,9 +121,12 @@ class MediaSessionStack { * @return the MediaSessionRecord. Can be {@code null} if the session is gone meanwhile. */ public MediaSessionRecord getMediaSessionRecord(MediaSession.Token sessionToken) { - for (MediaSessionRecord record : mSessions) { - if (Objects.equals(record.getSessionToken(), sessionToken)) { - return record; + for (MediaSessionRecordImpl record : mSessions) { + if (record instanceof MediaSessionRecord) { + MediaSessionRecord session1 = (MediaSessionRecord) record; + if (Objects.equals(session1.getSessionToken(), sessionToken)) { + return session1; + } } } return null; @@ -154,15 +136,15 @@ class MediaSessionStack { * Notify the priority tracker that a session's playback state changed. * * @param record The record that changed. - * @param oldState Its old playback state. - * @param newState Its new playback state. + * @param shouldUpdatePriority {@code true} if the record needs to prioritized */ - public void onPlaystateChanged(MediaSessionRecord record, int oldState, int newState) { - if (shouldUpdatePriority(oldState, newState)) { + public void onPlaybackStateChanged( + MediaSessionRecordImpl record, boolean shouldUpdatePriority) { + if (shouldUpdatePriority) { mSessions.remove(record); mSessions.add(0, record); clearCache(record.getUserId()); - } else if (!MediaSession.isActiveState(newState)) { + } else if (record.checkPlaybackActiveState(false)) { // Just clear the volume cache when a state goes inactive mCachedVolumeDefault = null; } @@ -172,7 +154,7 @@ class MediaSessionStack { // In that case, we pick the media session whose PlaybackState matches // the audio playback configuration. if (mMediaButtonSession != null && mMediaButtonSession.getUid() == record.getUid()) { - MediaSessionRecord newMediaButtonSession = + MediaSessionRecordImpl newMediaButtonSession = findMediaButtonSession(mMediaButtonSession.getUid()); if (newMediaButtonSession != mMediaButtonSession) { updateMediaButtonSession(newMediaButtonSession); @@ -185,7 +167,7 @@ class MediaSessionStack { * * @param record The record that changed. */ - public void onSessionStateChange(MediaSessionRecord record) { + public void onSessionActiveStateChanged(MediaSessionRecordImpl record) { // For now just clear the cache. Eventually we'll selectively clear // depending on what changed. clearCache(record.getUserId()); @@ -203,7 +185,7 @@ class MediaSessionStack { } IntArray audioPlaybackUids = mAudioPlayerStateMonitor.getSortedAudioPlaybackClientUids(); for (int i = 0; i < audioPlaybackUids.size(); i++) { - MediaSessionRecord mediaButtonSession = + MediaSessionRecordImpl mediaButtonSession = findMediaButtonSession(audioPlaybackUids.get(i)); if (mediaButtonSession != null) { // Found the media button session. @@ -225,9 +207,9 @@ class MediaSessionStack { * @return The media button session. Returns {@code null} if the app doesn't have a media * session. */ - private MediaSessionRecord findMediaButtonSession(int uid) { - MediaSessionRecord mediaButtonSession = null; - for (MediaSessionRecord session : mSessions) { + private MediaSessionRecordImpl findMediaButtonSession(int uid) { + MediaSessionRecordImpl mediaButtonSession = null; + for (MediaSessionRecordImpl session : mSessions) { if (uid == session.getUid()) { if (session.checkPlaybackActiveState( mAudioPlayerStateMonitor.isPlaybackActive(session.getUid()))) { @@ -253,8 +235,8 @@ class MediaSessionStack { * for all users in this {@link MediaSessionStack}. * @return All the active sessions in priority order. */ - public ArrayList getActiveSessions(int userId) { - ArrayList cachedActiveList = mCachedActiveLists.get(userId); + public List getActiveSessions(int userId) { + List cachedActiveList = mCachedActiveLists.get(userId); if (cachedActiveList == null) { cachedActiveList = getPriorityList(true, userId); mCachedActiveLists.put(userId, cachedActiveList); @@ -262,27 +244,47 @@ class MediaSessionStack { return cachedActiveList; } + /** + * Gets the session2 tokens. + * + * @param userId The user to check. It can be {@link UserHandle#USER_ALL} to get all session2 + * tokens for all users in this {@link MediaSessionStack}. + * @return All session2 tokens. + */ + public List getSession2Tokens(int userId) { + ArrayList session2Records = new ArrayList<>(); + for (MediaSessionRecordImpl record : mSessions) { + if ((userId == UserHandle.USER_ALL || record.getUserId() == userId) + && record.isActive() + && record instanceof MediaSession2Record) { + MediaSession2Record session2 = (MediaSession2Record) record; + session2Records.add(session2.getSession2Token()); + } + } + return session2Records; + } + /** * Get the media button session which receives the media button events. * * @return The media button session or null. */ - public MediaSessionRecord getMediaButtonSession() { + public MediaSessionRecordImpl getMediaButtonSession() { return mMediaButtonSession; } - private void updateMediaButtonSession(MediaSessionRecord newMediaButtonSession) { - MediaSessionRecord oldMediaButtonSession = mMediaButtonSession; + private void updateMediaButtonSession(MediaSessionRecordImpl newMediaButtonSession) { + MediaSessionRecordImpl oldMediaButtonSession = mMediaButtonSession; mMediaButtonSession = newMediaButtonSession; mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged( oldMediaButtonSession, newMediaButtonSession); } - public MediaSessionRecord getDefaultVolumeSession() { + public MediaSessionRecordImpl getDefaultVolumeSession() { if (mCachedVolumeDefault != null) { return mCachedVolumeDefault; } - ArrayList records = getPriorityList(true, UserHandle.USER_ALL); + List records = getPriorityList(true, UserHandle.USER_ALL); int size = records.size(); for (int i = 0; i < size; i++) { MediaSessionRecord record = records.get(i); @@ -294,13 +296,13 @@ class MediaSessionStack { return null; } - public MediaSessionRecord getDefaultRemoteSession(int userId) { - ArrayList records = getPriorityList(true, userId); + public MediaSessionRecordImpl getDefaultRemoteSession(int userId) { + List records = getPriorityList(true, userId); int size = records.size(); for (int i = 0; i < size; i++) { MediaSessionRecord record = records.get(i); - if (!record.isPlaybackLocal()) { + if (!record.isPlaybackTypeLocal()) { return record; } } @@ -308,16 +310,11 @@ class MediaSessionStack { } public void dump(PrintWriter pw, String prefix) { - ArrayList sortedSessions = getPriorityList(false, - UserHandle.USER_ALL); - int count = sortedSessions.size(); pw.println(prefix + "Media button session is " + mMediaButtonSession); - pw.println(prefix + "Sessions Stack - have " + count + " sessions:"); + pw.println(prefix + "Sessions Stack - have " + mSessions.size() + " sessions:"); String indent = prefix + " "; - for (int i = 0; i < count; i++) { - MediaSessionRecord record = sortedSessions.get(i); + for (MediaSessionRecordImpl record : mSessions) { record.dump(pw, indent); - pw.println(); } } @@ -335,17 +332,19 @@ class MediaSessionStack { * will return sessions for all users. * @return The priority sorted list of sessions. */ - public ArrayList getPriorityList(boolean activeOnly, int userId) { - ArrayList result = new ArrayList(); + public List getPriorityList(boolean activeOnly, int userId) { + List result = new ArrayList(); int lastPlaybackActiveIndex = 0; int lastActiveIndex = 0; - int size = mSessions.size(); - for (int i = 0; i < size; i++) { - final MediaSessionRecord session = mSessions.get(i); + for (MediaSessionRecordImpl record : mSessions) { + if (!(record instanceof MediaSessionRecord)) { + continue; + } + final MediaSessionRecord session = (MediaSessionRecord) record; - if (userId != UserHandle.USER_ALL && userId != session.getUserId()) { - // Filter out sessions for the wrong user + if ((userId != UserHandle.USER_ALL && userId != session.getUserId())) { + // Filter out sessions for the wrong user or session2. continue; } @@ -369,26 +368,6 @@ class MediaSessionStack { return result; } - private boolean shouldUpdatePriority(int oldState, int newState) { - if (containsState(newState, ALWAYS_PRIORITY_STATES)) { - return true; - } - if (!containsState(oldState, TRANSITION_PRIORITY_STATES) - && containsState(newState, TRANSITION_PRIORITY_STATES)) { - return true; - } - return false; - } - - private boolean containsState(int state, int[] states) { - for (int i = 0; i < states.length; i++) { - if (states[i] == state) { - return true; - } - } - return false; - } - private void clearCache(int userId) { mCachedVolumeDefault = null; mCachedActiveLists.remove(userId);