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
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 360e44f..b506f17 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -5,6 +5,10 @@
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 0deef53..a970322 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -185,6 +185,7 @@
List<ComponentName> 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 eef9c02..3f1e561 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -20,6 +20,7 @@
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 @@
}
}
+ /**
+ * 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 a5ca196..682c16d 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -5,6 +5,10 @@
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 0eba69e..9f6c18d 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.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.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 @@
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 @@
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 @@
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 @@
* 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 @@
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 @@
* @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 @@
// 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 @@
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<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 (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 88964e0..0d6577d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4752,6 +4752,12 @@
}
@Override
+ public boolean hasEnabledNotificationListener(String packageName, int userId) {
+ checkCallerIsSystem();
+ return mListeners.isPackageAllowed(packageName, userId);
+ }
+
+ @Override
public boolean isNotificationListenerAccessGranted(ComponentName listener) {
Objects.requireNonNull(listener);
checkCallerIsSystemOrSameApp(listener.getPackageName());