Loading media/java/android/media/session/ISessionManager.aidl +4 −3 Original line number Diff line number Diff line Loading @@ -52,12 +52,13 @@ interface ISessionManager { void setOnMediaKeyListener(in IOnMediaKeyListener listener); // MediaSession2 boolean isTrusted(int uid, String packageName); boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid); boolean createSession2(in Bundle sessionToken); void destroySession2(in Bundle sessionToken); List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly); List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly, String packageName); void addSessionTokensListener(in ISessionTokensListener listener, int userId, String packageName); void removeSessionTokensListener(in ISessionTokensListener listener); void removeSessionTokensListener(in ISessionTokensListener listener, String packageName); } media/java/android/media/session/MediaSessionManager.java +11 −7 Original line number Diff line number Diff line Loading @@ -342,16 +342,17 @@ public final class MediaSessionManager { /** * Returns whether the api * * @param uid uid of the app * @param packageName packageName * @param pid pid of the app * @param uid uid of the app * @hide */ public boolean isTrusted(int uid, @NonNull String packageName) { public boolean isTrusted(@NonNull String packageName, int pid, int uid) { if (packageName == null) { return false; } try { return mService.isTrusted(uid, packageName); return mService.isTrusted(packageName, pid, uid); } catch (RemoteException e) { Log.wtf(TAG, "Cannot communicate with the service.", e); } Loading Loading @@ -404,7 +405,8 @@ public final class MediaSessionManager { public List<SessionToken2> getActiveSessionTokens() { try { List<Bundle> bundles = mService.getSessionTokens( /* activeSessionOnly */ true, /* sessionServiceOnly */ false); /* activeSessionOnly */ true, /* sessionServiceOnly */ false, mContext.getPackageName()); return toTokenList(mContext, bundles); } catch (RemoteException e) { Log.wtf(TAG, "Cannot communicate with the service.", e); Loading @@ -426,7 +428,8 @@ public final class MediaSessionManager { public List<SessionToken2> getSessionServiceTokens() { try { List<Bundle> bundles = mService.getSessionTokens( /* activeSessionOnly */ false, /* sessionServiceOnly */ true); /* activeSessionOnly */ false, /* sessionServiceOnly */ true, mContext.getPackageName()); return toTokenList(mContext, bundles); } catch (RemoteException e) { Log.wtf(TAG, "Cannot communicate with the service.", e); Loading @@ -450,7 +453,8 @@ public final class MediaSessionManager { public List<SessionToken2> getAllSessionTokens() { try { List<Bundle> bundles = mService.getSessionTokens( /* activeSessionOnly */ false, /* sessionServiceOnly */ false); /* activeSessionOnly */ false, /* sessionServiceOnly */ false, mContext.getPackageName()); return toTokenList(mContext, bundles); } catch (RemoteException e) { Log.wtf(TAG, "Cannot communicate with the service.", e); Loading Loading @@ -526,7 +530,7 @@ public final class MediaSessionManager { SessionTokensChangedWrapper wrapper = mSessionTokensListener.remove(listener); if (wrapper != null) { try { mService.removeSessionTokensListener(wrapper.mStub); mService.removeSessionTokensListener(wrapper.mStub, mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "Error in removeSessionTokensListener.", e); } finally { Loading services/core/java/com/android/server/media/MediaSessionService.java +128 −75 Original line number Diff line number Diff line Loading @@ -608,7 +608,7 @@ public class MediaSessionService extends SystemService implements Monitor { */ private void enforceMediaPermissions(ComponentName compName, int pid, int uid, int resolvedUserId) { if (isCurrentVolumeController(uid, pid)) return; if (isCurrentVolumeController(pid, uid)) return; if (getContext() .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) != PackageManager.PERMISSION_GRANTED Loading @@ -618,13 +618,13 @@ public class MediaSessionService extends SystemService implements Monitor { } } private boolean isCurrentVolumeController(int uid, int pid) { private boolean isCurrentVolumeController(int pid, int uid) { return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE, pid, uid) == PackageManager.PERMISSION_GRANTED; } private void enforceSystemUiPermission(String action, int pid, int uid) { if (!isCurrentVolumeController(uid, pid)) { if (!isCurrentVolumeController(pid, uid)) { throw new SecurityException("Only system ui may " + action); } } Loading Loading @@ -1501,53 +1501,21 @@ public class MediaSessionService extends SystemService implements Monitor { * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL * permission or an enabled notification listener) * * @param uid uid of the controller app * @param packageName package name of the controller app * @param controllerPackageName package name of the controller app * @param controllerPid pid of the controller app * @param controllerUid uid of the controller app */ @Override public boolean isTrusted(int uid, String packageName) throws RemoteException { public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid) throws RemoteException { final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { int userId = UserHandle.getUserId(uid); // Sanity check whether uid and packageName matches if (uid != mPackageManager.getPackageUid(packageName, 0, userId)) { throw new IllegalArgumentException("uid=" + uid + " and packageName=" + packageName + " doesn't match"); } // 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 || mPackageManager.checkPermission( android.Manifest.permission.MEDIA_CONTENT_CONTROL, packageName, uid) == PackageManager.PERMISSION_GRANTED) { return true; } if (DEBUG) { Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted" + " MEDIA_CONTENT_CONTROL"); } // TODO(jaewan): Add hasEnabledNotificationListener(String pkgName) for // optimization (Post-P) final List<ComponentName> enabledNotificationListeners = mNotificationManager.getEnabledNotificationListeners(userId); if (enabledNotificationListeners != null) { for (int i = 0; i < enabledNotificationListeners.size(); i++) { if (TextUtils.equals(packageName, enabledNotificationListeners.get(i).getPackageName())) { return true; } } } return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName, controllerPid, controllerUid); } finally { Binder.restoreCallingIdentity(token); } if (DEBUG) { Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled notification" + " listener"); } return false; } /** Loading Loading @@ -1614,11 +1582,17 @@ public class MediaSessionService extends SystemService implements Monitor { destroySession2Internal(token); } // TODO(jaewan): Protect this API with permission (b/73226436) // TODO(jaewan): Make this API take userId as an argument (b/73597722) @Override public List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly) throws RemoteException { boolean sessionServiceOnly, String packageName) throws RemoteException { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); List<Bundle> tokens = new ArrayList<>(); try { verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid); synchronized (mLock) { for (Map.Entry<SessionToken2, MediaController2> record : mSessionRecords.entrySet()) { Loading @@ -1631,28 +1605,43 @@ public class MediaSessionService extends SystemService implements Monitor { tokens.add(record.getKey().toBundle()); } } } finally { Binder.restoreCallingIdentity(token); } return tokens; } // TODO(jaewan): Protect this API with permission (b/73226436) // TODO(jaewan): "userId != calling user" needs extra protection (b/73226436) @Override public void addSessionTokensListener(ISessionTokensListener listener, int userId, String packageName) { String packageName) throws RemoteException { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { int resolvedUserId = verifySessionsRequest2(userId, packageName, pid, uid); synchronized (mLock) { final SessionTokensListenerRecord record = new SessionTokensListenerRecord(listener, userId); new SessionTokensListenerRecord(listener, resolvedUserId); try { listener.asBinder().linkToDeath(record, 0); } catch (RemoteException e) { } mSessionTokensListeners.add(record); } } finally { Binder.restoreCallingIdentity(token); } } // TODO(jaewan): Protect this API with permission (b/73226436) // TODO(jaewan): Make this API take userId as an argument (b/73597722) @Override public void removeSessionTokensListener(ISessionTokensListener listener) { public void removeSessionTokensListener(ISessionTokensListener listener, String packageName) throws RemoteException { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid); synchronized (mLock) { IBinder listenerBinder = listener.asBinder(); for (SessionTokensListenerRecord record : mSessionTokensListeners) { Loading @@ -1666,8 +1655,12 @@ public class MediaSessionService extends SystemService implements Monitor { } } } } finally { Binder.restoreCallingIdentity(token); } } // For MediaSession private int verifySessionsRequest(ComponentName componentName, int userId, final int pid, final int uid) { String packageName = null; Loading @@ -1687,6 +1680,66 @@ public class MediaSessionService extends SystemService implements Monitor { return resolvedUserId; } // For MediaSession2 private int verifySessionsRequest2(int targetUserId, String callerPackageName, int callerPid, int callerUid) throws RemoteException { // Check that they can make calls on behalf of the user and get the final user id. int resolvedUserId = ActivityManager.handleIncomingUser(callerPid, callerUid, targetUserId, true /* allowAll */, true /* requireFull */, "getSessionTokens", callerPackageName); // Check if they have the permissions or their component is // enabled for the user they're calling from. if (!hasMediaControlPermission( resolvedUserId, callerPackageName, callerPid, callerUid)) { throw new SecurityException("Missing permission to control media."); } return resolvedUserId; } // For MediaSession2 private boolean hasMediaControlPermission(int resolvedUserId, String packageName, int pid, int uid) throws RemoteException { // Allow API calls from the System UI if (isCurrentVolumeController(pid, uid)) { return true; } // 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 || getContext().checkPermission( android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) == PackageManager.PERMISSION_GRANTED) { return true; } else if (DEBUG) { Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL"); } // You may not access another user's content as an enabled listener. final int userId = UserHandle.getUserId(uid); if (resolvedUserId != userId) { return false; } // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener( // String pkgName) to notification team for optimization final List<ComponentName> enabledNotificationListeners = mNotificationManager.getEnabledNotificationListeners(userId); if (enabledNotificationListeners != null) { for (int i = 0; i < enabledNotificationListeners.size(); i++) { if (TextUtils.equals(packageName, enabledNotificationListeners.get(i).getPackageName())) { return true; } } } if (DEBUG) { Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled " + "notification listener"); } return false; } private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) { MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession(); Loading Loading
media/java/android/media/session/ISessionManager.aidl +4 −3 Original line number Diff line number Diff line Loading @@ -52,12 +52,13 @@ interface ISessionManager { void setOnMediaKeyListener(in IOnMediaKeyListener listener); // MediaSession2 boolean isTrusted(int uid, String packageName); boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid); boolean createSession2(in Bundle sessionToken); void destroySession2(in Bundle sessionToken); List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly); List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly, String packageName); void addSessionTokensListener(in ISessionTokensListener listener, int userId, String packageName); void removeSessionTokensListener(in ISessionTokensListener listener); void removeSessionTokensListener(in ISessionTokensListener listener, String packageName); }
media/java/android/media/session/MediaSessionManager.java +11 −7 Original line number Diff line number Diff line Loading @@ -342,16 +342,17 @@ public final class MediaSessionManager { /** * Returns whether the api * * @param uid uid of the app * @param packageName packageName * @param pid pid of the app * @param uid uid of the app * @hide */ public boolean isTrusted(int uid, @NonNull String packageName) { public boolean isTrusted(@NonNull String packageName, int pid, int uid) { if (packageName == null) { return false; } try { return mService.isTrusted(uid, packageName); return mService.isTrusted(packageName, pid, uid); } catch (RemoteException e) { Log.wtf(TAG, "Cannot communicate with the service.", e); } Loading Loading @@ -404,7 +405,8 @@ public final class MediaSessionManager { public List<SessionToken2> getActiveSessionTokens() { try { List<Bundle> bundles = mService.getSessionTokens( /* activeSessionOnly */ true, /* sessionServiceOnly */ false); /* activeSessionOnly */ true, /* sessionServiceOnly */ false, mContext.getPackageName()); return toTokenList(mContext, bundles); } catch (RemoteException e) { Log.wtf(TAG, "Cannot communicate with the service.", e); Loading @@ -426,7 +428,8 @@ public final class MediaSessionManager { public List<SessionToken2> getSessionServiceTokens() { try { List<Bundle> bundles = mService.getSessionTokens( /* activeSessionOnly */ false, /* sessionServiceOnly */ true); /* activeSessionOnly */ false, /* sessionServiceOnly */ true, mContext.getPackageName()); return toTokenList(mContext, bundles); } catch (RemoteException e) { Log.wtf(TAG, "Cannot communicate with the service.", e); Loading @@ -450,7 +453,8 @@ public final class MediaSessionManager { public List<SessionToken2> getAllSessionTokens() { try { List<Bundle> bundles = mService.getSessionTokens( /* activeSessionOnly */ false, /* sessionServiceOnly */ false); /* activeSessionOnly */ false, /* sessionServiceOnly */ false, mContext.getPackageName()); return toTokenList(mContext, bundles); } catch (RemoteException e) { Log.wtf(TAG, "Cannot communicate with the service.", e); Loading Loading @@ -526,7 +530,7 @@ public final class MediaSessionManager { SessionTokensChangedWrapper wrapper = mSessionTokensListener.remove(listener); if (wrapper != null) { try { mService.removeSessionTokensListener(wrapper.mStub); mService.removeSessionTokensListener(wrapper.mStub, mContext.getPackageName()); } catch (RemoteException e) { Log.e(TAG, "Error in removeSessionTokensListener.", e); } finally { Loading
services/core/java/com/android/server/media/MediaSessionService.java +128 −75 Original line number Diff line number Diff line Loading @@ -608,7 +608,7 @@ public class MediaSessionService extends SystemService implements Monitor { */ private void enforceMediaPermissions(ComponentName compName, int pid, int uid, int resolvedUserId) { if (isCurrentVolumeController(uid, pid)) return; if (isCurrentVolumeController(pid, uid)) return; if (getContext() .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) != PackageManager.PERMISSION_GRANTED Loading @@ -618,13 +618,13 @@ public class MediaSessionService extends SystemService implements Monitor { } } private boolean isCurrentVolumeController(int uid, int pid) { private boolean isCurrentVolumeController(int pid, int uid) { return getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE, pid, uid) == PackageManager.PERMISSION_GRANTED; } private void enforceSystemUiPermission(String action, int pid, int uid) { if (!isCurrentVolumeController(uid, pid)) { if (!isCurrentVolumeController(pid, uid)) { throw new SecurityException("Only system ui may " + action); } } Loading Loading @@ -1501,53 +1501,21 @@ public class MediaSessionService extends SystemService implements Monitor { * Returns if the controller's package is trusted (i.e. has either MEDIA_CONTENT_CONTROL * permission or an enabled notification listener) * * @param uid uid of the controller app * @param packageName package name of the controller app * @param controllerPackageName package name of the controller app * @param controllerPid pid of the controller app * @param controllerUid uid of the controller app */ @Override public boolean isTrusted(int uid, String packageName) throws RemoteException { public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid) throws RemoteException { final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { int userId = UserHandle.getUserId(uid); // Sanity check whether uid and packageName matches if (uid != mPackageManager.getPackageUid(packageName, 0, userId)) { throw new IllegalArgumentException("uid=" + uid + " and packageName=" + packageName + " doesn't match"); } // 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 || mPackageManager.checkPermission( android.Manifest.permission.MEDIA_CONTENT_CONTROL, packageName, uid) == PackageManager.PERMISSION_GRANTED) { return true; } if (DEBUG) { Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted" + " MEDIA_CONTENT_CONTROL"); } // TODO(jaewan): Add hasEnabledNotificationListener(String pkgName) for // optimization (Post-P) final List<ComponentName> enabledNotificationListeners = mNotificationManager.getEnabledNotificationListeners(userId); if (enabledNotificationListeners != null) { for (int i = 0; i < enabledNotificationListeners.size(); i++) { if (TextUtils.equals(packageName, enabledNotificationListeners.get(i).getPackageName())) { return true; } } } return hasMediaControlPermission(UserHandle.getUserId(uid), controllerPackageName, controllerPid, controllerUid); } finally { Binder.restoreCallingIdentity(token); } if (DEBUG) { Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled notification" + " listener"); } return false; } /** Loading Loading @@ -1614,11 +1582,17 @@ public class MediaSessionService extends SystemService implements Monitor { destroySession2Internal(token); } // TODO(jaewan): Protect this API with permission (b/73226436) // TODO(jaewan): Make this API take userId as an argument (b/73597722) @Override public List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly) throws RemoteException { boolean sessionServiceOnly, String packageName) throws RemoteException { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); List<Bundle> tokens = new ArrayList<>(); try { verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid); synchronized (mLock) { for (Map.Entry<SessionToken2, MediaController2> record : mSessionRecords.entrySet()) { Loading @@ -1631,28 +1605,43 @@ public class MediaSessionService extends SystemService implements Monitor { tokens.add(record.getKey().toBundle()); } } } finally { Binder.restoreCallingIdentity(token); } return tokens; } // TODO(jaewan): Protect this API with permission (b/73226436) // TODO(jaewan): "userId != calling user" needs extra protection (b/73226436) @Override public void addSessionTokensListener(ISessionTokensListener listener, int userId, String packageName) { String packageName) throws RemoteException { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { int resolvedUserId = verifySessionsRequest2(userId, packageName, pid, uid); synchronized (mLock) { final SessionTokensListenerRecord record = new SessionTokensListenerRecord(listener, userId); new SessionTokensListenerRecord(listener, resolvedUserId); try { listener.asBinder().linkToDeath(record, 0); } catch (RemoteException e) { } mSessionTokensListeners.add(record); } } finally { Binder.restoreCallingIdentity(token); } } // TODO(jaewan): Protect this API with permission (b/73226436) // TODO(jaewan): Make this API take userId as an argument (b/73597722) @Override public void removeSessionTokensListener(ISessionTokensListener listener) { public void removeSessionTokensListener(ISessionTokensListener listener, String packageName) throws RemoteException { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { verifySessionsRequest2(UserHandle.getUserId(uid), packageName, pid, uid); synchronized (mLock) { IBinder listenerBinder = listener.asBinder(); for (SessionTokensListenerRecord record : mSessionTokensListeners) { Loading @@ -1666,8 +1655,12 @@ public class MediaSessionService extends SystemService implements Monitor { } } } } finally { Binder.restoreCallingIdentity(token); } } // For MediaSession private int verifySessionsRequest(ComponentName componentName, int userId, final int pid, final int uid) { String packageName = null; Loading @@ -1687,6 +1680,66 @@ public class MediaSessionService extends SystemService implements Monitor { return resolvedUserId; } // For MediaSession2 private int verifySessionsRequest2(int targetUserId, String callerPackageName, int callerPid, int callerUid) throws RemoteException { // Check that they can make calls on behalf of the user and get the final user id. int resolvedUserId = ActivityManager.handleIncomingUser(callerPid, callerUid, targetUserId, true /* allowAll */, true /* requireFull */, "getSessionTokens", callerPackageName); // Check if they have the permissions or their component is // enabled for the user they're calling from. if (!hasMediaControlPermission( resolvedUserId, callerPackageName, callerPid, callerUid)) { throw new SecurityException("Missing permission to control media."); } return resolvedUserId; } // For MediaSession2 private boolean hasMediaControlPermission(int resolvedUserId, String packageName, int pid, int uid) throws RemoteException { // Allow API calls from the System UI if (isCurrentVolumeController(pid, uid)) { return true; } // 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 || getContext().checkPermission( android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) == PackageManager.PERMISSION_GRANTED) { return true; } else if (DEBUG) { Log.d(TAG, packageName + " (uid=" + uid + ") hasn't granted MEDIA_CONTENT_CONTROL"); } // You may not access another user's content as an enabled listener. final int userId = UserHandle.getUserId(uid); if (resolvedUserId != userId) { return false; } // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener( // String pkgName) to notification team for optimization final List<ComponentName> enabledNotificationListeners = mNotificationManager.getEnabledNotificationListeners(userId); if (enabledNotificationListeners != null) { for (int i = 0; i < enabledNotificationListeners.size(); i++) { if (TextUtils.equals(packageName, enabledNotificationListeners.get(i).getPackageName())) { return true; } } } if (DEBUG) { Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled " + "notification listener"); } return false; } private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags) { MediaSessionRecord session = isGlobalPriorityActiveLocked() ? mGlobalPrioritySession : mCurrentFullUserRecord.mPriorityStack.getDefaultVolumeSession(); Loading