diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index 953f17d862912..c91c39a4293dc 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -5,6 +5,10 @@ package android.app { field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage"; } + public class NotificationManager { + method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle); + } + } package android.content.rollback { diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 0deef53c478fb..a970322601ecf 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -185,6 +185,7 @@ interface INotificationManager List getEnabledNotificationListeners(int userId); ComponentName getAllowedNotificationAssistantForUser(int userId); ComponentName getAllowedNotificationAssistant(); + boolean hasEnabledNotificationListener(String packageName, int userId); @UnsupportedAppUsage int getZenMode(); diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index eef9c022fda84..3f1e5614b90c9 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; @@ -1539,6 +1540,25 @@ public class NotificationManager { } } + /** + * Whether the given user has an enabled + * {@link android.service.notification.NotificationListenerService} with the given package name. + * + * @param packageName the package name of the NotificationListenerService class + * @param userHandle the handle of the user that set the listener + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @SuppressLint("UserHandle") + public boolean hasEnabledNotificationListener(@NonNull String packageName, + @NonNull UserHandle userHandle) { + INotificationManager service = getService(); + try { + return service.hasEnabledNotificationListener(packageName, userHandle.getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } private Context mContext; diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt index f7fbda02a2cb8..c1dbfb7a9be75 100644 --- a/non-updatable-api/module-lib-current.txt +++ b/non-updatable-api/module-lib-current.txt @@ -5,6 +5,10 @@ package android.app { field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage"; } + public class NotificationManager { + method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle); + } + } package android.content.rollback { diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 0eba69ef6348a..9f6c18d5ec728 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -26,8 +26,8 @@ import static com.android.server.media.MediaKeyDispatcher.isSingleTapOverridden; import static com.android.server.media.MediaKeyDispatcher.isTripleTapOverridden; import android.app.ActivityManager; -import android.app.INotificationManager; import android.app.KeyguardManager; +import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ActivityNotFoundException; import android.content.ComponentName; @@ -68,7 +68,6 @@ import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; -import android.os.ServiceManager; import android.os.ShellCallback; import android.os.UserHandle; import android.os.UserManager; @@ -118,7 +117,7 @@ public class MediaSessionService extends SystemService implements Monitor { private final SessionManagerImpl mSessionManagerImpl; private final MessageHandler mHandler = new MessageHandler(); private final PowerManager.WakeLock mMediaEventWakeLock; - private final INotificationManager mNotificationManager; + private final NotificationManager mNotificationManager; private final Object mLock = new Object(); private final HandlerThread mRecordThread = new HandlerThread("SessionRecordThread"); // Keeps the full user id for each user. @@ -158,10 +157,9 @@ public class MediaSessionService extends SystemService implements Monitor { super(context); mContext = context; mSessionManagerImpl = new SessionManagerImpl(); - PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + PowerManager pm = mContext.getSystemService(PowerManager.class); mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent"); - mNotificationManager = INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + mNotificationManager = mContext.getSystemService(NotificationManager.class); } @Override @@ -507,11 +505,12 @@ public class MediaSessionService extends SystemService implements Monitor { private void enforceMediaPermissions(ComponentName compName, int pid, int uid, int resolvedUserId) { if (hasStatusBarServicePermission(pid, uid)) return; + // TODO: Refactor to use hasMediaControlPermission and hasEnabledNotificationListener if (mContext .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) != PackageManager.PERMISSION_GRANTED && !isEnabledNotificationListener(compName, - UserHandle.getUserHandleForUid(uid).getIdentifier(), resolvedUserId)) { + UserHandle.getUserHandleForUid(uid), resolvedUserId)) { throw new SecurityException("Missing permission to control media."); } } @@ -547,13 +546,13 @@ public class MediaSessionService extends SystemService implements Monitor { * they're running as. * * @param compName The component that is enabled. - * @param userId The user id of the caller. + * @param userHandle The user handle of the caller. * @param forUserId The user id they're making the request on behalf of. * @return True if the component is enabled, false otherwise */ - private boolean isEnabledNotificationListener(ComponentName compName, int userId, + private boolean isEnabledNotificationListener(ComponentName compName, UserHandle userHandle, int forUserId) { - if (userId != forUserId) { + if (userHandle.getIdentifier() != forUserId) { // You may not access another user's content as an enabled listener. return false; } @@ -561,12 +560,8 @@ public class MediaSessionService extends SystemService implements Monitor { Log.d(TAG, "Checking if enabled notification listener " + compName); } if (compName != null) { - try { - return mNotificationManager.isNotificationListenerAccessGrantedForUser( - compName, userId); - } catch (RemoteException e) { - Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e); - } + return mNotificationManager.hasEnabledNotificationListener(compName.getPackageName(), + userHandle); } return false; } @@ -1922,8 +1917,8 @@ public class MediaSessionService extends SystemService implements Monitor { * @param controllerUid uid of the controller app */ @Override - public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid) - throws RemoteException { + public boolean isTrusted(String controllerPackageName, int controllerPid, + int controllerUid) { final int uid = Binder.getCallingUid(); final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); final long token = Binder.clearCallingIdentity(); @@ -1937,7 +1932,7 @@ public class MediaSessionService extends SystemService implements Monitor { // Context#getPackageName() for getting package name that matches with the PID/UID, // but it doesn't tell which package has created the MediaController, so useless. return hasMediaControlPermission(controllerPid, controllerUid) - || hasEnabledNotificationListener(userId, controllerPackageName); + || hasEnabledNotificationListener(userId, controllerPackageName, uid); } finally { Binder.restoreCallingIdentity(token); } @@ -2001,28 +1996,20 @@ public class MediaSessionService extends SystemService implements Monitor { return resolvedUserId; } - private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName) - throws RemoteException { + private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName, + int uid) { + // TODO: revisit this checking code // You may not access another user's content as an enabled listener. final int userId = UserHandle.getUserHandleForUid(resolvedUserId).getIdentifier(); if (resolvedUserId != userId) { return false; } - - // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener( - // String pkgName) to notification team for optimization - final List 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 (mNotificationManager.hasEnabledNotificationListener(packageName, + UserHandle.getUserHandleForUid(uid))) { + return true; } if (DEBUG) { - Log.d(TAG, packageName + " (uid=" + resolvedUserId + ") doesn't have an enabled " + Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled " + "notification listener"); } return false; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 88964e055a18e..0d6577daa979b 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4751,6 +4751,12 @@ public class NotificationManagerService extends SystemService { return getAllowedNotificationAssistantForUser(getCallingUserHandle().getIdentifier()); } + @Override + public boolean hasEnabledNotificationListener(String packageName, int userId) { + checkCallerIsSystem(); + return mListeners.isPackageAllowed(packageName, userId); + } + @Override public boolean isNotificationListenerAccessGranted(ComponentName listener) { Objects.requireNonNull(listener);