Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 64cbc085 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use UID when hiding/unhiding notifications"

parents 0aa4aa37 9d6f55c4
Loading
Loading
Loading
Loading
+76 −93
Original line number Diff line number Diff line
@@ -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 {
@@ -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);
                    }
                }

@@ -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) {
@@ -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);
@@ -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(
@@ -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);
                }
@@ -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);
                }
@@ -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.
+0 −21
Original line number Diff line number Diff line
@@ -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"
@@ -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) {
+101 −16
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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);
@@ -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);
@@ -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();
    }
@@ -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
@@ -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());
@@ -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());
@@ -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());
@@ -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.
@@ -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.
@@ -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());
@@ -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);
    }
}