diff options
| author | 2017-02-16 17:10:54 +0900 | |
|---|---|---|
| committer | 2017-03-13 12:10:16 +0900 | |
| commit | a7dce19b613b52894d36044ae6e88a57572d79e5 (patch) | |
| tree | 3f952994694ce747a6b703004f92d75e1b2e5481 | |
| parent | b2df16ae1a1b555df5e3360277918424fba370c8 (diff) | |
MediaSessionService: Keep the media sessions per full user
This is the intermediate CL to simplify the media button handling.
With this, it's easier to get the lastly played media app among the
foreground users.
Test: Manual test (Setup a device with multiple users and work profile,
and press media keys)
Change-Id: I240191fdc64a9f1240682f56407bf5de873345d7
| -rw-r--r-- | services/core/java/com/android/server/media/MediaSessionService.java | 572 | ||||
| -rw-r--r-- | services/core/java/com/android/server/media/MediaSessionStack.java | 95 |
2 files changed, 330 insertions, 337 deletions
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 6349f212d084..e6c38afb9db5 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -30,6 +30,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.database.ContentObserver; import android.media.AudioManager; import android.media.AudioManagerInternal; @@ -64,6 +65,7 @@ import android.text.TextUtils; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseIntArray; import android.view.KeyEvent; import android.view.ViewConfiguration; @@ -90,12 +92,11 @@ public class MediaSessionService extends SystemService implements Monitor { private static final int WAKELOCK_TIMEOUT = 5000; private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000; - /* package */final IBinder mICallback = new Binder(); - private final SessionManagerImpl mSessionManagerImpl; - private final MediaSessionStack mPriorityStack; - private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>(); + // Keeps the full user id for each user. + private final SparseIntArray mFullUserIds = new SparseIntArray(); + private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>(); private final ArrayList<SessionsListenerRecord> mSessionsListeners = new ArrayList<SessionsListenerRecord>(); private final Object mLock = new Object(); @@ -108,11 +109,11 @@ public class MediaSessionService extends SystemService implements Monitor { private AudioManagerInternal mAudioManagerInternal; private ContentResolver mContentResolver; private SettingsObserver mSettingsObserver; - private ICallback mCallback; - // List of user IDs running in the foreground. - // Multiple users can be in the foreground if the work profile is on. - private final List<Integer> mCurrentUserIdList = new ArrayList<>(); + // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile) + // It's always not null after the MediaSessionService is started. + private FullUserRecord mCurrentFullUserRecord; + private MediaSessionRecord mGlobalPrioritySession; // Used to notify system UI when remote volume was changed. TODO find a // better way to handle this. @@ -121,7 +122,6 @@ public class MediaSessionService extends SystemService implements Monitor { public MediaSessionService(Context context) { super(context); mSessionManagerImpl = new SessionManagerImpl(); - mPriorityStack = new MediaSessionStack(); PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); mLongPressTimeout = ViewConfiguration.getLongPressTimeout(); @@ -147,14 +147,21 @@ public class MediaSessionService extends SystemService implements Monitor { return IAudioService.Stub.asInterface(b); } + private boolean isGlobalPriorityActiveLocked() { + return mGlobalPrioritySession != null && mGlobalPrioritySession.isActive(); + } + public void updateSession(MediaSessionRecord record) { synchronized (mLock) { - UserRecord user = mUserRecords.get(record.getUserId()); - if (user == null || !user.mSessions.contains(record)) { + FullUserRecord user = getFullUserRecordLocked(record.getUserId()); + if (user == null || !user.mPriorityStack.contains(record)) { Log.d(TAG, "Unknown session updated. Ignoring."); return; } - mPriorityStack.onSessionStateChange(record); + user.mPriorityStack.onSessionStateChange(record); + if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { + mGlobalPrioritySession = record; + } } mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0); } @@ -176,12 +183,12 @@ public class MediaSessionService extends SystemService implements Monitor { public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) { boolean updateSessions = false; synchronized (mLock) { - UserRecord user = mUserRecords.get(record.getUserId()); - if (user == null || !user.mSessions.contains(record)) { + FullUserRecord user = getFullUserRecordLocked(record.getUserId()); + if (user == null || !user.mPriorityStack.contains(record)) { Log.d(TAG, "Unknown session changed playback state. Ignoring."); return; } - updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState); + updateSessions = user.mPriorityStack.onPlaystateChange(record, oldState, newState); } if (updateSessions) { mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0); @@ -190,8 +197,8 @@ public class MediaSessionService extends SystemService implements Monitor { public void onSessionPlaybackTypeChanged(MediaSessionRecord record) { synchronized (mLock) { - UserRecord user = mUserRecords.get(record.getUserId()); - if (user == null || !user.mSessions.contains(record)) { + FullUserRecord user = getFullUserRecordLocked(record.getUserId()); + if (user == null || !user.mPriorityStack.contains(record)) { Log.d(TAG, "Unknown session changed playback type. Ignoring."); return; } @@ -215,9 +222,14 @@ public class MediaSessionService extends SystemService implements Monitor { public void onStopUser(int userId) { if (DEBUG) Log.d(TAG, "onStopUser: " + userId); synchronized (mLock) { - UserRecord user = mUserRecords.get(userId); + FullUserRecord user = getFullUserRecordLocked(userId); if (user != null) { - destroyUserLocked(user); + if (user.mFullUserId == userId) { + user.destroySessionsForUserLocked(UserHandle.USER_ALL); + mUserRecords.remove(userId); + } else { + user.destroySessionsForUserLocked(userId); + } } updateUser(); } @@ -252,24 +264,29 @@ public class MediaSessionService extends SystemService implements Monitor { private void updateUser() { synchronized (mLock) { UserManager manager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); - int currentUser = ActivityManager.getCurrentUser(); - // Include all profiles even though they aren't yet enabled to handle work profile case. - int[] userIds = manager.getProfileIdsWithDisabled(currentUser); - mCurrentUserIdList.clear(); - if (userIds != null && userIds.length > 0) { - for (int userId : userIds) { - mCurrentUserIdList.add(userId); + mFullUserIds.clear(); + List<UserInfo> allUsers = manager.getUsers(); + if (allUsers != null) { + for (UserInfo userInfo : allUsers) { + if (userInfo.isManagedProfile()) { + mFullUserIds.put(userInfo.id, userInfo.profileGroupId); + } else { + mFullUserIds.put(userInfo.id, userInfo.id); + if (mUserRecords.get(userInfo.id) == null) { + mUserRecords.put(userInfo.id, new FullUserRecord(userInfo.id)); + } + } } - } else { - // This shouldn't happen. - Log.w(TAG, "Failed to get enabled profiles."); - mCurrentUserIdList.add(currentUser); } - for (int userId : mCurrentUserIdList) { - if (mUserRecords.get(userId) == null) { - mUserRecords.put(userId, new UserRecord(getContext(), userId)); - } + // Ensure that the current full user exists. + int currentFullUserId = ActivityManager.getCurrentUser(); + mCurrentFullUserRecord = mUserRecords.get(currentFullUserId); + if (mCurrentFullUserRecord == null) { + Log.w(TAG, "Cannot find FullUserInfo for the current user " + currentFullUserId); + mCurrentFullUserRecord = new FullUserRecord(currentFullUserId); + mUserRecords.put(currentFullUserId, mCurrentFullUserRecord); } + mFullUserIds.put(currentFullUserId, currentFullUserId); } } @@ -295,16 +312,6 @@ public class MediaSessionService extends SystemService implements Monitor { } } - /** - * Stop the user and unbind from everything. - * - * @param user The user to dispose of - */ - private void destroyUserLocked(UserRecord user) { - user.destroyLocked(); - mUserRecords.remove(user.mUserId); - } - /* * When a session is removed several things need to happen. * 1. We need to remove it from the relevant user. @@ -319,12 +326,13 @@ public class MediaSessionService extends SystemService implements Monitor { Log.d(TAG, "Destroying " + session); } int userId = session.getUserId(); - UserRecord user = mUserRecords.get(userId); + FullUserRecord user = getFullUserRecordLocked(userId); if (user != null) { user.removeSessionLocked(session); } - - mPriorityStack.removeSession(session); + if (mGlobalPrioritySession == session) { + mGlobalPrioritySession = null; + } try { session.getCallback().asBinder().unlinkToDeath(session, 0); @@ -446,8 +454,7 @@ public class MediaSessionService extends SystemService implements Monitor { */ private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId, String callerPackageName, ISessionCallback cb, String tag) { - - UserRecord user = mUserRecords.get(userId); + FullUserRecord user = getFullUserRecordLocked(userId); if (user == null) { Log.wtf(TAG, "Request from invalid user: " + userId); throw new RuntimeException("Session request from invalid user."); @@ -461,7 +468,6 @@ public class MediaSessionService extends SystemService implements Monitor { throw new RuntimeException("Media Session owner died prematurely.", e); } - mPriorityStack.addSession(session, mCurrentUserIdList.contains(userId)); user.addSessionLocked(session); mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0); @@ -483,12 +489,17 @@ public class MediaSessionService extends SystemService implements Monitor { private void pushSessionsChanged(int userId) { synchronized (mLock) { - List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId); + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null) { + Log.w(TAG, "pushSessionsChanged failed. No user with id=" + userId); + return; + } + List<MediaSessionRecord> records = user.mPriorityStack.getActiveSessions(userId); int size = records.size(); if (size > 0 && records.get(0).isPlaybackActive(false)) { - rememberMediaButtonReceiverLocked(records.get(0)); + user.rememberMediaButtonReceiverLocked(records.get(0)); } - pushAddressedPlayerChangedLocked(); + user.pushAddressedPlayerChangedLocked(); ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>(); for (int i = 0; i < size; i++) { tokens.add(new MediaSession.Token(records.get(i).getControllerBinder())); @@ -512,7 +523,12 @@ public class MediaSessionService extends SystemService implements Monitor { private void pushRemoteVolumeUpdateLocked(int userId) { if (mRvc != null) { try { - MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId); + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null) { + Log.w(TAG, "pushRemoteVolumeUpdateLocked failed. No user with id=" + userId); + return; + } + MediaSessionRecord record = user.mPriorityStack.getDefaultRemoteSession(userId); mRvc.updateRemoteController(record == null ? null : record.getControllerBinder()); } catch (RemoteException e) { Log.wtf(TAG, "Error sending default remote volume to sys ui.", e); @@ -520,66 +536,6 @@ public class MediaSessionService extends SystemService implements Monitor { } } - private MediaSessionRecord getMediaButtonSessionLocked() { - // If we don't have a media button receiver to fall back on - // include non-playing sessions for dispatching. - boolean useNotPlayingSessions = true; - for (int userId : mCurrentUserIdList) { - UserRecord ur = mUserRecords.get(userId); - if (ur.mLastMediaButtonReceiver != null - || ur.mRestoredMediaButtonReceiver != null) { - useNotPlayingSessions = false; - break; - } - } - return mPriorityStack.getDefaultMediaButtonSession( - mCurrentUserIdList, useNotPlayingSessions); - } - - private void pushAddressedPlayerChangedLocked() { - if (mCallback == null) { - return; - } - try { - MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked(); - if (mediaButtonSession != null) { - mCallback.onAddressedPlayerChangedToMediaSession( - new MediaSession.Token(mediaButtonSession.getControllerBinder())); - } else { - for (int userId : mCurrentUserIdList) { - UserRecord user = mUserRecords.get(userId); - if (user.mLastMediaButtonReceiver == null - && user.mRestoredMediaButtonReceiver == null) { - continue; - } - ComponentName componentName = user.mLastMediaButtonReceiver != null - ? user.mLastMediaButtonReceiver.getIntent().getComponent() - : user.mRestoredMediaButtonReceiver; - mCallback.onAddressedPlayerChangedToMediaButtonReceiver(componentName); - return; - } - } - } catch (RemoteException e) { - Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e); - } - } - - // Remember media button receiver and keep it in the persistent storage. - // This should be called whenever there's no media session to receive media button event. - private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) { - PendingIntent receiver = record.getMediaButtonReceiver(); - UserRecord user = mUserRecords.get(record.getUserId()); - if (receiver != null && user != null) { - user.mLastMediaButtonReceiver = receiver; - ComponentName component = receiver.getIntent().getComponent(); - if (component != null && record.getPackageName().equals(component.getPackageName())) { - Settings.Secure.putStringForUser(mContentResolver, - Settings.System.MEDIA_BUTTON_RECEIVER, component.flattenToString(), - record.getUserId()); - } - } - } - private String getCallingPackageName(int uid) { String[] packages = getContext().getPackageManager().getPackagesForUid(uid); if (packages != null && packages.length > 0) { @@ -589,25 +545,36 @@ public class MediaSessionService extends SystemService implements Monitor { } private void dispatchVolumeKeyLongPressLocked(KeyEvent keyEvent) { - // Only consider full user. - UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0)); try { - user.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent); + mCurrentFullUserRecord.mOnVolumeKeyLongPressListener.onVolumeKeyLongPress(keyEvent); } catch (RemoteException e) { Log.w(TAG, "Failed to send " + keyEvent + " to volume key long-press listener"); } } + private FullUserRecord getFullUserRecordLocked(int userId) { + int fullUserId = mFullUserIds.get(userId, -1); + if (fullUserId < 0) { + return null; + } + return mUserRecords.get(fullUserId); + } + /** - * Information about a particular user. The contents of this object is - * guarded by mLock. + * Information about a full user and its corresponding managed profiles. + * + * <p>Since the full user runs together with its managed profiles, a user wouldn't differentiate + * them when he/she presses a media/volume button. So keeping media sessions for them in one + * place makes more sense and increases the readability.</p> + * <p>The contents of this object is guarded by {@link #mLock}. */ - final class UserRecord { - private final int mUserId; - private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); - private final Context mContext; + final class FullUserRecord { + private static final String COMPONENT_NAME_USER_ID_DELIM = ","; + private final int mFullUserId; + private final MediaSessionStack mPriorityStack = new MediaSessionStack(); private PendingIntent mLastMediaButtonReceiver; private ComponentName mRestoredMediaButtonReceiver; + private int mRestoredMediaButtonReceiverUserId; private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener; private int mOnVolumeKeyLongPressListenerUid; @@ -617,64 +584,113 @@ public class MediaSessionService extends SystemService implements Monitor { private IOnMediaKeyListener mOnMediaKeyListener; private int mOnMediaKeyListenerUid; - - public UserRecord(Context context, int userId) { - mContext = context; - mUserId = userId; - restoreMediaButtonReceiver(); + private ICallback mCallback; + + public FullUserRecord(int fullUserId) { + mFullUserId = fullUserId; + // Restore the remembered media button receiver before the boot. + String mediaButtonReceiver = Settings.Secure.getStringForUser(mContentResolver, + Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId); + if (mediaButtonReceiver == null) { + return; + } + String[] tokens = mediaButtonReceiver.split(COMPONENT_NAME_USER_ID_DELIM); + if (tokens == null || tokens.length != 2) { + return; + } + mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]); + mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]); } - public void destroyLocked() { - for (int i = mSessions.size() - 1; i >= 0; i--) { - MediaSessionRecord session = mSessions.get(i); + public void destroySessionsForUserLocked(int userId) { + List<MediaSessionRecord> sessions = mPriorityStack.getPriorityList(false, 0, userId); + for (MediaSessionRecord session : sessions) { MediaSessionService.this.destroySessionLocked(session); } } - public ArrayList<MediaSessionRecord> getSessionsLocked() { - return mSessions; - } - public void addSessionLocked(MediaSessionRecord session) { - mSessions.add(session); + mPriorityStack.addSession(session, + mFullUserId == mFullUserIds.get(session.getUserId())); } public void removeSessionLocked(MediaSessionRecord session) { - mSessions.remove(session); + mPriorityStack.removeSession(session); } public void dumpLocked(PrintWriter pw, String prefix) { - pw.println(prefix + "Record for user " + mUserId); + pw.print(prefix + "Record for full_user=" + mFullUserId); + // Dump managed profile user ids associated with this user. + int size = mFullUserIds.size(); + for (int i = 0; i < size; i++) { + if (mFullUserIds.keyAt(i) != mFullUserIds.valueAt(i) + && mFullUserIds.valueAt(i) == mFullUserId) { + pw.print(", profile_user=" + mFullUserIds.keyAt(i)); + } + } + pw.println(); String indent = prefix + " "; - pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver); - pw.println(indent + "Restored ButtonReceiver:" + mRestoredMediaButtonReceiver); - pw.println(indent + "Volume key long-press listener:" + mOnVolumeKeyLongPressListener); - pw.println(indent + "Volume key long-press listener package:" + + pw.println(indent + "Volume key long-press listener: " + mOnVolumeKeyLongPressListener); + pw.println(indent + "Volume key long-press listener package: " + getCallingPackageName(mOnVolumeKeyLongPressListenerUid)); pw.println(indent + "Media key listener: " + mOnMediaKeyListener); pw.println(indent + "Media key listener package: " + getCallingPackageName(mOnMediaKeyListenerUid)); - int size = mSessions.size(); - pw.println(indent + size + " Sessions:"); - for (int i = 0; i < size; i++) { - // Just print the short version, the full session dump will - // already be in the list of all sessions. - pw.println(indent + mSessions.get(i).toString()); + pw.println(indent + "Callback: " + mCallback); + pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver); + pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver); + mPriorityStack.dump(pw, indent); + } + + // Remember the media button receiver and keep it in the persistent storage. + private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) { + PendingIntent receiver = record.getMediaButtonReceiver(); + if (receiver == null) { + return; + } + mLastMediaButtonReceiver = receiver; + ComponentName component = receiver.getIntent().getComponent(); + if (component != null && record.getPackageName().equals(component.getPackageName())) { + String componentName = component.flattenToString(); + Settings.Secure.putStringForUser(mContentResolver, + Settings.System.MEDIA_BUTTON_RECEIVER, + componentName + COMPONENT_NAME_USER_ID_DELIM + record.getUserId(), + mFullUserId); } } - private void restoreMediaButtonReceiver() { - String receiverName = Settings.Secure.getStringForUser(mContentResolver, - Settings.System.MEDIA_BUTTON_RECEIVER, mUserId); - if (!TextUtils.isEmpty(receiverName)) { - ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName); - if (eventReceiver == null) { - // an invalid name was persisted - return; + private void pushAddressedPlayerChangedLocked() { + if (mCallback == null) { + return; + } + try { + MediaSessionRecord mediaButtonSession = getMediaButtonSessionLocked(); + if (mediaButtonSession != null) { + mCallback.onAddressedPlayerChangedToMediaSession( + new MediaSession.Token(mediaButtonSession.getControllerBinder())); + } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { + mCallback.onAddressedPlayerChangedToMediaButtonReceiver( + mCurrentFullUserRecord.mLastMediaButtonReceiver + .getIntent().getComponent()); + } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { + mCallback.onAddressedPlayerChangedToMediaButtonReceiver( + mCurrentFullUserRecord.mRestoredMediaButtonReceiver); } - mRestoredMediaButtonReceiver = eventReceiver; + } catch (RemoteException e) { + Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e); } } + + private MediaSessionRecord getMediaButtonSessionLocked() { + if (isGlobalPriorityActiveLocked()) { + return mGlobalPrioritySession; + } + // If we don't have a media button receiver to fall back on + // include non-playing sessions for dispatching. + boolean useNotPlayingSessions = (mLastMediaButtonReceiver == null + && mRestoredMediaButtonReceiver == null); + return mPriorityStack.getDefaultMediaButtonSession(useNotPlayingSessions); + } } final class SessionsListenerRecord implements IBinder.DeathRecipient { @@ -759,11 +775,27 @@ public class MediaSessionService extends SystemService implements Monitor { int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid); ArrayList<IBinder> binders = new ArrayList<IBinder>(); synchronized (mLock) { - ArrayList<MediaSessionRecord> records = mPriorityStack - .getActiveSessions(resolvedUserId); - int size = records.size(); - for (int i = 0; i < size; i++) { - binders.add(records.get(i).getControllerBinder().asBinder()); + if (resolvedUserId == UserHandle.USER_ALL) { + int size = mUserRecords.size(); + for (int i = 0; i < size; i++) { + List<MediaSessionRecord> records = + mUserRecords.valueAt(i).mPriorityStack.getActiveSessions( + resolvedUserId); + for (MediaSessionRecord record : records) { + binders.add(record.getControllerBinder().asBinder()); + } + } + } else { + FullUserRecord user = getFullUserRecordLocked(resolvedUserId); + if (user == null) { + Log.w(TAG, "getSessions failed. Unknown user " + userId); + return binders; + } + List<MediaSessionRecord> records = user.mPriorityStack + .getActiveSessions(resolvedUserId); + for (MediaSessionRecord record : records) { + binders.add(record.getControllerBinder().asBinder()); + } } } return binders; @@ -852,7 +884,7 @@ public class MediaSessionService extends SystemService implements Monitor { } synchronized (mLock) { - boolean isGlobalPriorityActive = mPriorityStack.isGlobalPriorityActive(); + boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked(); if (isGlobalPriorityActive && uid != Process.SYSTEM_UID) { // Prevent dispatching key event through reflection while the global // priority session is active. @@ -861,18 +893,17 @@ public class MediaSessionService extends SystemService implements Monitor { return; } if (!isGlobalPriorityActive) { - // Only consider full user. - UserRecord user = mUserRecords.get(mCurrentUserIdList.get(0)); - if (user.mOnMediaKeyListener != null) { + if (mCurrentFullUserRecord.mOnMediaKeyListener != null) { if (DEBUG_KEY_EVENT) { - Log.d(TAG, "Send " + keyEvent + " to media key listener"); + Log.d(TAG, "Send " + keyEvent + " to the media key listener"); } try { - user.mOnMediaKeyListener.onMediaKey(keyEvent, + mCurrentFullUserRecord.mOnMediaKeyListener.onMediaKey(keyEvent, new MediaKeyListenerResultReceiver(keyEvent, needWakeLock)); return; } catch (RemoteException e) { - Log.w(TAG, "Failed to send " + keyEvent + " to media key listener"); + Log.w(TAG, "Failed to send " + keyEvent + + " to the media key listener"); } } } @@ -898,26 +929,33 @@ public class MediaSessionService extends SystemService implements Monitor { + " Callback"); } synchronized (mLock) { - Log.d(TAG, "Callback + " + mCallback + int userId = UserHandle.getUserId(uid); + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null || user.mFullUserId != userId) { + Log.w(TAG, "Only the full user can set the callback" + + ", userId=" + userId); + return; + } + user.mCallback = callback; + Log.d(TAG, "The callback " + user.mCallback + " is set by " + getCallingPackageName(uid)); - mCallback = callback; - if (mCallback == null) { + if (user.mCallback == null) { return; } try { - mCallback.asBinder().linkToDeath( + user.mCallback.asBinder().linkToDeath( new IBinder.DeathRecipient() { @Override public void binderDied() { synchronized (mLock) { - mCallback = null; + user.mCallback = null; } } }, 0); - pushAddressedPlayerChangedLocked(); + user.pushAddressedPlayerChangedLocked(); } catch (RemoteException e) { Log.w(TAG, "Failed to set callback", e); - mCallback = null; + user.mCallback = null; } } } finally { @@ -940,17 +978,26 @@ public class MediaSessionService extends SystemService implements Monitor { } synchronized (mLock) { - UserRecord user = mUserRecords.get(UserHandle.getUserId(uid)); + int userId = UserHandle.getUserId(uid); + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null || user.mFullUserId != userId) { + Log.w(TAG, "Only the full user can set the volume key long-press listener" + + ", userId=" + userId); + return; + } if (user.mOnVolumeKeyLongPressListener != null && user.mOnVolumeKeyLongPressListenerUid != uid) { - Log.w(TAG, "Volume key long-press listener cannot be reset by another app"); + Log.w(TAG, "The volume key long-press listener cannot be reset" + + " by another app , mOnVolumeKeyLongPressListener=" + + user.mOnVolumeKeyLongPressListenerUid + + ", uid=" + uid); return; } user.mOnVolumeKeyLongPressListener = listener; user.mOnVolumeKeyLongPressListenerUid = uid; - Log.d(TAG, "Volume key long-press listener " + Log.d(TAG, "The volume key long-press listener " + listener + " is set by " + getCallingPackageName(uid)); if (user.mOnVolumeKeyLongPressListener != null) { @@ -992,16 +1039,23 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { int userId = UserHandle.getUserId(uid); - UserRecord user = mUserRecords.get(userId); + FullUserRecord user = getFullUserRecordLocked(userId); + if (user == null || user.mFullUserId != userId) { + Log.w(TAG, "Only the full user can set the media key listener" + + ", userId=" + userId); + return; + } if (user.mOnMediaKeyListener != null && user.mOnMediaKeyListenerUid != uid) { - Log.w(TAG, "Media key listener cannot be reset by another app"); + Log.w(TAG, "The media key listener cannot be reset by another app. " + + ", mOnMediaKeyListenerUid=" + user.mOnMediaKeyListenerUid + + ", uid=" + uid); return; } user.mOnMediaKeyListener = listener; user.mOnMediaKeyListenerUid = uid; - Log.d(TAG, "Media key listener " + user.mOnMediaKeyListener + Log.d(TAG, "The media key listener " + user.mOnMediaKeyListener + " is set by " + getCallingPackageName(uid)); if (user.mOnMediaKeyListener != null) { @@ -1060,12 +1114,8 @@ public class MediaSessionService extends SystemService implements Monitor { try { synchronized (mLock) { - // Only consider full user. - int userId = mCurrentUserIdList.get(0); - UserRecord user = mUserRecords.get(userId); - - if (mPriorityStack.isGlobalPriorityActive() - || user.mOnVolumeKeyLongPressListener == null) { + if (isGlobalPriorityActiveLocked() + || mCurrentFullUserRecord.mOnVolumeKeyLongPressListener == null) { dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly); } else { // TODO: Consider the case when both volume up and down keys are pressed @@ -1073,34 +1123,36 @@ public class MediaSessionService extends SystemService implements Monitor { if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) { if (keyEvent.getRepeatCount() == 0) { // Keeps the copy of the KeyEvent because it can be reused. - user.mInitialDownVolumeKeyEvent = KeyEvent.obtain(keyEvent); - user.mInitialDownVolumeStream = stream; - user.mInitialDownMusicOnly = musicOnly; + mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = + KeyEvent.obtain(keyEvent); + mCurrentFullUserRecord.mInitialDownVolumeStream = stream; + mCurrentFullUserRecord.mInitialDownMusicOnly = musicOnly; mHandler.sendMessageDelayed( mHandler.obtainMessage( - MessageHandler.MSG_VOLUME_INITIAL_DOWN, userId, 0), + MessageHandler.MSG_VOLUME_INITIAL_DOWN, + mCurrentFullUserRecord.mFullUserId, 0), mLongPressTimeout); } if (keyEvent.getRepeatCount() > 0 || keyEvent.isLongPress()) { mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN); - if (user.mInitialDownVolumeKeyEvent != null) { + if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null) { dispatchVolumeKeyLongPressLocked( - user.mInitialDownVolumeKeyEvent); + mCurrentFullUserRecord.mInitialDownVolumeKeyEvent); // Mark that the key is already handled. - user.mInitialDownVolumeKeyEvent = null; + mCurrentFullUserRecord.mInitialDownVolumeKeyEvent = null; } dispatchVolumeKeyLongPressLocked(keyEvent); } } else { // if up mHandler.removeMessages(MessageHandler.MSG_VOLUME_INITIAL_DOWN); - if (user.mInitialDownVolumeKeyEvent != null - && user.mInitialDownVolumeKeyEvent.getDownTime() - == keyEvent.getDownTime()) { + if (mCurrentFullUserRecord.mInitialDownVolumeKeyEvent != null + && mCurrentFullUserRecord.mInitialDownVolumeKeyEvent + .getDownTime() == keyEvent.getDownTime()) { // Short-press. Should change volume. dispatchVolumeKeyEventLocked( - user.mInitialDownVolumeKeyEvent, - user.mInitialDownVolumeStream, - user.mInitialDownMusicOnly); + mCurrentFullUserRecord.mInitialDownVolumeKeyEvent, + mCurrentFullUserRecord.mInitialDownVolumeStream, + mCurrentFullUserRecord.mInitialDownMusicOnly); dispatchVolumeKeyEventLocked(keyEvent, stream, musicOnly); } else { dispatchVolumeKeyLongPressLocked(keyEvent); @@ -1185,7 +1237,7 @@ public class MediaSessionService extends SystemService implements Monitor { @Override public boolean isGlobalPriorityActive() { synchronized (mLock) { - return mPriorityStack.isGlobalPriorityActive(); + return isGlobalPriorityActiveLocked(); } } @@ -1204,13 +1256,11 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { pw.println(mSessionsListeners.size() + " sessions listeners."); - mPriorityStack.dump(pw, ""); - + pw.println("Global priority session is " + mGlobalPrioritySession); pw.println("User Records:"); int count = mUserRecords.size(); for (int i = 0; i < count; i++) { - UserRecord user = mUserRecords.get(mUserRecords.keyAt(i)); - user.dumpLocked(pw, ""); + mUserRecords.valueAt(i).dumpLocked(pw, ""); } } } @@ -1235,7 +1285,8 @@ public class MediaSessionService extends SystemService implements Monitor { } private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) { - MediaSessionRecord session = mPriorityStack.getDefaultVolumeSession(mCurrentUserIdList); + MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession + : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession(); boolean preferSuggestedStream = false; if (isValidLocalStreamType(suggestedStream) @@ -1304,7 +1355,7 @@ public class MediaSessionService extends SystemService implements Monitor { } private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock) { - MediaSessionRecord session = getMediaButtonSessionLocked(); + MediaSessionRecord session = mCurrentFullUserRecord.getMediaButtonSessionLocked(); if (session != null) { if (DEBUG_KEY_EVENT) { Log.d(TAG, "Sending " + keyEvent + " to " + session); @@ -1317,68 +1368,65 @@ public class MediaSessionService extends SystemService implements Monitor { needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, mKeyEventReceiver, Process.SYSTEM_UID, getContext().getPackageName()); - if (mCallback != null) { + if (mCurrentFullUserRecord.mCallback != null) { try { - mCallback.onMediaKeyEventDispatchedToMediaSession(keyEvent, + mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession( + keyEvent, new MediaSession.Token(session.getControllerBinder())); } catch (RemoteException e) { Log.w(TAG, "Failed to send callback", e); } } - } else { - // Launch the last PendingIntent we had with priority - for (int userId : mCurrentUserIdList) { - UserRecord user = mUserRecords.get(userId); - if (user.mLastMediaButtonReceiver == null - && user.mRestoredMediaButtonReceiver == null) { - continue; - } - if (needWakeLock) { - mKeyEventReceiver.aquireWakeLockLocked(); - } - Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); - mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); - try { - if (user.mLastMediaButtonReceiver != null) { - PendingIntent receiver = user.mLastMediaButtonReceiver; - if (DEBUG_KEY_EVENT) { - Log.d(TAG, "Sending " + keyEvent - + " to the last known pendingIntent " + receiver); - } - receiver.send(getContext(), - needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, - mediaButtonIntent, mKeyEventReceiver, mHandler); - if (mCallback != null) { - ComponentName componentName = - user.mLastMediaButtonReceiver.getIntent().getComponent(); - if (componentName != null) { - mCallback.onMediaKeyEventDispatchedToMediaButtonReceiver( - keyEvent, componentName); - } - } - } else { - ComponentName receiver = user.mRestoredMediaButtonReceiver; - if (DEBUG_KEY_EVENT) { - Log.d(TAG, "Sending " + keyEvent + " to the restored intent " - + receiver); - } - mediaButtonIntent.setComponent(receiver); - getContext().sendBroadcastAsUser(mediaButtonIntent, - UserHandle.of(userId)); - if (mCallback != null) { - mCallback.onMediaKeyEventDispatchedToMediaButtonReceiver( - keyEvent, receiver); + } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null + || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) { + if (needWakeLock) { + mKeyEventReceiver.aquireWakeLockLocked(); + } + Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); + mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); + try { + if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) { + PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver; + if (DEBUG_KEY_EVENT) { + Log.d(TAG, "Sending " + keyEvent + + " to the last known pendingIntent " + receiver); + } + receiver.send(getContext(), + needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, + mediaButtonIntent, mKeyEventReceiver, mHandler); + if (mCurrentFullUserRecord.mCallback != null) { + ComponentName componentName = mCurrentFullUserRecord + .mLastMediaButtonReceiver.getIntent().getComponent(); + if (componentName != null) { + mCurrentFullUserRecord.mCallback + .onMediaKeyEventDispatchedToMediaButtonReceiver( + keyEvent, componentName); } } - } catch (CanceledException e) { - Log.i(TAG, "Error sending key event to media button receiver " - + user.mLastMediaButtonReceiver, e); - } catch (RemoteException e) { - Log.w(TAG, "Failed to send callback", e); + } else { + ComponentName receiver = + mCurrentFullUserRecord.mRestoredMediaButtonReceiver; + if (DEBUG_KEY_EVENT) { + Log.d(TAG, "Sending " + keyEvent + " to the restored intent " + + receiver); + } + mediaButtonIntent.setComponent(receiver); + getContext().sendBroadcastAsUser(mediaButtonIntent, + UserHandle.of(mCurrentFullUserRecord.mRestoredMediaButtonReceiverUserId)); + if (mCurrentFullUserRecord.mCallback != null) { + mCurrentFullUserRecord.mCallback + .onMediaKeyEventDispatchedToMediaButtonReceiver( + keyEvent, receiver); + } } - return; + } catch (CanceledException e) { + Log.i(TAG, "Error sending key event to media button receiver " + + mCurrentFullUserRecord.mLastMediaButtonReceiver, e); + } catch (RemoteException e) { + Log.w(TAG, "Failed to send callback", e); } + } else { if (DEBUG) { Log.d(TAG, "Sending media key ordered broadcast"); } @@ -1486,7 +1534,7 @@ public class MediaSessionService extends SystemService implements Monitor { mHandled = true; mHandler.removeCallbacks(this); synchronized (mLock) { - if (!mPriorityStack.isGlobalPriorityActive() + if (!isGlobalPriorityActiveLocked() && isVoiceKey(mKeyEvent.getKeyCode())) { handleVoiceKeyEventLocked(mKeyEvent, mNeedWakeLock); } else { @@ -1598,7 +1646,7 @@ public class MediaSessionService extends SystemService implements Monitor { break; case MSG_VOLUME_INITIAL_DOWN: synchronized (mLock) { - UserRecord user = mUserRecords.get((int) msg.arg1); + FullUserRecord user = mUserRecords.get((int) msg.arg1); if (user != null && user.mInitialDownVolumeKeyEvent != null) { dispatchVolumeKeyLongPressLocked(user.mInitialDownVolumeKeyEvent); // Mark that the key is already handled. diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index d8fd6e25ac08..8b8073454f8c 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -30,6 +30,7 @@ import java.util.List; /** * Keeps track of media sessions and their priority for notifications, media * button dispatch, etc. + * <p>This class isn't thread-safe. The caller should take care of the synchronization. */ class MediaSessionStack { /** @@ -52,8 +53,6 @@ class MediaSessionStack { private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>(); - private MediaSessionRecord mGlobalPrioritySession; - // The last record that either entered one of the playing states or was // added. private MediaSessionRecord mLastInterestingRecord; @@ -112,13 +111,17 @@ class MediaSessionStack { */ public void removeSession(MediaSessionRecord record) { mSessions.remove(record); - if (record == mGlobalPrioritySession) { - mGlobalPrioritySession = null; - } clearCache(); } /** + * Return if the record exists in the priority tracker. + */ + public boolean contains(MediaSessionRecord record) { + return mSessions.contains(record); + } + + /** * Notify the priority tracker that a session's state changed. * * @param record The record that changed. @@ -149,9 +152,6 @@ class MediaSessionStack { * @param record The record that changed. */ public void onSessionStateChange(MediaSessionRecord record) { - if ((record.getFlags() & MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) { - mGlobalPrioritySession = record; - } // For now just clear the cache. Eventually we'll selectively clear // depending on what changed. clearCache(); @@ -166,63 +166,24 @@ class MediaSessionStack { */ public ArrayList<MediaSessionRecord> getActiveSessions(int userId) { if (mCachedActiveList == null) { - mCachedActiveList = getPriorityListLocked(true, 0, userId); + mCachedActiveList = getPriorityList(true, 0, userId); } return mCachedActiveList; } /** - * Get the current priority sorted list of active sessions that use - * transport controls. The most important session is at index 0 and the - * least important at size -1. - * - * @param userId The user to check. - * @return All the active sessions that handle transport controls in - * priority order. - */ - public ArrayList<MediaSessionRecord> getTransportControlSessions(int userId) { - if (mCachedTransportControlList == null) { - mCachedTransportControlList = getPriorityListLocked(true, - MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS, userId); - } - return mCachedTransportControlList; - } - - /** - * Get the highest priority active session. - * - * @param userId The user to check. - * @return The current highest priority session or null. - */ - public MediaSessionRecord getDefaultSession(int userId) { - if (mCachedDefault != null) { - return mCachedDefault; - } - ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId); - if (records.size() > 0) { - return records.get(0); - } - return null; - } - - /** * Get the highest priority session that can handle media buttons. * - * @param userIdList The user lists to check. * @param includeNotPlaying Return a non-playing session if nothing else is * available * @return The default media button session or null. */ - public MediaSessionRecord getDefaultMediaButtonSession( - List<Integer> userIdList, boolean includeNotPlaying) { - if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) { - return mGlobalPrioritySession; - } + public MediaSessionRecord getDefaultMediaButtonSession(boolean includeNotPlaying) { if (mCachedButtonReceiver != null) { return mCachedButtonReceiver; } - ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, - MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, userIdList); + ArrayList<MediaSessionRecord> records = getPriorityList(true, + MediaSession.FLAG_HANDLES_MEDIA_BUTTONS, UserHandle.USER_ALL); if (records.size() > 0) { MediaSessionRecord record = records.get(0); if (record.isPlaybackActive(false)) { @@ -247,14 +208,11 @@ class MediaSessionStack { return mCachedButtonReceiver; } - public MediaSessionRecord getDefaultVolumeSession(List<Integer> userIdList) { - if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) { - return mGlobalPrioritySession; - } + public MediaSessionRecord getDefaultVolumeSession() { if (mCachedVolumeDefault != null) { return mCachedVolumeDefault; } - ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userIdList); + ArrayList<MediaSessionRecord> records = getPriorityList(true, 0, UserHandle.USER_ALL); int size = records.size(); for (int i = 0; i < size; i++) { MediaSessionRecord record = records.get(i); @@ -267,7 +225,7 @@ class MediaSessionStack { } public MediaSessionRecord getDefaultRemoteSession(int userId) { - ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId); + ArrayList<MediaSessionRecord> records = getPriorityList(true, 0, userId); int size = records.size(); for (int i = 0; i < size; i++) { @@ -279,15 +237,10 @@ class MediaSessionStack { return null; } - public boolean isGlobalPriorityActive() { - return mGlobalPrioritySession == null ? false : mGlobalPrioritySession.isActive(); - } - public void dump(PrintWriter pw, String prefix) { - ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0, + ArrayList<MediaSessionRecord> sortedSessions = getPriorityList(false, 0, UserHandle.USER_ALL); int count = sortedSessions.size(); - pw.println(prefix + "Global priority session is " + mGlobalPrioritySession); pw.println(prefix + "Sessions Stack - have " + count + " sessions:"); String indent = prefix + " "; for (int i = 0; i < count; i++) { @@ -297,13 +250,6 @@ class MediaSessionStack { } } - private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags, - int userId) { - List<Integer> userIdList = new ArrayList<>(); - userIdList.add(userId); - return getPriorityListLocked(activeOnly, withFlags, userIdList); - } - /** * Get a priority sorted list of sessions. Can filter to only return active * sessions or sessions with specific flags. @@ -312,23 +258,22 @@ class MediaSessionStack { * all sessions. * @param withFlags Only return sessions with all the specified flags set. 0 * returns all sessions. - * @param userIdList The user to get sessions for. {@link UserHandle#USER_ALL} + * @param userId The user to get sessions for. {@link UserHandle#USER_ALL} * will return sessions for all users. * @return The priority sorted list of sessions. */ - private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags, - List<Integer> userIdList) { + public ArrayList<MediaSessionRecord> getPriorityList(boolean activeOnly, int withFlags, + int userId) { ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>(); int lastLocalIndex = 0; int lastActiveIndex = 0; int lastPublishedIndex = 0; - boolean filterUser = !userIdList.contains(UserHandle.USER_ALL); int size = mSessions.size(); for (int i = 0; i < size; i++) { final MediaSessionRecord session = mSessions.get(i); - if (filterUser && !userIdList.contains(session.getUserId())) { + if (userId != UserHandle.USER_ALL && userId != session.getUserId()) { // Filter out sessions for the wrong user continue; } |