From 4e968666fde328b7abdac3a7dd12262f64d14586 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Thu, 21 Oct 2021 10:30:19 -0400 Subject: [PATCH] Check permission when posting notification Test: atest FrameworksUiServicesTests, core tests Bug: 194833441 Change-Id: I77816addeec483f645ac4bb8d8552e8853aaf633 --- .../notification/StatusBarNotification.java | 27 +++++++++ .../StatusBarNotificationTest.java | 23 ++++++++ .../NotificationManagerService.java | 56 +++++++++++-------- .../NotificationManagerServiceTest.java | 43 ++++++++++++-- .../NotificationPermissionMigrationTest.java | 33 +++++++++++ 5 files changed, 153 insertions(+), 29 deletions(-) diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java index 40041486f6a62..66188cd197210 100644 --- a/core/java/android/service/notification/StatusBarNotification.java +++ b/core/java/android/service/notification/StatusBarNotification.java @@ -139,6 +139,33 @@ public class StatusBarNotification implements Parcelable { this.groupKey = groupKey(); } + /** + * @hide + */ + public static int getUidFromKey(@NonNull String key) { + String[] parts = key.split("\\|"); + if (parts.length >= 5) { + try { + int uid = Integer.parseInt(parts[4]); + return uid; + } catch (NumberFormatException e) { + return -1; + } + } + return -1; + } + + /** + * @hide + */ + public static String getPkgFromKey(@NonNull String key) { + String[] parts = key.split("\\|"); + if (parts.length >= 2) { + return parts[1]; + } + return null; + } + private String key() { String sbnKey = user.getIdentifier() + "|" + pkg + "|" + id + "|" + tag + "|" + uid; if (overrideGroupKey != null && getNotification().isGroupSummary()) { diff --git a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java index 109b7ab73d6f1..76c9f88921058 100644 --- a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java +++ b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java @@ -23,6 +23,7 @@ import static junit.framework.Assert.assertNull; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -54,6 +55,7 @@ public class StatusBarNotificationTest { private final Context mMockContext = mock(Context.class); @Mock private Context mRealContext; + @Mock private PackageManager mPm; private static final String PKG = "com.example.o"; @@ -78,6 +80,7 @@ public class StatusBarNotificationTest { InstrumentationRegistry.getContext().getResources()); when(mMockContext.getPackageManager()).thenReturn(mPm); when(mMockContext.getApplicationInfo()).thenReturn(new ApplicationInfo()); + when(mPm.getApplicationLabel(any())).thenReturn(""); mRealContext = InstrumentationRegistry.getContext(); } @@ -217,6 +220,26 @@ public class StatusBarNotificationTest { assertEquals(pkg, resultContext.getPackageName()); } + @Test + public void testGetUidFromKey() { + StatusBarNotification sbn = getNotification("pkg", null, "channel"); + + assertEquals(UID, StatusBarNotification.getUidFromKey(sbn.getKey())); + + sbn.setOverrideGroupKey("addsToKey"); + assertEquals(UID, StatusBarNotification.getUidFromKey(sbn.getKey())); + } + + @Test + public void testGetPkgFromKey() { + StatusBarNotification sbn = getNotification("pkg", null, "channel"); + + assertEquals("pkg", StatusBarNotification.getPkgFromKey(sbn.getKey())); + + sbn.setOverrideGroupKey("addsToKey"); + assertEquals("pkg", StatusBarNotification.getPkgFromKey(sbn.getKey())); + } + private StatusBarNotification getNotification(String pkg, String group, String channelId) { return getNotification(pkg, getNotificationBuilder(group, channelId)); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 95c26d902c94b..f14f769548acc 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3544,11 +3544,7 @@ public class NotificationManagerService extends SystemService { "canNotifyAsPackage for uid " + uid); } - if (mEnableAppSettingMigration) { - return mPermissionHelper.hasPermission(uid); - } else { - return mPreferencesHelper.getImportance(pkg, uid) != IMPORTANCE_NONE; - } + return areNotificationsEnabledForPackageInt(pkg, uid); } /** @@ -6732,13 +6728,30 @@ public class NotificationManagerService extends SystemService { // blocked apps - if (isBlocked(r, mUsageStats)) { + boolean isBlocked = !areNotificationsEnabledForPackageInt(pkg, uid); + synchronized (mNotificationLock) { + isBlocked |= isRecordBlockedLocked(r); + } + if (isBlocked) { + if (DBG) { + Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName() + + " by user request."); + } + mUsageStats.registerBlocked(r); return false; } return true; } + private boolean areNotificationsEnabledForPackageInt(String pkg, int uid) { + if (mEnableAppSettingMigration) { + return mPermissionHelper.hasPermission(uid); + } else { + return mPreferencesHelper.getImportance(pkg, uid) != IMPORTANCE_NONE; + } + } + protected int getNotificationCount(String pkg, int userId, int excludedId, String excludedTag) { int count = 0; @@ -6767,24 +6780,15 @@ public class NotificationManagerService extends SystemService { return count; } - protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) { - if (isBlocked(r)) { - if (DBG) { - Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName() - + " by user request."); - } - usageStats.registerBlocked(r); - return true; - } - return false; - } - - private boolean isBlocked(NotificationRecord r) { + /** + * Checks whether a notification is banned at a group or channel level or if the NAS or system + * has blocked the notification. + */ + @GuardedBy("mNotificationLock") + boolean isRecordBlockedLocked(NotificationRecord r) { final String pkg = r.getSbn().getPackageName(); final int callingUid = r.getSbn().getUid(); return mPreferencesHelper.isGroupBlocked(pkg, callingUid, r.getChannel().getGroup()) - || mPreferencesHelper.getImportance(pkg, callingUid) - == NotificationManager.IMPORTANCE_NONE || r.getImportance() == NotificationManager.IMPORTANCE_NONE; } @@ -7095,6 +7099,9 @@ public class NotificationManagerService extends SystemService { @Override public void run() { + String pkg = StatusBarNotification.getPkgFromKey(key); + int uid = StatusBarNotification.getUidFromKey(key); + boolean appBanned = !areNotificationsEnabledForPackageInt(pkg, uid); synchronized (mNotificationLock) { try { NotificationRecord r = null; @@ -7111,8 +7118,11 @@ public class NotificationManagerService extends SystemService { return; } - if (isBlocked(r)) { - Slog.i(TAG, "notification blocked by assistant request"); + if (appBanned || isRecordBlockedLocked(r)) { + mUsageStats.registerBlocked(r); + if (DBG) { + Slog.e(TAG, "Suppressing notification from package " + pkg); + } return; } 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 2bd237b1e900c..f61d3000f97d5 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -182,7 +182,6 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import android.widget.RemoteViews; -import androidx.annotation.Nullable; import androidx.test.InstrumentationRegistry; import com.android.internal.app.IAppOpsService; @@ -1040,7 +1039,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationRecord r = generateNotificationRecord(channel); // isBlocked is only used for user blocking, not app suspension - assertFalse(mService.isBlocked(r, mUsageStats)); + assertFalse(mService.isRecordBlockedLocked(r)); } @Test @@ -1050,8 +1049,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationChannel channel = new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_NONE); NotificationRecord r = generateNotificationRecord(channel); - assertTrue(mService.isBlocked(r, mUsageStats)); - verify(mUsageStats, times(1)).registerBlocked(eq(r)); + assertTrue(mService.isRecordBlockedLocked(r)); mBinderService.createNotificationChannels( PKG, new ParceledListSlice(Arrays.asList(channel))); @@ -1148,8 +1146,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManager.IMPORTANCE_HIGH); channel.setGroup("something"); NotificationRecord r = generateNotificationRecord(channel); - assertTrue(mService.isBlocked(r, mUsageStats)); - verify(mUsageStats, times(1)).registerBlocked(eq(r)); + assertTrue(mService.isRecordBlockedLocked(r)); } @Test @@ -1286,6 +1283,40 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mUsageStats, never()).registerPostedByApp(any()); } + @Test + public void testBlockedNotifications_blockedByUser() throws Exception { + mService.setPreferencesHelper(mPreferencesHelper); + when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false); + when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); + + NotificationChannel channel = new NotificationChannel("id", "name", + NotificationManager.IMPORTANCE_HIGH); + NotificationRecord r = generateNotificationRecord(channel); + mService.addEnqueuedNotification(r); + + when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(IMPORTANCE_NONE); + + NotificationManagerService.PostNotificationRunnable runnable = + mService.new PostNotificationRunnable(r.getKey()); + runnable.run(); + waitForIdle(); + + verify(mUsageStats).registerBlocked(any()); + verify(mUsageStats, never()).registerPostedByApp(any()); + } + + @Test + public void testEnqueueNotification_appBlocked() throws Exception { + mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "testEnqueueNotification_appBlocked", 0, + generateNotificationRecord(null).getNotification(), 0); + waitForIdle(); + verify(mWorkerHandler, never()).post( + any(NotificationManagerService.EnqueueNotificationRunnable.class)); + } + @Test public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception { mBinderService.enqueueNotificationWithTag(PKG, PKG, diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java index c36d7ad8909e1..3ec87291cc32d 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java @@ -630,4 +630,37 @@ public class NotificationPermissionMigrationTest extends UiServiceTestCase { verify(mPreferencesHelper, never()).getImportance(anyString(), anyInt()); verify(mPreferencesHelper, never()).getNotificationChannelsBypassingDnd(PKG, mUid); } + + @Test + public void testBlockedNotifications_blockedByUser() throws Exception { + when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false); + when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); + + NotificationChannel channel = new NotificationChannel("id", "name", + NotificationManager.IMPORTANCE_HIGH); + NotificationRecord r = generateNotificationRecord(channel); + mService.addEnqueuedNotification(r); + + when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false); + + NotificationManagerService.PostNotificationRunnable runnable = + mService.new PostNotificationRunnable(r.getKey()); + runnable.run(); + waitForIdle(); + + verify(mUsageStats).registerBlocked(any()); + verify(mUsageStats, never()).registerPostedByApp(any()); + } + + @Test + public void testEnqueueNotification_appBlocked() throws Exception { + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "testEnqueueNotification_appBlocked", 0, + generateNotificationRecord(null).getNotification(), 0); + waitForIdle(); + verify(mWorkerHandler, never()).post( + any(NotificationManagerService.EnqueueNotificationRunnable.class)); + } }