Loading services/core/java/com/android/server/notification/NotificationManagerService.java +76 −93 Original line number Diff line number Diff line Loading @@ -309,6 +309,7 @@ import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.stream.Collectors; /** {@hide} */ public class NotificationManagerService extends SystemService { Loading Loading @@ -1528,10 +1529,10 @@ public class NotificationManagerService extends SystemService { cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0, !queryRestart, changeUserId, reason, null); } } else if (hideNotifications) { hideNotificationsForPackages(pkgList); } else if (unhideNotifications) { unhideNotificationsForPackages(pkgList); } else if (hideNotifications && uidList != null && (uidList.length > 0)) { hideNotificationsForPackages(pkgList, uidList); } else if (unhideNotifications && uidList != null && (uidList.length > 0)) { unhideNotificationsForPackages(pkgList, uidList); } } Loading Loading @@ -2076,6 +2077,68 @@ public class NotificationManagerService extends SystemService { mMsgPkgsAllowedAsConvos = Set.of(getStringArrayResource( com.android.internal.R.array.config_notificationMsgPkgsAllowedAsConvos)); mStatsManager = statsManager; // register for various Intents. // If this is called within a test, make sure to unregister the intent receivers by // calling onDestroy() IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); filter.addAction(Intent.ACTION_USER_PRESENT); filter.addAction(Intent.ACTION_USER_STOPPED); filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_USER_ADDED); filter.addAction(Intent.ACTION_USER_REMOVED); filter.addAction(Intent.ACTION_USER_UNLOCKED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null); IntentFilter pkgFilter = new IntentFilter(); pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); pkgFilter.addDataScheme("package"); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null, null); IntentFilter suspendedPkgFilter = new IntentFilter(); suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); suspendedPkgFilter.addAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, suspendedPkgFilter, null, null); IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null, null); IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT); timeoutFilter.addDataScheme(SCHEME_TIMEOUT); getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter); IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED); getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter); IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED); getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter); } /** * Cleanup broadcast receivers change listeners. */ public void onDestroy() { getContext().unregisterReceiver(mIntentReceiver); getContext().unregisterReceiver(mPackageIntentReceiver); getContext().unregisterReceiver(mNotificationTimeoutReceiver); getContext().unregisterReceiver(mRestoreReceiver); getContext().unregisterReceiver(mLocaleChangeReceiver); if (mDeviceConfigChangedListener != null) { DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener); } } protected String[] getStringArrayResource(int key) { Loading Loading @@ -2126,51 +2189,6 @@ public class NotificationManagerService extends SystemService { Context.STATS_MANAGER), getContext().getSystemService(TelephonyManager.class)); // register for various Intents IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); filter.addAction(Intent.ACTION_USER_PRESENT); filter.addAction(Intent.ACTION_USER_STOPPED); filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_USER_ADDED); filter.addAction(Intent.ACTION_USER_REMOVED); filter.addAction(Intent.ACTION_USER_UNLOCKED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null); IntentFilter pkgFilter = new IntentFilter(); pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); pkgFilter.addDataScheme("package"); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null, null); IntentFilter suspendedPkgFilter = new IntentFilter(); suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); suspendedPkgFilter.addAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, suspendedPkgFilter, null, null); IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null, null); IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT); timeoutFilter.addDataScheme(SCHEME_TIMEOUT); getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter); IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED); getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter); IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED); getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter); publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL); publishLocalService(NotificationManagerInternal.class, mInternalService); Loading @@ -2193,12 +2211,6 @@ public class NotificationManagerService extends SystemService { mDeviceConfigChangedListener); } void unregisterDeviceConfigChange() { if (mDeviceConfigChangedListener != null) { DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener); } } private void registerNotificationPreferencesPullers() { mPullAtomCallback = new StatsPullAtomCallbackImpl(); mStatsManager.setPullAtomCallback( Loading Loading @@ -8339,15 +8351,16 @@ public class NotificationManagerService extends SystemService { return -1; } @VisibleForTesting protected void hideNotificationsForPackages(String[] pkgs) { private void hideNotificationsForPackages(@NonNull String[] pkgs, @NonNull int[] uidList) { synchronized (mNotificationLock) { Set<Integer> uidSet = Arrays.stream(uidList).boxed().collect(Collectors.toSet()); List<String> pkgList = Arrays.asList(pkgs); List<NotificationRecord> changedNotifications = new ArrayList<>(); int numNotifications = mNotificationList.size(); for (int i = 0; i < numNotifications; i++) { NotificationRecord rec = mNotificationList.get(i); if (pkgList.contains(rec.getSbn().getPackageName())) { if (pkgList.contains(rec.getSbn().getPackageName()) && uidSet.contains(rec.getUid())) { rec.setHidden(true); changedNotifications.add(rec); } Loading @@ -8357,15 +8370,17 @@ public class NotificationManagerService extends SystemService { } } @VisibleForTesting protected void unhideNotificationsForPackages(String[] pkgs) { private void unhideNotificationsForPackages(@NonNull String[] pkgs, @NonNull int[] uidList) { synchronized (mNotificationLock) { Set<Integer> uidSet = Arrays.stream(uidList).boxed().collect(Collectors.toSet()); List<String> pkgList = Arrays.asList(pkgs); List<NotificationRecord> changedNotifications = new ArrayList<>(); int numNotifications = mNotificationList.size(); for (int i = 0; i < numNotifications; i++) { NotificationRecord rec = mNotificationList.get(i); if (pkgList.contains(rec.getSbn().getPackageName())) { if (pkgList.contains(rec.getSbn().getPackageName()) && uidSet.contains(rec.getUid())) { rec.setHidden(false); changedNotifications.add(rec); } Loading Loading @@ -9939,38 +9954,6 @@ public class NotificationManagerService extends SystemService { return CollectionUtils.firstOrNull(allowedComponents); } @VisibleForTesting protected void simulatePackageSuspendBroadcast(boolean suspend, String pkg) { checkCallerIsSystemOrShell(); // only use for testing: mimic receive broadcast that package is (un)suspended // but does not actually (un)suspend the package final Bundle extras = new Bundle(); extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[]{pkg}); final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED : Intent.ACTION_PACKAGES_UNSUSPENDED; final Intent intent = new Intent(action); intent.putExtras(extras); mPackageIntentReceiver.onReceive(getContext(), intent); } @VisibleForTesting protected void simulatePackageDistractionBroadcast(int flag, String[] pkgs) { checkCallerIsSystemOrShell(); // only use for testing: mimic receive broadcast that package is (un)distracting // but does not actually register that info with packagemanager final Bundle extras = new Bundle(); extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgs); extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, flag); final Intent intent = new Intent(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED); intent.putExtras(extras); mPackageIntentReceiver.onReceive(getContext(), intent); } /** * Wrapper for a StatusBarNotification object that allows transfer across a oneway * binder without sending large amounts of data over a oneway transaction. Loading services/core/java/com/android/server/notification/NotificationShellCmd.java +0 −21 Original line number Diff line number Diff line Loading @@ -66,8 +66,6 @@ public class NotificationShellCmd extends ShellCommand { + " set_dnd [on|none (same as on)|priority|alarms|all|off (same as all)]" + " allow_dnd PACKAGE [user_id (current user if not specified)]\n" + " disallow_dnd PACKAGE [user_id (current user if not specified)]\n" + " suspend_package PACKAGE\n" + " unsuspend_package PACKAGE\n" + " reset_assistant_user_set [user_id (current user if not specified)]\n" + " get_approved_assistant [user_id (current user if not specified)]\n" + " post [--help | flags] TAG TEXT\n" Loading Loading @@ -258,25 +256,6 @@ public class NotificationShellCmd extends ShellCommand { mBinderService.setNotificationAssistantAccessGrantedForUser(cn, userId, false); } break; case "suspend_package": { // only use for testing mDirectService.simulatePackageSuspendBroadcast(true, getNextArgRequired()); } break; case "unsuspend_package": { // only use for testing mDirectService.simulatePackageSuspendBroadcast(false, getNextArgRequired()); } break; case "distract_package": { // only use for testing // Flag values are in // {@link android.content.pm.PackageManager.DistractionRestriction}. mDirectService.simulatePackageDistractionBroadcast( Integer.parseInt(getNextArgRequired()), getNextArgRequired().split(",")); break; } case "reset_assistant_user_set": { int userId = ActivityManager.getCurrentUser(); if (peekNextArg() != null) { Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +101 −16 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; Loading Loading @@ -102,10 +103,12 @@ import android.app.StatsManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.usage.UsageStatsManagerInternal; import android.companion.ICompanionDeviceManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; Loading Loading @@ -282,6 +285,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationHistoryManager mHistoryManager; @Mock StatsManager mStatsManager; BroadcastReceiver mPackageIntentReceiver; NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake(); private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake( 1 << 30); Loading Loading @@ -480,6 +484,28 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mShortcutHelper.setLauncherApps(mLauncherApps); mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal); // Capture PackageIntentReceiver ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); ArgumentCaptor<IntentFilter> intentFilterCaptor = ArgumentCaptor.forClass(IntentFilter.class); verify(mContext, atLeastOnce()).registerReceiverAsUser(broadcastReceiverCaptor.capture(), any(), intentFilterCaptor.capture(), any(), any()); List<BroadcastReceiver> broadcastReceivers = broadcastReceiverCaptor.getAllValues(); List<IntentFilter> intentFilters = intentFilterCaptor.getAllValues(); for (int i = 0; i < intentFilters.size(); i++) { final IntentFilter filter = intentFilters.get(i); if (filter.hasAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED) && filter.hasAction(Intent.ACTION_PACKAGES_UNSUSPENDED) && filter.hasAction(Intent.ACTION_PACKAGES_SUSPENDED)) { mPackageIntentReceiver = broadcastReceivers.get(i); break; } } assertNotNull("package intent receiver should exist", mPackageIntentReceiver); // Pretend the shortcut exists List<ShortcutInfo> shortcutInfos = new ArrayList<>(); ShortcutInfo info = mock(ShortcutInfo.class); Loading Loading @@ -526,7 +552,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void tearDown() throws Exception { if (mFile != null) mFile.delete(); clearDeviceConfig(); mService.unregisterDeviceConfigChange(); try { mService.onDestroy(); } catch (IllegalStateException e) { // can throw if a broadcast receiver was never registered } InstrumentationRegistry.getInstrumentation() .getUiAutomation().dropShellPermissionIdentity(); } Loading Loading @@ -2533,10 +2565,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testHasCompanionDevice_noService() { mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger, NotificationManagerService noManService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger, mNotificationInstanceIdSequence); assertFalse(mService.hasCompanionDevice(mListener)); assertFalse(noManService.hasCompanionDevice(mListener)); } @Test Loading Loading @@ -4347,13 +4380,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(notif2); // on broadcast, hide the 2 notifications mService.simulatePackageSuspendBroadcast(true, PKG); simulatePackageSuspendBroadcast(true, PKG, notif1.getUid()); ArgumentCaptor<List> captorHide = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture()); assertEquals(2, captorHide.getValue().size()); // on broadcast, unhide the 2 notifications mService.simulatePackageSuspendBroadcast(false, PKG); simulatePackageSuspendBroadcast(false, PKG, notif1.getUid()); ArgumentCaptor<List> captorUnhide = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture()); assertEquals(2, captorUnhide.getValue().size()); Loading @@ -4370,7 +4403,24 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(notif2); // on broadcast, nothing is hidden since no notifications are of package "test_package" mService.simulatePackageSuspendBroadcast(true, "test_package"); simulatePackageSuspendBroadcast(true, "test_package", notif1.getUid()); ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyHiddenLocked(captor.capture()); assertEquals(0, captor.getValue().size()); } @Test public void testNotificationFromDifferentUserHidden() { // post 2 notification from this package final NotificationRecord notif1 = generateNotificationRecord( mTestNotificationChannel, 1, null, true); final NotificationRecord notif2 = generateNotificationRecord( mTestNotificationChannel, 2, null, false); mService.addNotification(notif1); mService.addNotification(notif2); // on broadcast, nothing is hidden since no notifications are of user 10 with package PKG simulatePackageSuspendBroadcast(true, PKG, 10); ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyHiddenLocked(captor.capture()); assertEquals(0, captor.getValue().size()); Loading @@ -4387,16 +4437,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(pkgB); // on broadcast, hide one of the packages mService.simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a"}); simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a"}, new int[] {1000}); ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture()); assertEquals(1, captorHide.getValue().size()); assertEquals("a", captorHide.getValue().get(0).getSbn().getPackageName()); // on broadcast, unhide the package mService.simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a"}); simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a"}, new int[] {1000}); ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture()); assertEquals(1, captorUnhide.getValue().size()); Loading @@ -4414,8 +4466,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(pkgB); // on broadcast, hide one of the packages mService.simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a", "b"}); simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a", "b"}, new int[] {1000, 1001}); ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class); // should be called only once. Loading @@ -4425,8 +4478,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals("b", captorHide.getValue().get(1).getSbn().getPackageName()); // on broadcast, unhide the package mService.simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a", "b"}); simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a", "b"}, new int[] {1000, 1001}); ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class); // should be called only once. Loading @@ -4444,8 +4498,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(notif1); // on broadcast, nothing is hidden since no notifications are of package "test_package" mService.simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"test_package"}); simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"test_package"}, new int[]{notif1.getUid()}); ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyHiddenLocked(captor.capture()); assertEquals(0, captor.getValue().size()); Loading Loading @@ -7011,4 +7066,34 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertTrue(mService.isVisibleToListener(sbn, info)); } private void simulatePackageSuspendBroadcast(boolean suspend, String pkg, int uid) { // mimics receive broadcast that package is (un)suspended // but does not actually (un)suspend the package final Bundle extras = new Bundle(); extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[]{pkg}); extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, new int[]{uid}); final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED : Intent.ACTION_PACKAGES_UNSUSPENDED; final Intent intent = new Intent(action); intent.putExtras(extras); mPackageIntentReceiver.onReceive(getContext(), intent); } private void simulatePackageDistractionBroadcast(int flag, String[] pkgs, int[] uids) { // mimics receive broadcast that package is (un)distracting // but does not actually register that info with packagemanager final Bundle extras = new Bundle(); extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgs); extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, flag); extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uids); final Intent intent = new Intent(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED); intent.putExtras(extras); mPackageIntentReceiver.onReceive(getContext(), intent); } } Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +76 −93 Original line number Diff line number Diff line Loading @@ -309,6 +309,7 @@ import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.stream.Collectors; /** {@hide} */ public class NotificationManagerService extends SystemService { Loading Loading @@ -1528,10 +1529,10 @@ public class NotificationManagerService extends SystemService { cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0, !queryRestart, changeUserId, reason, null); } } else if (hideNotifications) { hideNotificationsForPackages(pkgList); } else if (unhideNotifications) { unhideNotificationsForPackages(pkgList); } else if (hideNotifications && uidList != null && (uidList.length > 0)) { hideNotificationsForPackages(pkgList, uidList); } else if (unhideNotifications && uidList != null && (uidList.length > 0)) { unhideNotificationsForPackages(pkgList, uidList); } } Loading Loading @@ -2076,6 +2077,68 @@ public class NotificationManagerService extends SystemService { mMsgPkgsAllowedAsConvos = Set.of(getStringArrayResource( com.android.internal.R.array.config_notificationMsgPkgsAllowedAsConvos)); mStatsManager = statsManager; // register for various Intents. // If this is called within a test, make sure to unregister the intent receivers by // calling onDestroy() IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); filter.addAction(Intent.ACTION_USER_PRESENT); filter.addAction(Intent.ACTION_USER_STOPPED); filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_USER_ADDED); filter.addAction(Intent.ACTION_USER_REMOVED); filter.addAction(Intent.ACTION_USER_UNLOCKED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null); IntentFilter pkgFilter = new IntentFilter(); pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); pkgFilter.addDataScheme("package"); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null, null); IntentFilter suspendedPkgFilter = new IntentFilter(); suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); suspendedPkgFilter.addAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, suspendedPkgFilter, null, null); IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null, null); IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT); timeoutFilter.addDataScheme(SCHEME_TIMEOUT); getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter); IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED); getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter); IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED); getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter); } /** * Cleanup broadcast receivers change listeners. */ public void onDestroy() { getContext().unregisterReceiver(mIntentReceiver); getContext().unregisterReceiver(mPackageIntentReceiver); getContext().unregisterReceiver(mNotificationTimeoutReceiver); getContext().unregisterReceiver(mRestoreReceiver); getContext().unregisterReceiver(mLocaleChangeReceiver); if (mDeviceConfigChangedListener != null) { DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener); } } protected String[] getStringArrayResource(int key) { Loading Loading @@ -2126,51 +2189,6 @@ public class NotificationManagerService extends SystemService { Context.STATS_MANAGER), getContext().getSystemService(TelephonyManager.class)); // register for various Intents IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); filter.addAction(Intent.ACTION_USER_PRESENT); filter.addAction(Intent.ACTION_USER_STOPPED); filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_USER_ADDED); filter.addAction(Intent.ACTION_USER_REMOVED); filter.addAction(Intent.ACTION_USER_UNLOCKED); filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null); IntentFilter pkgFilter = new IntentFilter(); pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); pkgFilter.addDataScheme("package"); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null, null); IntentFilter suspendedPkgFilter = new IntentFilter(); suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); suspendedPkgFilter.addAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, suspendedPkgFilter, null, null); IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null, null); IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT); timeoutFilter.addDataScheme(SCHEME_TIMEOUT); getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter); IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED); getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter); IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED); getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter); publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL); publishLocalService(NotificationManagerInternal.class, mInternalService); Loading @@ -2193,12 +2211,6 @@ public class NotificationManagerService extends SystemService { mDeviceConfigChangedListener); } void unregisterDeviceConfigChange() { if (mDeviceConfigChangedListener != null) { DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener); } } private void registerNotificationPreferencesPullers() { mPullAtomCallback = new StatsPullAtomCallbackImpl(); mStatsManager.setPullAtomCallback( Loading Loading @@ -8339,15 +8351,16 @@ public class NotificationManagerService extends SystemService { return -1; } @VisibleForTesting protected void hideNotificationsForPackages(String[] pkgs) { private void hideNotificationsForPackages(@NonNull String[] pkgs, @NonNull int[] uidList) { synchronized (mNotificationLock) { Set<Integer> uidSet = Arrays.stream(uidList).boxed().collect(Collectors.toSet()); List<String> pkgList = Arrays.asList(pkgs); List<NotificationRecord> changedNotifications = new ArrayList<>(); int numNotifications = mNotificationList.size(); for (int i = 0; i < numNotifications; i++) { NotificationRecord rec = mNotificationList.get(i); if (pkgList.contains(rec.getSbn().getPackageName())) { if (pkgList.contains(rec.getSbn().getPackageName()) && uidSet.contains(rec.getUid())) { rec.setHidden(true); changedNotifications.add(rec); } Loading @@ -8357,15 +8370,17 @@ public class NotificationManagerService extends SystemService { } } @VisibleForTesting protected void unhideNotificationsForPackages(String[] pkgs) { private void unhideNotificationsForPackages(@NonNull String[] pkgs, @NonNull int[] uidList) { synchronized (mNotificationLock) { Set<Integer> uidSet = Arrays.stream(uidList).boxed().collect(Collectors.toSet()); List<String> pkgList = Arrays.asList(pkgs); List<NotificationRecord> changedNotifications = new ArrayList<>(); int numNotifications = mNotificationList.size(); for (int i = 0; i < numNotifications; i++) { NotificationRecord rec = mNotificationList.get(i); if (pkgList.contains(rec.getSbn().getPackageName())) { if (pkgList.contains(rec.getSbn().getPackageName()) && uidSet.contains(rec.getUid())) { rec.setHidden(false); changedNotifications.add(rec); } Loading Loading @@ -9939,38 +9954,6 @@ public class NotificationManagerService extends SystemService { return CollectionUtils.firstOrNull(allowedComponents); } @VisibleForTesting protected void simulatePackageSuspendBroadcast(boolean suspend, String pkg) { checkCallerIsSystemOrShell(); // only use for testing: mimic receive broadcast that package is (un)suspended // but does not actually (un)suspend the package final Bundle extras = new Bundle(); extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[]{pkg}); final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED : Intent.ACTION_PACKAGES_UNSUSPENDED; final Intent intent = new Intent(action); intent.putExtras(extras); mPackageIntentReceiver.onReceive(getContext(), intent); } @VisibleForTesting protected void simulatePackageDistractionBroadcast(int flag, String[] pkgs) { checkCallerIsSystemOrShell(); // only use for testing: mimic receive broadcast that package is (un)distracting // but does not actually register that info with packagemanager final Bundle extras = new Bundle(); extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgs); extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, flag); final Intent intent = new Intent(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED); intent.putExtras(extras); mPackageIntentReceiver.onReceive(getContext(), intent); } /** * Wrapper for a StatusBarNotification object that allows transfer across a oneway * binder without sending large amounts of data over a oneway transaction. Loading
services/core/java/com/android/server/notification/NotificationShellCmd.java +0 −21 Original line number Diff line number Diff line Loading @@ -66,8 +66,6 @@ public class NotificationShellCmd extends ShellCommand { + " set_dnd [on|none (same as on)|priority|alarms|all|off (same as all)]" + " allow_dnd PACKAGE [user_id (current user if not specified)]\n" + " disallow_dnd PACKAGE [user_id (current user if not specified)]\n" + " suspend_package PACKAGE\n" + " unsuspend_package PACKAGE\n" + " reset_assistant_user_set [user_id (current user if not specified)]\n" + " get_approved_assistant [user_id (current user if not specified)]\n" + " post [--help | flags] TAG TEXT\n" Loading Loading @@ -258,25 +256,6 @@ public class NotificationShellCmd extends ShellCommand { mBinderService.setNotificationAssistantAccessGrantedForUser(cn, userId, false); } break; case "suspend_package": { // only use for testing mDirectService.simulatePackageSuspendBroadcast(true, getNextArgRequired()); } break; case "unsuspend_package": { // only use for testing mDirectService.simulatePackageSuspendBroadcast(false, getNextArgRequired()); } break; case "distract_package": { // only use for testing // Flag values are in // {@link android.content.pm.PackageManager.DistractionRestriction}. mDirectService.simulatePackageDistractionBroadcast( Integer.parseInt(getNextArgRequired()), getNextArgRequired().split(",")); break; } case "reset_assistant_user_set": { int userId = ActivityManager.getCurrentUser(); if (peekNextArg() != null) { Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +101 −16 Original line number Diff line number Diff line Loading @@ -69,6 +69,7 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; Loading Loading @@ -102,10 +103,12 @@ import android.app.StatsManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.usage.UsageStatsManagerInternal; import android.companion.ICompanionDeviceManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; Loading Loading @@ -282,6 +285,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationHistoryManager mHistoryManager; @Mock StatsManager mStatsManager; BroadcastReceiver mPackageIntentReceiver; NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake(); private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake( 1 << 30); Loading Loading @@ -480,6 +484,28 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mShortcutHelper.setLauncherApps(mLauncherApps); mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal); // Capture PackageIntentReceiver ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); ArgumentCaptor<IntentFilter> intentFilterCaptor = ArgumentCaptor.forClass(IntentFilter.class); verify(mContext, atLeastOnce()).registerReceiverAsUser(broadcastReceiverCaptor.capture(), any(), intentFilterCaptor.capture(), any(), any()); List<BroadcastReceiver> broadcastReceivers = broadcastReceiverCaptor.getAllValues(); List<IntentFilter> intentFilters = intentFilterCaptor.getAllValues(); for (int i = 0; i < intentFilters.size(); i++) { final IntentFilter filter = intentFilters.get(i); if (filter.hasAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED) && filter.hasAction(Intent.ACTION_PACKAGES_UNSUSPENDED) && filter.hasAction(Intent.ACTION_PACKAGES_SUSPENDED)) { mPackageIntentReceiver = broadcastReceivers.get(i); break; } } assertNotNull("package intent receiver should exist", mPackageIntentReceiver); // Pretend the shortcut exists List<ShortcutInfo> shortcutInfos = new ArrayList<>(); ShortcutInfo info = mock(ShortcutInfo.class); Loading Loading @@ -526,7 +552,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void tearDown() throws Exception { if (mFile != null) mFile.delete(); clearDeviceConfig(); mService.unregisterDeviceConfigChange(); try { mService.onDestroy(); } catch (IllegalStateException e) { // can throw if a broadcast receiver was never registered } InstrumentationRegistry.getInstrumentation() .getUiAutomation().dropShellPermissionIdentity(); } Loading Loading @@ -2533,10 +2565,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testHasCompanionDevice_noService() { mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger, NotificationManagerService noManService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger, mNotificationInstanceIdSequence); assertFalse(mService.hasCompanionDevice(mListener)); assertFalse(noManService.hasCompanionDevice(mListener)); } @Test Loading Loading @@ -4347,13 +4380,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(notif2); // on broadcast, hide the 2 notifications mService.simulatePackageSuspendBroadcast(true, PKG); simulatePackageSuspendBroadcast(true, PKG, notif1.getUid()); ArgumentCaptor<List> captorHide = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture()); assertEquals(2, captorHide.getValue().size()); // on broadcast, unhide the 2 notifications mService.simulatePackageSuspendBroadcast(false, PKG); simulatePackageSuspendBroadcast(false, PKG, notif1.getUid()); ArgumentCaptor<List> captorUnhide = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture()); assertEquals(2, captorUnhide.getValue().size()); Loading @@ -4370,7 +4403,24 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(notif2); // on broadcast, nothing is hidden since no notifications are of package "test_package" mService.simulatePackageSuspendBroadcast(true, "test_package"); simulatePackageSuspendBroadcast(true, "test_package", notif1.getUid()); ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyHiddenLocked(captor.capture()); assertEquals(0, captor.getValue().size()); } @Test public void testNotificationFromDifferentUserHidden() { // post 2 notification from this package final NotificationRecord notif1 = generateNotificationRecord( mTestNotificationChannel, 1, null, true); final NotificationRecord notif2 = generateNotificationRecord( mTestNotificationChannel, 2, null, false); mService.addNotification(notif1); mService.addNotification(notif2); // on broadcast, nothing is hidden since no notifications are of user 10 with package PKG simulatePackageSuspendBroadcast(true, PKG, 10); ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyHiddenLocked(captor.capture()); assertEquals(0, captor.getValue().size()); Loading @@ -4387,16 +4437,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(pkgB); // on broadcast, hide one of the packages mService.simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a"}); simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a"}, new int[] {1000}); ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture()); assertEquals(1, captorHide.getValue().size()); assertEquals("a", captorHide.getValue().get(0).getSbn().getPackageName()); // on broadcast, unhide the package mService.simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a"}); simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a"}, new int[] {1000}); ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture()); assertEquals(1, captorUnhide.getValue().size()); Loading @@ -4414,8 +4466,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(pkgB); // on broadcast, hide one of the packages mService.simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a", "b"}); simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a", "b"}, new int[] {1000, 1001}); ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class); // should be called only once. Loading @@ -4425,8 +4478,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals("b", captorHide.getValue().get(1).getSbn().getPackageName()); // on broadcast, unhide the package mService.simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a", "b"}); simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a", "b"}, new int[] {1000, 1001}); ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class); // should be called only once. Loading @@ -4444,8 +4498,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(notif1); // on broadcast, nothing is hidden since no notifications are of package "test_package" mService.simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"test_package"}); simulatePackageDistractionBroadcast( PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"test_package"}, new int[]{notif1.getUid()}); ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); verify(mListeners, times(1)).notifyHiddenLocked(captor.capture()); assertEquals(0, captor.getValue().size()); Loading Loading @@ -7011,4 +7066,34 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertTrue(mService.isVisibleToListener(sbn, info)); } private void simulatePackageSuspendBroadcast(boolean suspend, String pkg, int uid) { // mimics receive broadcast that package is (un)suspended // but does not actually (un)suspend the package final Bundle extras = new Bundle(); extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[]{pkg}); extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, new int[]{uid}); final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED : Intent.ACTION_PACKAGES_UNSUSPENDED; final Intent intent = new Intent(action); intent.putExtras(extras); mPackageIntentReceiver.onReceive(getContext(), intent); } private void simulatePackageDistractionBroadcast(int flag, String[] pkgs, int[] uids) { // mimics receive broadcast that package is (un)distracting // but does not actually register that info with packagemanager final Bundle extras = new Bundle(); extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgs); extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, flag); extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uids); final Intent intent = new Intent(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED); intent.putExtras(extras); mPackageIntentReceiver.onReceive(getContext(), intent); } }