Merge "DO NOT MERGE Suppress notifications when device enter lockdown" into sc-v2-dev
This commit is contained in:
@@ -246,6 +246,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;
|
||||
@@ -277,6 +278,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;
|
||||
@@ -1888,6 +1890,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(),
|
||||
@@ -1910,6 +1960,11 @@ public class NotificationManagerService extends SystemService {
|
||||
mAudioManager = audioMananger;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setStrongAuthTracker(StrongAuthTracker strongAuthTracker) {
|
||||
mStrongAuthTracker = strongAuthTracker;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setKeyguardManager(KeyguardManager keyguardManager) {
|
||||
mKeyguardManager = keyguardManager;
|
||||
@@ -2097,6 +2152,8 @@ public class NotificationManagerService extends SystemService {
|
||||
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
|
||||
|
||||
mUiHandler = new Handler(UiThread.get().getLooper());
|
||||
mLockPatternUtils = new LockPatternUtils(getContext());
|
||||
mStrongAuthTracker = new StrongAuthTracker(getContext());
|
||||
String[] extractorNames;
|
||||
try {
|
||||
extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
|
||||
@@ -2572,6 +2629,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.
|
||||
@@ -9117,6 +9175,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();
|
||||
@@ -9352,6 +9433,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();
|
||||
@@ -10403,8 +10488,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();
|
||||
@@ -10502,6 +10591,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
|
||||
@@ -10547,6 +10640,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,
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -24,15 +24,14 @@ import static com.android.server.notification.NotificationManagerService.Notific
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
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;
|
||||
@@ -47,10 +46,11 @@ 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;
|
||||
import android.util.Slog;
|
||||
import android.util.TypedXmlPullParser;
|
||||
import android.util.TypedXmlSerializer;
|
||||
import android.util.Xml;
|
||||
@@ -61,11 +61,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 {
|
||||
|
||||
@@ -374,4 +376,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,10 +58,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;
|
||||
@@ -223,7 +226,6 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -409,8 +411,26 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
interface NotificationAssistantAccessGrantedCallback {
|
||||
void onGranted(ComponentName assistant, int userId, boolean granted, boolean userSet);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker;
|
||||
|
||||
private class TestableToastCallback extends ITransientNotification.Stub {
|
||||
@Override
|
||||
public void show(IBinder windowToken) {
|
||||
@@ -530,6 +550,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);
|
||||
@@ -8354,4 +8377,44 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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_LOCKDOWN.
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user