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

Commit ab38ac6f authored by Wenhao Wang's avatar Wenhao Wang Committed by Automerger Merge Worker
Browse files

DO NOT MERGE Suppress notifications when device enter lockdown am: 02771b7f

parents 984ae5f9 02771b7f
Loading
Loading
Loading
Loading
+98 −1
Original line number Diff line number Diff line
@@ -252,6 +252,7 @@ import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.StatsEvent;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -284,6 +285,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.TriPredicate;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.DeviceIdleInternal;
import com.android.server.EventLogTags;
import com.android.server.IoThread;
@@ -1923,6 +1925,54 @@ public class NotificationManagerService extends SystemService {
    private SettingsObserver mSettingsObserver;
    protected ZenModeHelper mZenModeHelper;
    protected class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
        SparseBooleanArray mUserInLockDownMode = new SparseBooleanArray();
        boolean mIsInLockDownMode = false;
        StrongAuthTracker(Context context) {
            super(context);
        }
        private boolean containsFlag(int haystack, int needle) {
            return (haystack & needle) != 0;
        }
        public boolean isInLockDownMode() {
            return mIsInLockDownMode;
        }
        @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) {
                return;
            }
            if (isInLockDownModeNext) {
                cancelNotificationsWhenEnterLockDownMode();
            }
            // 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;
            if (!isInLockDownModeNext) {
                postNotificationsWhenExitLockDownMode();
            }
        }
    }
    private LockPatternUtils mLockPatternUtils;
    private StrongAuthTracker mStrongAuthTracker;
    public NotificationManagerService(Context context) {
        this(context,
                new NotificationRecordLoggerImpl(),
@@ -1951,6 +2001,11 @@ public class NotificationManagerService extends SystemService {
        mAudioManager = audioMananger;
    }
    @VisibleForTesting
    void setStrongAuthTracker(StrongAuthTracker strongAuthTracker) {
        mStrongAuthTracker = strongAuthTracker;
    }
    @VisibleForTesting
    void setKeyguardManager(KeyguardManager keyguardManager) {
        mKeyguardManager = keyguardManager;
@@ -2145,6 +2200,8 @@ public class NotificationManagerService extends SystemService {
        mPlatformCompat = IPlatformCompat.Stub.asInterface(
                ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
        mLockPatternUtils = new LockPatternUtils(getContext());
        mStrongAuthTracker = new StrongAuthTracker(getContext());
        mUiHandler = new Handler(UiThread.get().getLooper());
        String[] extractorNames;
        try {
@@ -2641,6 +2698,7 @@ public class NotificationManagerService extends SystemService {
                bubbsExtractor.setShortcutHelper(mShortcutHelper);
            }
            registerNotificationPreferencesPullers();
            mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker);
        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
            // This observer will force an update when observe is called, causing us to
            // bind to listener services.
@@ -9537,6 +9595,29 @@ public class NotificationManagerService extends SystemService {
        }
    }
    private void cancelNotificationsWhenEnterLockDownMode() {
        synchronized (mNotificationLock) {
            int numNotifications = mNotificationList.size();
            for (int i = 0; i < numNotifications; i++) {
                NotificationRecord rec = mNotificationList.get(i);
                mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL,
                        rec.getStats());
            }
        }
    }
    private void postNotificationsWhenExitLockDownMode() {
        synchronized (mNotificationLock) {
            int numNotifications = mNotificationList.size();
            for (int i = 0; i < numNotifications; i++) {
                NotificationRecord rec = mNotificationList.get(i);
                mListeners.notifyPostedLocked(rec, rec);
            }
        }
    }
    private void updateNotificationPulse() {
        synchronized (mNotificationLock) {
            updateLightsLocked();
@@ -9753,6 +9834,10 @@ public class NotificationManagerService extends SystemService {
                rankings.toArray(new NotificationListenerService.Ranking[0]));
    }
    boolean isInLockDownMode() {
        return mStrongAuthTracker.isInLockDownMode();
    }
    boolean hasCompanionDevice(ManagedServiceInfo info) {
        if (mCompanionManager == null) {
            mCompanionManager = getCompanionManager();
@@ -10804,8 +10889,12 @@ public class NotificationManagerService extends SystemService {
         *                           targetting <= O_MR1
         */
        @GuardedBy("mNotificationLock")
        private void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
        void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
                boolean notifyAllListeners) {
            if (isInLockDownMode()) {
                return;
            }
            try {
                // Lazily initialized snapshots of the notification.
                StatusBarNotification sbn = r.getSbn();
@@ -10903,6 +10992,10 @@ public class NotificationManagerService extends SystemService {
        @GuardedBy("mNotificationLock")
        public void notifyRemovedLocked(NotificationRecord r, int reason,
                NotificationStats notificationStats) {
            if (isInLockDownMode()) {
                return;
            }
            final StatusBarNotification sbn = r.getSbn();
            // make a copy in case changes are made to the underlying Notification object
@@ -10948,6 +11041,10 @@ 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,
+1 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@
    <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" />
    <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/>
    <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
    <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />

    <application android:debuggable="true">
        <uses-library android:name="android.test.runner" />
+69 −0
Original line number Diff line number Diff line
@@ -30,8 +30,11 @@ import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -46,6 +49,8 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.service.notification.NotificationListenerFilter;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.testing.TestableContext;
import android.util.ArraySet;
import android.util.Pair;
@@ -59,11 +64,13 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.internal.util.reflection.FieldSetter;

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 {

@@ -388,4 +395,66 @@ public class NotificationListenersTest extends UiServiceTestCase {
        verify(mContext).sendBroadcastAsUser(
                any(), eq(UserHandle.of(userId)), nullable(String.class));
    }

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

    @Test
    public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException {
        NotificationRecord r = mock(NotificationRecord.class);
        NotificationStats rs = mock(NotificationStats.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();
    }
}
+47 −0
Original line number Diff line number Diff line
@@ -63,10 +63,13 @@ import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;

import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;

import static com.google.common.truth.Truth.assertThat;

import static junit.framework.Assert.assertEquals;
@@ -352,6 +355,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    MultiRateLimiter mToastRateLimiter;
    BroadcastReceiver mPackageIntentReceiver;
    NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
    TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker;
    private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
            1 << 30);
    @Mock
@@ -508,6 +512,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

        mService.setAudioManager(mAudioManager);

        mStrongAuthTracker = mService.new StrongAuthTrackerFake(mContext);
        mService.setStrongAuthTracker(mStrongAuthTracker);

        mShortcutHelper = mService.getShortcutHelper();
        mShortcutHelper.setLauncherApps(mLauncherApps);
        mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal);
@@ -9247,4 +9254,44 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        // make sure the summary was removed and not re-posted
        assertThat(mService.getNotificationRecordCount()).isEqualTo(0);
    }

    @Test
    public void testStrongAuthTracker_isInLockDownMode() {
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
        mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
        assertTrue(mStrongAuthTracker.isInLockDownMode());
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
        mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
        assertFalse(mStrongAuthTracker.isInLockDownMode());
    }

    @Test
    public void testCancelAndPostNotificationsWhenEnterAndExitLockDownMode() {
        // post 2 notifications from 2 packages
        NotificationRecord pkgA = new NotificationRecord(mContext,
                generateSbn("a", 1000, 9, 0), mTestNotificationChannel);
        mService.addNotification(pkgA);
        NotificationRecord pkgB = new NotificationRecord(mContext,
                generateSbn("b", 1001, 9, 0), mTestNotificationChannel);
        mService.addNotification(pkgB);

        // when entering the lockdown mode, cancel the 2 notifications.
        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
        mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
        assertTrue(mStrongAuthTracker.isInLockDownMode());

        // the notifyRemovedLocked function is called twice due to REASON_CANCEL_ALL.
        ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
        verify(mListeners, times(2)).notifyRemovedLocked(any(), captor.capture(), any());
        assertEquals(REASON_CANCEL_ALL, captor.getValue().intValue());

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

        // the notifyPostedLocked function is called twice.
        verify(mListeners, times(2)).notifyPostedLocked(any(), any());
    }
}
+16 −0
Original line number Diff line number Diff line
@@ -113,4 +113,20 @@ public class TestableNotificationManagerService extends NotificationManagerServi
    protected void doChannelWarningToast(int uid, CharSequence toastText) {
        mChannelToastsSent.add(uid);
    }

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

        public void setGetStrongAuthForUserReturnValue(int val) {
            mGetStrongAuthForUserReturnValue = val;
        }

        @Override
        public int getStrongAuthForUser(int userId) {
            return mGetStrongAuthForUserReturnValue;
        }
    }
}