Loading services/core/java/com/android/server/notification/NotificationManagerService.java +98 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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(), Loading Loading @@ -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; Loading Loading @@ -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 { Loading Loading @@ -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. Loading Loading @@ -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(); Loading Loading @@ -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(); Loading Loading @@ -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(); Loading Loading @@ -10908,6 +10997,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 Loading Loading @@ -10953,6 +11046,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, Loading services/tests/uiservicestests/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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" /> Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +67 −3 Original line number Diff line number Diff line Loading @@ -31,8 +31,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; 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; Loading @@ -42,18 +45,16 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.content.pm.VersionedPackage; import android.os.Bundle; import android.os.IBinder; import android.os.IInterface; import android.os.UserHandle; 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.testing.TestableContext; import android.util.ArraySet; Loading @@ -70,6 +71,7 @@ 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; Loading Loading @@ -430,4 +432,66 @@ public class NotificationListenersTest extends UiServiceTestCase { verify(mPmi).grantImplicitAccess(sbn.getUserId(), null, UserHandle.getAppId(33), sbn.getUid(), false, false); } @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(); } } services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +47 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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()); } } services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java +16 −0 Original line number Diff line number Diff line Loading @@ -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; } } } Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +98 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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(), Loading Loading @@ -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; Loading Loading @@ -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 { Loading Loading @@ -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. Loading Loading @@ -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(); Loading Loading @@ -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(); Loading Loading @@ -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(); Loading Loading @@ -10908,6 +10997,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 Loading Loading @@ -10953,6 +11046,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, Loading
services/tests/uiservicestests/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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" /> Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +67 −3 Original line number Diff line number Diff line Loading @@ -31,8 +31,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; 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; Loading @@ -42,18 +45,16 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.content.pm.VersionedPackage; import android.os.Bundle; import android.os.IBinder; import android.os.IInterface; import android.os.UserHandle; 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.testing.TestableContext; import android.util.ArraySet; Loading @@ -70,6 +71,7 @@ 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; Loading Loading @@ -430,4 +432,66 @@ public class NotificationListenersTest extends UiServiceTestCase { verify(mPmi).grantImplicitAccess(sbn.getUserId(), null, UserHandle.getAppId(33), sbn.getUid(), false, false); } @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(); } }
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +47 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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()); } }
services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java +16 −0 Original line number Diff line number Diff line Loading @@ -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; } } }