Allow delegates to cancel notifs they posted
They still cannot see or cancel notifications that the delegator
app posted
Test: cts
Bug: 134585713
Change-Id: If2065010ea5f65cdc0b531441be9a9989214afb7
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 1432f57..d848796 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5310,7 +5310,8 @@
long identityToken = clearCallingIdentity();
try {
INotificationManager service = mInjector.getNotificationManager();
- service.cancelNotificationWithTag(packageName, id.mTag, id.mId, user.getIdentifier());
+ service.cancelNotificationWithTag(
+ packageName, "android", id.mTag, id.mId, user.getIdentifier());
} catch (RemoteException e) {
/* ignore - local call */
} finally {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index dee8e3b..3f723fc 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -918,7 +918,7 @@
return;
}
try {
- inm.cancelNotificationWithTag(localPackageName, null,
+ inm.cancelNotificationWithTag(localPackageName, "android", null,
localForegroundId, userId);
} catch (RuntimeException e) {
Slog.w(TAG, "Error canceling notification for service", e);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 9a0932f..089b1d2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2378,10 +2378,29 @@
}
@Override
- public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
- checkCallerIsSystemOrSameApp(pkg);
+ public void cancelNotificationWithTag(String pkg, String opPkg, String tag, int id,
+ int userId) {
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
+
+ // ensure opPkg is delegate if does not match pkg
+ resolveNotificationUid(opPkg, pkg, Binder.getCallingUid(), userId);
+
+ // if opPkg is not the same as pkg, make sure the notification given was posted
+ // by opPkg
+ if (!Objects.equals(pkg, opPkg)) {
+ synchronized (mNotificationLock) {
+ // Look for the notification, searching both the posted and enqueued lists.
+ NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
+ if (r != null) {
+ if (!Objects.equals(opPkg, r.sbn.getOpPkg())) {
+ throw new SecurityException(opPkg + " does not have permission to "
+ + "cancel a notification they did not post " + tag + " " + id);
+ }
+ }
+ }
+ }
+
// Don't allow client applications to cancel foreground service notis or autobundled
// summaries.
final int mustNotHaveFlags = isCallingUidSystem() ? 0 :
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 9fc278e..6fe7881 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5683,7 +5683,7 @@
return;
}
try {
- inm.cancelNotificationWithTag("android", null,
+ inm.cancelNotificationWithTag("android", "android", null,
SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, userId);
} catch (RuntimeException e) {
Slog.w(TAG, "Error canceling notification for service", e);
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 7a96f4c..0f11566 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -1065,7 +1065,7 @@
waitForLatch(latch);
// Verify notification is cancelled
- verify(mMockNotificationManager).cancelNotificationWithTag(
+ verify(mMockNotificationManager).cancelNotificationWithTag(anyString(),
anyString(), nullable(String.class), anyInt(), anyInt());
verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
@@ -1943,7 +1943,7 @@
UserHandle.USER_SYSTEM);
waitForLatch(latch);
// Verify notification is cancelled
- verify(mMockNotificationManager).cancelNotificationWithTag(
+ verify(mMockNotificationManager).cancelNotificationWithTag(anyString(),
anyString(), nullable(String.class), anyInt(), anyInt());
verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index b4b1194..677705d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -733,7 +733,7 @@
mService.getNotificationRecord(sbn.getKey()).getImportance());
assertEquals(IMPORTANCE_LOW, mBinderService.getNotificationChannel(
PKG, mContext.getUserId(), PKG, channel.getId()).getImportance());
- mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId());
+ mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", sbn.getId(), sbn.getUserId());
waitForIdle();
update = new NotificationChannel("blockedbyuser", "name", IMPORTANCE_NONE);
@@ -908,7 +908,7 @@
public void testCancelNotificationImmediatelyAfterEnqueue() throws Exception {
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
generateNotificationRecord(null).getNotification(), 0);
- mBinderService.cancelNotificationWithTag(PKG, "tag", 0, 0);
+ mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", 0, 0);
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(PKG);
@@ -923,7 +923,7 @@
waitForIdle();
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
generateNotificationRecord(null).getNotification(), 0);
- mBinderService.cancelNotificationWithTag(PKG, "tag", 0, 0);
+ mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", 0, 0);
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(PKG);
@@ -1207,7 +1207,7 @@
sbn.getNotification().flags = Notification.FLAG_ONGOING_EVENT;
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
- mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId());
+ mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", sbn.getId(), sbn.getUserId());
waitForIdle();
assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
assertEquals(0, mService.getNotificationRecordCount());
@@ -4403,7 +4403,7 @@
nr1.sbn.getKey()).getNotification().isBubbleNotification());
// Remove the bubble
- mBinderService.cancelNotificationWithTag(PKG, "tag", nr1.sbn.getId(),
+ mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", nr1.sbn.getId(),
nr1.sbn.getUserId());
waitForIdle();
@@ -4802,7 +4802,7 @@
assertEquals(1, notifs.length);
assertEquals(1, mService.getNotificationRecordCount());
- mBinderService.cancelNotificationWithTag(PKG, null, nrBubble.sbn.getId(),
+ mBinderService.cancelNotificationWithTag(PKG, PKG, null, nrBubble.sbn.getId(),
nrBubble.sbn.getUserId());
waitForIdle();