diff options
6 files changed, 146 insertions, 0 deletions
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 6fedcbe50f30..b9255ecaf1b6 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -126,6 +126,7 @@ interface INotificationManager boolean areChannelsBypassingDnd(); ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int uid); ParceledListSlice getPackagesBypassingDnd(int userId); + List<String> getPackagesWithAnyChannels(int userId); boolean isPackagePaused(String pkg); void deleteNotificationHistoryItem(String pkg, int uid, long postedTime); boolean isPermissionFixed(String pkg, int userId); diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index a10b6ff39a37..9d8ab03982e6 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -308,6 +308,16 @@ flag { } flag { + name: "nm_binder_perf_get_apps_with_channels" + namespace: "systemui" + description: "Use a single binder call to get the set of apps with channels for a user" + bug: "362981561" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "no_sbnholder" namespace: "systemui" description: "removes sbnholder from NLS" diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 8242eeb7a3fe..09c8b5ba823e 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -5248,6 +5248,21 @@ public class NotificationManagerService extends SystemService { } @Override + @FlaggedApi(android.app.Flags.FLAG_NM_BINDER_PERF_GET_APPS_WITH_CHANNELS) + public List<String> getPackagesWithAnyChannels(int userId) throws RemoteException { + checkCallerIsSystem(); + UserHandle user = UserHandle.of(userId); + List<String> packages = mPreferencesHelper.getPackagesWithAnyChannels(userId); + for (int i = packages.size() - 1; i >= 0; i--) { + String pkg = packages.get(i); + if (!areNotificationsEnabledForPackage(pkg, getUidForPackageAndUser(pkg, user))) { + packages.remove(i); + } + } + return packages; + } + + @Override public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException { boolean packagesChanged = false; checkCallerIsSystem(); diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index a171ffc2ed98..3974c839fd38 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -2006,6 +2006,29 @@ public class PreferencesHelper implements RankingConfig { } /** + * Gets all apps for this user that have a nonzero number of channels. This count does not + * include deleted channels. + */ + @FlaggedApi(android.app.Flags.FLAG_NM_BINDER_PERF_GET_APPS_WITH_CHANNELS) + public @NonNull List<String> getPackagesWithAnyChannels(@UserIdInt int userId) { + List<String> pkgs = new ArrayList<>(); + synchronized (mLock) { + for (PackagePreferences p : mPackagePreferences.values()) { + if (UserHandle.getUserId(p.uid) != userId) { + continue; + } + for (NotificationChannel c : p.channels.values()) { + if (!c.isDeleted()) { + pkgs.add(p.pkg); + break; + } + } + } + } + return pkgs; + } + + /** * True for pre-O apps that only have the default channel, or pre O apps that have no * channels yet. This method will create the default channel for pre-O apps that don't have it. * Should never be true for O+ targeting apps, but that's enforced on boot/when an app 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 3ec41fd673b1..858dd3a605d8 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -5385,6 +5385,42 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testGetPackagesWithChannels_blocked() throws Exception { + // While we mostly rely on the PreferencesHelper implementation of channels, we filter in + // NMS so that we do not return blocked packages. + // Three packages; all under user 1. + // pkg2 is blocked, but pkg1 and pkg3 are not. + String pkg1 = "com.package.one", pkg2 = "com.package.two", pkg3 = "com.package.three"; + int uid1 = UserHandle.getUid(1, 111); + int uid2 = UserHandle.getUid(1, 222); + int uid3 = UserHandle.getUid(1, 333); + + when(mPackageManager.getPackageUid(eq(pkg1), anyLong(), anyInt())).thenReturn(uid1); + when(mPackageManager.getPackageUid(eq(pkg2), anyLong(), anyInt())).thenReturn(uid2); + when(mPackageManager.getPackageUid(eq(pkg3), anyLong(), anyInt())).thenReturn(uid3); + when(mPermissionHelper.hasPermission(uid1)).thenReturn(true); + when(mPermissionHelper.hasPermission(uid2)).thenReturn(false); + when(mPermissionHelper.hasPermission(uid3)).thenReturn(true); + + NotificationChannel channel1 = new NotificationChannel("id1", "name1", + NotificationManager.IMPORTANCE_DEFAULT); + NotificationChannel channel2 = new NotificationChannel("id3", "name3", + NotificationManager.IMPORTANCE_DEFAULT); + NotificationChannel channel3 = new NotificationChannel("id4", "name3", + NotificationManager.IMPORTANCE_DEFAULT); + mService.mPreferencesHelper.createNotificationChannel(pkg1, uid1, channel1, true, false, + uid1, false); + mService.mPreferencesHelper.createNotificationChannel(pkg2, uid2, channel2, true, false, + uid2, false); + mService.mPreferencesHelper.createNotificationChannel(pkg3, uid3, channel3, true, false, + uid3, false); + + // Output should contain only the package with notification permissions (1, 3). + enableInteractAcrossUsers(); + assertThat(mBinderService.getPackagesWithAnyChannels(1)).containsExactly(pkg1, pkg3); + } + + @Test public void testHasCompanionDevice_failure() throws Exception { when(mCompanionMgr.getAssociations(anyString(), anyInt())).thenThrow( new IllegalArgumentException()); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 3f26cd9258af..640de174ba20 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -3096,6 +3096,67 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + public void getPackagesWithAnyChannels_noChannels() { + assertThat(mHelper.getPackagesWithAnyChannels(UserHandle.getUserId(UID_O))).isEmpty(); + } + + @Test + public void getPackagesWithAnyChannels_someChannels() { + // 2 channels under PKG_N_MR1, 1 under PKG_O + NotificationChannel channel1 = new NotificationChannel("1", "something", + IMPORTANCE_DEFAULT); + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false, UID_N_MR1, + false); + NotificationChannel channel2 = new NotificationChannel("2", "another", IMPORTANCE_DEFAULT); + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, false, UID_N_MR1, + false); + + NotificationChannel other = new NotificationChannel("3", "still another", + IMPORTANCE_DEFAULT); + mHelper.createNotificationChannel(PKG_O, UID_O, other, true, false, UID_O, false); + + assertThat(mHelper.getPackagesWithAnyChannels(USER.getIdentifier())).containsExactly( + PKG_N_MR1, PKG_O); + } + + @Test + public void getPackagesWithAnyChannels_onlyDeleted() { + NotificationChannel channel1 = new NotificationChannel("1", "something", + IMPORTANCE_DEFAULT); + channel1.setDeleted(true); + mHelper.createNotificationChannel(PKG_O, UID_O, channel1, true, false, UID_O, + false); + NotificationChannel channel2 = new NotificationChannel("2", "another", IMPORTANCE_DEFAULT); + channel2.setDeleted(true); + mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false, UID_O, + false); + + assertThat(mHelper.getPackagesWithAnyChannels(UserHandle.getUserId(UID_O))).isEmpty(); + } + + @Test + public void getPackagesWithAnyChannels_distinguishesUsers() throws Exception { + // Set a package up for both users 0 and 10 + String pkgName = "test.package"; + int uid0 = UserHandle.getUid(0, 1234); + int uid10 = UserHandle.getUid(10, 1234); + setUpPackageWithUid(pkgName, uid0); + setUpPackageWithUid(pkgName, uid10); + + // but only user 10 has channels + NotificationChannel channel1 = new NotificationChannel("1", "something", + IMPORTANCE_DEFAULT); + mHelper.createNotificationChannel(pkgName, uid10, channel1, true, false, uid10, + false); + NotificationChannel channel2 = new NotificationChannel("2", "another", IMPORTANCE_DEFAULT); + mHelper.createNotificationChannel(pkgName, uid10, channel2, true, false, uid10, + false); + + assertThat(mHelper.getPackagesWithAnyChannels(0)).isEmpty(); + assertThat(mHelper.getPackagesWithAnyChannels(10)).containsExactly(pkgName); + } + + @Test public void testOnlyHasDefaultChannel() throws Exception { assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1)); assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O)); |