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

Commit 0bbfb8ab authored by Julia Reynolds's avatar Julia Reynolds Committed by Automerger Merge Worker
Browse files

Merge "Enable user graularity for lockdown mode" into tm-qpr-dev am: 04cbb0e2

parents cbcf778a 04cbb0e2
Loading
Loading
Loading
Loading
+43 −27
Original line number Diff line number Diff line
@@ -1978,34 +1978,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);
            }
        }
    }
@@ -9721,11 +9726,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());
            }
@@ -9733,14 +9741,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;
            }
        }
    }
@@ -9919,6 +9936,9 @@ public class NotificationManagerService extends SystemService {
        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;
            }
@@ -9960,8 +9980,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) {
@@ -11037,7 +11057,7 @@ public class NotificationManagerService extends SystemService {
        @GuardedBy("mNotificationLock")
        void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
                boolean notifyAllListeners) {
            if (isInLockDownMode()) {
            if (isInLockDownMode(r.getUser().getIdentifier())) {
                return;
            }
@@ -11143,7 +11163,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;
            }
@@ -11192,10 +11212,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,
+98 −49
Original line number Diff line number Diff line
@@ -488,63 +488,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();
    }
}
+44 −7
Original line number Diff line number Diff line
@@ -174,6 +174,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;
@@ -9837,10 +9838,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
@@ -9856,8 +9857,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_CANCEL_ALL.
        ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
@@ -9866,10 +9867,46 @@ 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);
    }

    @Test
+18 −0
Original line number Diff line number Diff line
@@ -19,10 +19,12 @@ package com.android.server.notification;
import android.companion.ICompanionDeviceManager;
import android.content.ComponentName;
import android.content.Context;
import android.service.notification.StatusBarNotification;

import androidx.annotation.Nullable;

import com.android.internal.logging.InstanceIdSequence;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;

import java.util.HashSet;
import java.util.Set;
@@ -37,6 +39,9 @@ public class TestableNotificationManagerService extends NotificationManagerServi
    @Nullable
    NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;

    @Nullable
    Boolean mIsVisibleToListenerReturnValue = null;

    TestableNotificationManagerService(Context context, NotificationRecordLogger logger,
            InstanceIdSequence notificationInstanceIdSequence) {
        super(context, logger, notificationInstanceIdSequence);
@@ -119,6 +124,19 @@ public class TestableNotificationManagerService extends NotificationManagerServi
        mShowReviewPermissionsNotification = setting;
    }

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

    public class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker {
        private int mGetStrongAuthForUserReturnValue = 0;
        StrongAuthTrackerFake(Context context) {