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