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
This commit is contained in:
Jin Seok Park
2020-07-14 21:15:44 +09:00
parent 4cc7111329
commit e1f4d51d55
6 changed files with 56 additions and 34 deletions

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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())) {
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;

View File

@@ -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);