diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 9953626f0a7a9..870c1b4a35582 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -146,6 +146,8 @@ public final class MediaSession { * the system but will not be published until {@link #setActive(boolean) * setActive(true)} is called. You must call {@link #release()} when * finished with the session. + *

+ * Note that {@link RuntimeException} will be thrown if an app creates too many sessions. * * @param context The context to use to create the session. * @param tag A short name for debugging purposes. @@ -163,6 +165,8 @@ public final class MediaSession { * The {@code sessionInfo} can include additional unchanging information about this session. * For example, it can include the version of the application, or the list of the custom * commands that this session supports. + *

+ * Note that {@link RuntimeException} will be thrown if an app creates too many sessions. * * @param context The context to use to create the session. * @param tag A short name for debugging purposes. diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 4a6fcdf73b908..a6ad57a7ae3ae 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -103,6 +103,7 @@ public class MediaSessionService extends SystemService implements Monitor { private static final int WAKELOCK_TIMEOUT = 5000; private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000; + private static final int SESSION_CREATION_LIMIT_PER_UID = 100; private final Context mContext; private final SessionManagerImpl mSessionManagerImpl; @@ -428,6 +429,18 @@ public class MediaSessionService extends SystemService implements Monitor { Log.d(TAG, "Destroying " + session); } FullUserRecord user = getFullUserRecordLocked(session.getUserId()); + + if (user != null) { + final int uid = session.getUid(); + final int sessionCount = user.mUidToSessionCount.get(uid, 0); + if (sessionCount <= 0) { + Log.w(TAG, "destroySessionLocked: sessionCount should be positive. " + + "sessionCount=" + sessionCount); + } else { + user.mUidToSessionCount.put(uid, sessionCount - 1); + } + } + if (mGlobalPrioritySession == session) { mGlobalPrioritySession = null; if (session.isActive() && user != null) { @@ -490,6 +503,20 @@ public class MediaSessionService extends SystemService implements Monitor { } } + private boolean hasMediaControlPermission(int pid, int uid) { + // Check if it's system server or has MEDIA_CONTENT_CONTROL. + // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra + // check here. + if (uid == Process.SYSTEM_UID || mContext.checkPermission( + android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) + == PackageManager.PERMISSION_GRANTED) { + return true; + } else if (DEBUG) { + Log.d(TAG, "uid(" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL"); + } + return false; + } + /** * This checks if the component is an enabled notification listener for the * specified user. Enabled components may only operate on behalf of the user @@ -544,6 +571,14 @@ public class MediaSessionService extends SystemService implements Monitor { throw new RuntimeException("Media Session owner died prematurely.", e); } + final int sessionCount = user.mUidToSessionCount.get(callerUid, 0); + if (sessionCount >= SESSION_CREATION_LIMIT_PER_UID + && !hasMediaControlPermission(callerPid, callerUid)) { + throw new RuntimeException("Created too many sessions. count=" + + sessionCount + ")"); + } + user.mUidToSessionCount.put(callerUid, sessionCount + 1); + user.mPriorityStack.addSession(session); mHandler.postSessionsChanged(session); @@ -723,6 +758,7 @@ public class MediaSessionService extends SystemService implements Monitor { mOnMediaKeyEventDispatchedListeners = new HashMap<>(); private final HashMap mOnMediaKeyEventSessionChangedListeners = new HashMap<>(); + private final SparseIntArray mUidToSessionCount = new SparseIntArray(); private PendingIntent mLastMediaButtonReceiver; private ComponentName mRestoredMediaButtonReceiver; @@ -1954,20 +1990,6 @@ public class MediaSessionService extends SystemService implements Monitor { return resolvedUserId; } - private boolean hasMediaControlPermission(int pid, int uid) { - // Check if it's system server or has MEDIA_CONTENT_CONTROL. - // Note that system server doesn't have MEDIA_CONTENT_CONTROL, so we need extra - // check here. - if (uid == Process.SYSTEM_UID || mContext.checkPermission( - android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) - == PackageManager.PERMISSION_GRANTED) { - return true; - } else if (DEBUG) { - Log.d(TAG, "uid(" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL"); - } - return false; - } - private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName) throws RemoteException { // You may not access another user's content as an enabled listener.