From e1f4d51d555eac69d9d56ecefafcd4cc65a4c1bd Mon Sep 17 00:00:00 2001 From: Jin Seok Park Date: Tue, 14 Jul 2020 21:15:44 +0900 Subject: [PATCH] Add hasEnabledNotificationListener API This CL adds a system API to NotificationManager for checking whether the NotificationListener for the current package name and user id is enabled or not. This replaces existing implementation which was using binder calls directly to NotificationManagerService. Bug: 160762852 Test: atest CtsMediaHostTestCases:\ android.media.session.cts.MediaSessionManagerHostTest\ #testGetActiveSessions_withSession2 -> MediaSessionManager#getActiveSessions verifies call by checking if the notification listener is currently enabled. Change-Id: Ic730b0abfe08c40171062421b2015be9cc05310a --- api/module-lib-current.txt | 4 ++ .../android/app/INotificationManager.aidl | 1 + .../java/android/app/NotificationManager.java | 20 +++++++ non-updatable-api/module-lib-current.txt | 4 ++ .../server/media/MediaSessionService.java | 55 +++++++------------ .../NotificationManagerService.java | 6 ++ 6 files changed, 56 insertions(+), 34 deletions(-) diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index 360e44ff055c2..b506f17f4c7ef 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 a5ca196ef5b42..682c16dfb354b 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);