Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e1f4d51d authored by Jin Seok Park's avatar Jin Seok Park
Browse files

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
parent 4cc71113
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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 {
+1 −0
Original line number Diff line number Diff line
@@ -185,6 +185,7 @@ interface INotificationManager
    List<ComponentName> getEnabledNotificationListeners(int userId);
    ComponentName getAllowedNotificationAssistantForUser(int userId);
    ComponentName getAllowedNotificationAssistant();
    boolean hasEnabledNotificationListener(String packageName, int userId);

    @UnsupportedAppUsage
    int getZenMode();
+20 −0
Original line number Diff line number Diff line
@@ -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;

+4 −0
Original line number Diff line number Diff line
@@ -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 {
+21 −34
Original line number Diff line number Diff line
@@ -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<ComponentName> enabledNotificationListeners =
                    mNotificationManager.getEnabledNotificationListeners(userId);
            if (enabledNotificationListeners != null) {
                for (int i = 0; i < enabledNotificationListeners.size(); i++) {
                    if (TextUtils.equals(packageName,
                            enabledNotificationListeners.get(i).getPackageName())) {
            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;
Loading