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

Commit baca9125 authored by Wenhao Wang's avatar Wenhao Wang
Browse files

Enable user graularity for lockdown mode

The NotificationManagerService registers a LockPatternUtils.StrongAuthTracker
to observe the StrongAuth changes of every user.
More specifically, it’s the STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN flag.
Via this flag, NotificationManagerService can perform the following operations
when the user enter or exit lockdown mode:

Enter lockdown:
1. Remove all the notifications belonging to the user.
2. Set the local flag to indicate the lockdown is on for the user.
   The local flag will suppress the user's notifications on the
   post, remove and update functions.

Exit lockdown:
1. Clear the local flag to indicate the lockdown is off for the user.
2. Repost the user’s notifications (suppressed during lockdown mode).

The CL also updates corresponding tests.

Bug: 173721373
Bug: 250743174
Test: atest NotificationManagerServiceTest
Test: atest NotificationListenersTest
Ignore-AOSP-First: pending fix for a security issue.

Change-Id: I4f30e56550729db7d673a92d2a1250509713f36d
Merged-In: I4f30e56550729db7d673a92d2a1250509713f36d
(cherry picked from commit de3b12fc)
parent d47de97f
Loading
Loading
Loading
Loading
+46 −30
Original line number Original line Diff line number Diff line
@@ -59,7 +59,6 @@ import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.media.AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
@@ -1727,34 +1726,39 @@ public class NotificationManagerService extends SystemService {
            return (haystack & needle) != 0;
            return (haystack & needle) != 0;
        }
        }


        public boolean isInLockDownMode() {
        // Return whether the user is in lockdown mode.
            return mIsInLockDownMode;
        // If the flag is not set, we assume the user is not in lockdown.
        public boolean isInLockDownMode(int userId) {
            return mUserInLockDownMode.get(userId, false);
        }
        }


        @Override
        @Override
        public synchronized void onStrongAuthRequiredChanged(int userId) {
        public synchronized void onStrongAuthRequiredChanged(int userId) {
            boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId),
            boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId),
                    STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
                    STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
            mUserInLockDownMode.put(userId, userInLockDownModeNext);
            boolean isInLockDownModeNext = mUserInLockDownMode.indexOfValue(true) != -1;


            if (mIsInLockDownMode == isInLockDownModeNext) {
            // Nothing happens if the lockdown mode of userId keeps the same.
            if (userInLockDownModeNext == isInLockDownMode(userId)) {
                return;
                return;
            }
            }


            if (isInLockDownModeNext) {
            // When the lockdown mode is changed, we perform the following steps.
                cancelNotificationsWhenEnterLockDownMode();
            // If the userInLockDownModeNext is true, all the function calls to
            // notifyPostedLocked and notifyRemovedLocked will not be executed.
            // The cancelNotificationsWhenEnterLockDownMode calls notifyRemovedLocked
            // and postNotificationsWhenExitLockDownMode calls notifyPostedLocked.
            // So we shall call cancelNotificationsWhenEnterLockDownMode before
            // we set mUserInLockDownMode as true.
            // On the other hand, if the userInLockDownModeNext is false, we shall call
            // postNotificationsWhenExitLockDownMode after we put false into mUserInLockDownMode
            if (userInLockDownModeNext) {
                cancelNotificationsWhenEnterLockDownMode(userId);
            }
            }


            // When the mIsInLockDownMode is true, both notifyPostedLocked and
            mUserInLockDownMode.put(userId, userInLockDownModeNext);
            // notifyRemovedLocked will be dismissed. So we shall call
            // cancelNotificationsWhenEnterLockDownMode before we set mIsInLockDownMode
            // as true and call postNotificationsWhenExitLockDownMode after we set
            // mIsInLockDownMode as false.
            mIsInLockDownMode = isInLockDownModeNext;


            if (!isInLockDownModeNext) {
            if (!userInLockDownModeNext) {
                postNotificationsWhenExitLockDownMode();
                postNotificationsWhenExitLockDownMode(userId);
            }
            }
        }
        }
    }
    }
@@ -8610,11 +8614,14 @@ public class NotificationManagerService extends SystemService {
        }
        }
    }
    }


    private void cancelNotificationsWhenEnterLockDownMode() {
    private void cancelNotificationsWhenEnterLockDownMode(int userId) {
        synchronized (mNotificationLock) {
        synchronized (mNotificationLock) {
            int numNotifications = mNotificationList.size();
            int numNotifications = mNotificationList.size();
            for (int i = 0; i < numNotifications; i++) {
            for (int i = 0; i < numNotifications; i++) {
                NotificationRecord rec = mNotificationList.get(i);
                NotificationRecord rec = mNotificationList.get(i);
                if (rec.getUser().getIdentifier() != userId) {
                    continue;
                }
                mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL,
                mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL,
                        rec.getStats());
                        rec.getStats());
            }
            }
@@ -8622,14 +8629,23 @@ public class NotificationManagerService extends SystemService {
        }
        }
    }
    }


    private void postNotificationsWhenExitLockDownMode() {
    private void postNotificationsWhenExitLockDownMode(int userId) {
        synchronized (mNotificationLock) {
        synchronized (mNotificationLock) {
            int numNotifications = mNotificationList.size();
            int numNotifications = mNotificationList.size();
            // Set the delay to spread out the burst of notifications.
            long delay = 0;
            for (int i = 0; i < numNotifications; i++) {
            for (int i = 0; i < numNotifications; i++) {
                NotificationRecord rec = mNotificationList.get(i);
                NotificationRecord rec = mNotificationList.get(i);
                if (rec.getUser().getIdentifier() != userId) {
                    continue;
                }
                mHandler.postDelayed(() -> {
                    synchronized (mNotificationLock) {
                        mListeners.notifyPostedLocked(rec, rec);
                        mListeners.notifyPostedLocked(rec, rec);
                    }
                    }

                }, delay);
                delay += 20;
            }
        }
        }
    }
    }


@@ -8832,12 +8848,15 @@ public class NotificationManagerService extends SystemService {
     * notifications visible to the given listener.
     * notifications visible to the given listener.
     */
     */
    @GuardedBy("mNotificationLock")
    @GuardedBy("mNotificationLock")
    private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
    NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) {
        final int N = mNotificationList.size();
        final int N = mNotificationList.size();
        final ArrayList<NotificationListenerService.Ranking> rankings = new ArrayList<>();
        final ArrayList<NotificationListenerService.Ranking> rankings = new ArrayList<>();


        for (int i = 0; i < N; i++) {
        for (int i = 0; i < N; i++) {
            NotificationRecord record = mNotificationList.get(i);
            NotificationRecord record = mNotificationList.get(i);
            if (isInLockDownMode(record.getUser().getIdentifier())) {
                continue;
            }
            if (!isVisibleToListener(record.getSbn(), info)) {
            if (!isVisibleToListener(record.getSbn(), info)) {
                continue;
                continue;
            }
            }
@@ -8876,8 +8895,8 @@ public class NotificationManagerService extends SystemService {
                rankings.toArray(new NotificationListenerService.Ranking[0]));
                rankings.toArray(new NotificationListenerService.Ranking[0]));
    }
    }


    boolean isInLockDownMode() {
    boolean isInLockDownMode(int userId) {
        return mStrongAuthTracker.isInLockDownMode();
        return mStrongAuthTracker.isInLockDownMode(userId);
    }
    }


    boolean hasCompanionDevice(ManagedServiceInfo info) {
    boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -8912,7 +8931,8 @@ public class NotificationManagerService extends SystemService {
                ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE));
                ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE));
    }
    }


    private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
    @VisibleForTesting
    boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
        if (!listener.enabledAndUserMatches(sbn.getUserId())) {
        if (!listener.enabledAndUserMatches(sbn.getUserId())) {
            return false;
            return false;
        }
        }
@@ -9598,7 +9618,7 @@ public class NotificationManagerService extends SystemService {
        @GuardedBy("mNotificationLock")
        @GuardedBy("mNotificationLock")
        void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
        void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
                boolean notifyAllListeners) {
                boolean notifyAllListeners) {
            if (isInLockDownMode()) {
            if (isInLockDownMode(r.getUser().getIdentifier())) {
                return;
                return;
            }
            }


@@ -9698,7 +9718,7 @@ public class NotificationManagerService extends SystemService {
        @GuardedBy("mNotificationLock")
        @GuardedBy("mNotificationLock")
        public void notifyRemovedLocked(NotificationRecord r, int reason,
        public void notifyRemovedLocked(NotificationRecord r, int reason,
                NotificationStats notificationStats) {
                NotificationStats notificationStats) {
            if (isInLockDownMode()) {
            if (isInLockDownMode(r.getUser().getIdentifier())) {
                return;
                return;
            }
            }


@@ -9747,10 +9767,6 @@ public class NotificationManagerService extends SystemService {
         */
         */
        @GuardedBy("mNotificationLock")
        @GuardedBy("mNotificationLock")
        public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) {
        public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) {
            if (isInLockDownMode()) {
                return;
            }

            boolean isHiddenRankingUpdate = changedHiddenNotifications != null
            boolean isHiddenRankingUpdate = changedHiddenNotifications != null
                    && changedHiddenNotifications.size() > 0;
                    && changedHiddenNotifications.size() > 0;


+99 −51
Original line number Original line Diff line number Diff line
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.when;
import android.app.INotificationManager;
import android.app.INotificationManager;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.service.notification.NotificationStats;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.service.notification.StatusBarNotification;
import android.testing.TestableContext;
import android.testing.TestableContext;
@@ -40,8 +41,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoAnnotations;
import org.mockito.internal.util.reflection.FieldSetter;
import org.mockito.internal.util.reflection.FieldSetter;


import java.util.List;

public class NotificationListenersTest extends UiServiceTestCase {
public class NotificationListenersTest extends UiServiceTestCase {


    @Mock
    @Mock
@@ -71,63 +70,112 @@ public class NotificationListenersTest extends UiServiceTestCase {


    @Test
    @Test
    public void testNotifyPostedLockedInLockdownMode() {
    public void testNotifyPostedLockedInLockdownMode() {
        NotificationRecord r = mock(NotificationRecord.class);
        NotificationRecord r0 = mock(NotificationRecord.class);
        NotificationRecord old = mock(NotificationRecord.class);
        NotificationRecord old0 = mock(NotificationRecord.class);

        UserHandle uh0 = mock(UserHandle.class);
        // before the lockdown mode

        when(mNm.isInLockDownMode()).thenReturn(false);
        NotificationRecord r1 = mock(NotificationRecord.class);
        mListeners.notifyPostedLocked(r, old, true);
        NotificationRecord old1 = mock(NotificationRecord.class);
        mListeners.notifyPostedLocked(r, old, false);
        UserHandle uh1 = mock(UserHandle.class);
        verify(r, atLeast(2)).getSbn();


        // Neither user0 and user1 is in the lockdown mode
        // in the lockdown mode
        when(r0.getUser()).thenReturn(uh0);
        reset(r);
        when(uh0.getIdentifier()).thenReturn(0);
        reset(old);
        when(mNm.isInLockDownMode(0)).thenReturn(false);
        when(mNm.isInLockDownMode()).thenReturn(true);

        mListeners.notifyPostedLocked(r, old, true);
        when(r1.getUser()).thenReturn(uh1);
        mListeners.notifyPostedLocked(r, old, false);
        when(uh1.getIdentifier()).thenReturn(1);
        verify(r, never()).getSbn();
        when(mNm.isInLockDownMode(1)).thenReturn(false);
    }


        mListeners.notifyPostedLocked(r0, old0, true);
    @Test
        mListeners.notifyPostedLocked(r0, old0, false);
    public void testnotifyRankingUpdateLockedInLockdownMode() {
        verify(r0, atLeast(2)).getSbn();
        List chn = mock(List.class);


        mListeners.notifyPostedLocked(r1, old1, true);
        // before the lockdown mode
        mListeners.notifyPostedLocked(r1, old1, false);
        when(mNm.isInLockDownMode()).thenReturn(false);
        verify(r1, atLeast(2)).getSbn();
        mListeners.notifyRankingUpdateLocked(chn);

        verify(chn, atLeast(1)).size();
        // Reset

        reset(r0);
        // in the lockdown mode
        reset(old0);
        reset(chn);
        reset(r1);
        when(mNm.isInLockDownMode()).thenReturn(true);
        reset(old1);
        mListeners.notifyRankingUpdateLocked(chn);

        verify(chn, never()).size();
        // Only user 0 is in the lockdown mode
        when(r0.getUser()).thenReturn(uh0);
        when(uh0.getIdentifier()).thenReturn(0);
        when(mNm.isInLockDownMode(0)).thenReturn(true);

        when(r1.getUser()).thenReturn(uh1);
        when(uh1.getIdentifier()).thenReturn(1);
        when(mNm.isInLockDownMode(1)).thenReturn(false);

        mListeners.notifyPostedLocked(r0, old0, true);
        mListeners.notifyPostedLocked(r0, old0, false);
        verify(r0, never()).getSbn();

        mListeners.notifyPostedLocked(r1, old1, true);
        mListeners.notifyPostedLocked(r1, old1, false);
        verify(r1, atLeast(2)).getSbn();
    }
    }


    @Test
    @Test
    public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException {
    public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException {
        NotificationRecord r = mock(NotificationRecord.class);
        NotificationRecord r0 = mock(NotificationRecord.class);
        NotificationStats rs = mock(NotificationStats.class);
        NotificationStats rs0 = mock(NotificationStats.class);
        UserHandle uh0 = mock(UserHandle.class);

        NotificationRecord r1 = mock(NotificationRecord.class);
        NotificationStats rs1 = mock(NotificationStats.class);
        UserHandle uh1 = mock(UserHandle.class);

        StatusBarNotification sbn = mock(StatusBarNotification.class);
        StatusBarNotification sbn = mock(StatusBarNotification.class);
        FieldSetter.setField(mNm,
        FieldSetter.setField(mNm,
                NotificationManagerService.class.getDeclaredField("mHandler"),
                NotificationManagerService.class.getDeclaredField("mHandler"),
                mock(NotificationManagerService.WorkerHandler.class));
                mock(NotificationManagerService.WorkerHandler.class));


        // before the lockdown mode
        // Neither user0 and user1 is in the lockdown mode
        when(mNm.isInLockDownMode()).thenReturn(false);
        when(r0.getUser()).thenReturn(uh0);
        when(r.getSbn()).thenReturn(sbn);
        when(uh0.getIdentifier()).thenReturn(0);
        mListeners.notifyRemovedLocked(r, 0, rs);
        when(mNm.isInLockDownMode(0)).thenReturn(false);
        mListeners.notifyRemovedLocked(r, 0, rs);
        when(r0.getSbn()).thenReturn(sbn);
        verify(r, atLeast(2)).getSbn();


        when(r1.getUser()).thenReturn(uh1);
        // in the lockdown mode
        when(uh1.getIdentifier()).thenReturn(1);
        reset(r);
        when(mNm.isInLockDownMode(1)).thenReturn(false);
        reset(rs);
        when(r1.getSbn()).thenReturn(sbn);
        when(mNm.isInLockDownMode()).thenReturn(true);

        when(r.getSbn()).thenReturn(sbn);
        mListeners.notifyRemovedLocked(r0, 0, rs0);
        mListeners.notifyRemovedLocked(r, 0, rs);
        mListeners.notifyRemovedLocked(r0, 0, rs0);
        mListeners.notifyRemovedLocked(r, 0, rs);
        verify(r0, atLeast(2)).getSbn();
        verify(r, never()).getSbn();

        mListeners.notifyRemovedLocked(r1, 0, rs1);
        mListeners.notifyRemovedLocked(r1, 0, rs1);
        verify(r1, atLeast(2)).getSbn();

        // Reset
        reset(r0);
        reset(rs0);
        reset(r1);
        reset(rs1);

        // Only user 0 is in the lockdown mode
        when(r0.getUser()).thenReturn(uh0);
        when(uh0.getIdentifier()).thenReturn(0);
        when(mNm.isInLockDownMode(0)).thenReturn(true);
        when(r0.getSbn()).thenReturn(sbn);

        when(r1.getUser()).thenReturn(uh1);
        when(uh1.getIdentifier()).thenReturn(1);
        when(mNm.isInLockDownMode(1)).thenReturn(false);
        when(r1.getSbn()).thenReturn(sbn);

        mListeners.notifyRemovedLocked(r0, 0, rs0);
        mListeners.notifyRemovedLocked(r0, 0, rs0);
        verify(r0, never()).getSbn();

        mListeners.notifyRemovedLocked(r1, 0, rs1);
        mListeners.notifyRemovedLocked(r1, 0, rs1);
        verify(r1, atLeast(2)).getSbn();
    }
    }
}
}
+64 −8
Original line number Original line Diff line number Diff line
@@ -141,6 +141,7 @@ import android.provider.Settings;
import android.service.notification.Adjustment;
import android.service.notification.Adjustment;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.NotificationStats;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenPolicy;
import android.service.notification.ZenPolicy;
@@ -174,6 +175,7 @@ import com.android.server.SystemService;
import com.android.server.UiServiceTestCase;
import com.android.server.UiServiceTestCase;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
import com.android.server.lights.LogicalLight;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
import com.android.server.notification.NotificationManagerService.NotificationListeners;
import com.android.server.notification.NotificationManagerService.NotificationListeners;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageManagerService;
@@ -304,6 +306,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    StatusBarManagerInternal mStatusBar;
    StatusBarManagerInternal mStatusBar;
    private final FakeSystemClock mSystemClock = new FakeSystemClock();
    private final FakeSystemClock mSystemClock = new FakeSystemClock();


    private NotificationManagerService.WorkerHandler mWorkerHandler;

    // Use a Testable subclass so we can simulate calls from the system without failing.
    // Use a Testable subclass so we can simulate calls from the system without failing.
    private static class TestableNotificationManagerService extends NotificationManagerService {
    private static class TestableNotificationManagerService extends NotificationManagerService {
        int countSystemChecks = 0;
        int countSystemChecks = 0;
@@ -316,6 +320,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        @Nullable
        @Nullable
        NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;
        NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;


        @Nullable
        Boolean mIsVisibleToListenerReturnValue = null;

        TestableNotificationManagerService(
        TestableNotificationManagerService(
                Context context,
                Context context,
                NotificationRecordLogger logger,
                NotificationRecordLogger logger,
@@ -402,6 +409,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
            void onGranted(ComponentName assistant, int userId, boolean granted);
            void onGranted(ComponentName assistant, int userId, boolean granted);
        }
        }


        protected void setIsVisibleToListenerReturnValue(boolean value) {
            mIsVisibleToListenerReturnValue = value;
        }

        @Override
        boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
            if (mIsVisibleToListenerReturnValue != null) {
                return mIsVisibleToListenerReturnValue;
            }
            return super.isVisibleToListener(sbn, listener);
        }

        class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker {
        class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker {
            private int mGetStrongAuthForUserReturnValue = 0;
            private int mGetStrongAuthForUserReturnValue = 0;
            StrongAuthTrackerFake(Context context) {
            StrongAuthTrackerFake(Context context) {
@@ -524,7 +543,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {


        when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true);
        when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true);


        mService.init(mService.new WorkerHandler(mTestableLooper.getLooper()),
        mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
        mService.init(mWorkerHandler,
                mRankingHandler, mPackageManager, mPackageManagerClient, mockLightsManager,
                mRankingHandler, mPackageManager, mPackageManagerClient, mockLightsManager,
                mListeners, mAssistants, mConditionProviders,
                mListeners, mAssistants, mConditionProviders,
                mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
                mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
@@ -593,6 +613,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        mService.unregisterDeviceConfigChange();
        mService.unregisterDeviceConfigChange();
        InstrumentationRegistry.getInstrumentation()
        InstrumentationRegistry.getInstrumentation()
                .getUiAutomation().dropShellPermissionIdentity();
                .getUiAutomation().dropShellPermissionIdentity();
        mWorkerHandler.removeCallbacksAndMessages(null);
    }
    }


    private ArrayMap<Boolean, ArrayList<ComponentName>> generateResetComponentValues() {
    private ArrayMap<Boolean, ArrayList<ComponentName>> generateResetComponentValues() {
@@ -7261,10 +7282,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
        mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
        mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
        assertTrue(mStrongAuthTracker.isInLockDownMode());
        assertTrue(mStrongAuthTracker.isInLockDownMode(mContext.getUserId()));
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(mContext.getUserId());
        mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
        mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
        assertFalse(mStrongAuthTracker.isInLockDownMode());
        assertFalse(mStrongAuthTracker.isInLockDownMode(mContext.getUserId()));
    }
    }


    @Test
    @Test
@@ -7280,8 +7301,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        // when entering the lockdown mode, cancel the 2 notifications.
        // when entering the lockdown mode, cancel the 2 notifications.
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
        mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
        mStrongAuthTracker.onStrongAuthRequiredChanged(0);
        assertTrue(mStrongAuthTracker.isInLockDownMode());
        assertTrue(mStrongAuthTracker.isInLockDownMode(0));


        // the notifyRemovedLocked function is called twice due to REASON_LOCKDOWN.
        // the notifyRemovedLocked function is called twice due to REASON_LOCKDOWN.
        ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
        ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
@@ -7290,9 +7311,44 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {


        // exit lockdown mode.
        // exit lockdown mode.
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
        mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
        mStrongAuthTracker.onStrongAuthRequiredChanged(0);
        assertFalse(mStrongAuthTracker.isInLockDownMode(0));


        // the notifyPostedLocked function is called twice.
        // the notifyPostedLocked function is called twice.
        verify(mListeners, times(2)).notifyPostedLocked(any(), any());
        verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong());
    }

    @Test
    public void testMakeRankingUpdateLockedInLockDownMode() {
        // post 2 notifications from a same package
        NotificationRecord pkgA = new NotificationRecord(mContext,
                generateSbn("a", 1000, 9, 0), mTestNotificationChannel);
        mService.addNotification(pkgA);
        NotificationRecord pkgB = new NotificationRecord(mContext,
                generateSbn("a", 1000, 9, 1), mTestNotificationChannel);
        mService.addNotification(pkgB);

        mService.setIsVisibleToListenerReturnValue(true);
        NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(null);
        assertEquals(2, nru.getRankingMap().getOrderedKeys().length);

        // when only user 0 entering the lockdown mode, its notification will be suppressed.
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
        mStrongAuthTracker.onStrongAuthRequiredChanged(0);
        assertTrue(mStrongAuthTracker.isInLockDownMode(0));
        assertFalse(mStrongAuthTracker.isInLockDownMode(1));

        nru = mService.makeRankingUpdateLocked(null);
        assertEquals(1, nru.getRankingMap().getOrderedKeys().length);

        // User 0 exits lockdown mode. Its notification will be resumed.
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
        mStrongAuthTracker.onStrongAuthRequiredChanged(0);
        assertFalse(mStrongAuthTracker.isInLockDownMode(0));
        assertFalse(mStrongAuthTracker.isInLockDownMode(1));

        nru = mService.makeRankingUpdateLocked(null);
        assertEquals(2, nru.getRankingMap().getOrderedKeys().length);
    }
    }
}
}