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

Commit 1032594c authored by Wenhao Wang's avatar Wenhao Wang Committed by Android (Google) Code Review
Browse files

Merge "Enable user graularity for lockdown mode" into sc-qpr1-dev

parents 923c9fc0 9281fb98
Loading
Loading
Loading
Loading
+44 −28
Original line number Diff line number Diff line
@@ -2013,34 +2013,39 @@ public class NotificationManagerService extends SystemService {
            return (haystack & needle) != 0;
        }

        public boolean isInLockDownMode() {
            return mIsInLockDownMode;
        // Return whether the user is in lockdown mode.
        // 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
        public synchronized void onStrongAuthRequiredChanged(int userId) {
            boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId),
                    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;
            }

            if (isInLockDownModeNext) {
                cancelNotificationsWhenEnterLockDownMode();
            // When the lockdown mode is changed, we perform the following steps.
            // 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
            // 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;
            mUserInLockDownMode.put(userId, userInLockDownModeNext);

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

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

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

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

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

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

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

    boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -10614,7 +10634,7 @@ public class NotificationManagerService extends SystemService {
        @GuardedBy("mNotificationLock")
        void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
                boolean notifyAllListeners) {
            if (isInLockDownMode()) {
            if (isInLockDownMode(r.getUser().getIdentifier())) {
                return;
            }

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

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

            boolean isHiddenRankingUpdate = changedHiddenNotifications != null
                    && changedHiddenNotifications.size() > 0;
            // TODO (b/73052211): if the ranking update changed the notification type,
+99 −50
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.pm.VersionedPackage;
import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.NotificationListenerFilter;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
@@ -61,7 +62,6 @@ import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.List;

public class NotificationListenersTest extends UiServiceTestCase {

@@ -362,63 +362,112 @@ public class NotificationListenersTest extends UiServiceTestCase {

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

        // before the lockdown mode
        when(mNm.isInLockDownMode()).thenReturn(false);
        mListeners.notifyPostedLocked(r, old, true);
        mListeners.notifyPostedLocked(r, old, false);
        verify(r, atLeast(2)).getSbn();

        // in the lockdown mode
        reset(r);
        reset(old);
        when(mNm.isInLockDownMode()).thenReturn(true);
        mListeners.notifyPostedLocked(r, old, true);
        mListeners.notifyPostedLocked(r, old, false);
        verify(r, never()).getSbn();
    }

    @Test
    public void testnotifyRankingUpdateLockedInLockdownMode() {
        List chn = mock(List.class);

        // before the lockdown mode
        when(mNm.isInLockDownMode()).thenReturn(false);
        mListeners.notifyRankingUpdateLocked(chn);
        verify(chn, atLeast(1)).size();

        // in the lockdown mode
        reset(chn);
        when(mNm.isInLockDownMode()).thenReturn(true);
        mListeners.notifyRankingUpdateLocked(chn);
        verify(chn, never()).size();
        NotificationRecord r0 = mock(NotificationRecord.class);
        NotificationRecord old0 = mock(NotificationRecord.class);
        UserHandle uh0 = mock(UserHandle.class);

        NotificationRecord r1 = mock(NotificationRecord.class);
        NotificationRecord old1 = mock(NotificationRecord.class);
        UserHandle uh1 = mock(UserHandle.class);

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

        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, atLeast(2)).getSbn();

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

        // Reset
        reset(r0);
        reset(old0);
        reset(r1);
        reset(old1);

        // 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
    public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException {
        NotificationRecord r = mock(NotificationRecord.class);
        NotificationStats rs = mock(NotificationStats.class);
        NotificationRecord r0 = mock(NotificationRecord.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);
        FieldSetter.setField(mNm,
                NotificationManagerService.class.getDeclaredField("mHandler"),
                mock(NotificationManagerService.WorkerHandler.class));

        // before the lockdown mode
        when(mNm.isInLockDownMode()).thenReturn(false);
        when(r.getSbn()).thenReturn(sbn);
        mListeners.notifyRemovedLocked(r, 0, rs);
        mListeners.notifyRemovedLocked(r, 0, rs);
        verify(r, atLeast(2)).getSbn();

        // in the lockdown mode
        reset(r);
        reset(rs);
        when(mNm.isInLockDownMode()).thenReturn(true);
        when(r.getSbn()).thenReturn(sbn);
        mListeners.notifyRemovedLocked(r, 0, rs);
        mListeners.notifyRemovedLocked(r, 0, rs);
        verify(r, never()).getSbn();
        // Neither user0 and user1 is in the lockdown mode
        when(r0.getUser()).thenReturn(uh0);
        when(uh0.getIdentifier()).thenReturn(0);
        when(mNm.isInLockDownMode(0)).thenReturn(false);
        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, atLeast(2)).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();
    }
}
+61 −7
Original line number Diff line number Diff line
@@ -160,6 +160,7 @@ import android.service.notification.Adjustment;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerFilter;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenPolicy;
@@ -195,6 +196,7 @@ import com.android.server.SystemService.TargetUser;
import com.android.server.UiServiceTestCase;
import com.android.server.lights.LightsManager;
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.NotificationListeners;
import com.android.server.pm.PackageManagerService;
@@ -345,6 +347,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        @Nullable
        NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;

        @Nullable
        Boolean mIsVisibleToListenerReturnValue = null;

        TestableNotificationManagerService(Context context, NotificationRecordLogger logger,
                InstanceIdSequence notificationInstanceIdSequence) {
            super(context, logger, notificationInstanceIdSequence);
@@ -413,6 +418,19 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
            void onGranted(ComponentName assistant, int userId, boolean granted, boolean userSet);
        }

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

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

        class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker {
            private int mGetStrongAuthForUserReturnValue = 0;
            StrongAuthTrackerFake(Context context) {
@@ -8549,10 +8567,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
        mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
        assertTrue(mStrongAuthTracker.isInLockDownMode());
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
        assertTrue(mStrongAuthTracker.isInLockDownMode(mContext.getUserId()));
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(mContext.getUserId());
        mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
        assertFalse(mStrongAuthTracker.isInLockDownMode());
        assertFalse(mStrongAuthTracker.isInLockDownMode(mContext.getUserId()));
    }

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

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

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

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

    @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);
    }
}