FullScreenIntent cannot launch unless Keyguard is showing.

This feature is currently guarded behind a teamfood flag.

Bug: 231322873
Test: atest NotificationInterruptStateProviderImplTest StatusBarNotificationPresenterTest CentralSurfacesImplTest BubblesTest
Change-Id: Icd10387c20d5d49699e2794334fe03e9514d1e0b
This commit is contained in:
Jeff DeCew
2022-07-18 13:33:39 +00:00
parent 55a6021f24
commit c1b8107a7d
9 changed files with 245 additions and 8 deletions

View File

@@ -63,6 +63,9 @@ public class Flags {
public static final BooleanFlag REMOVE_UNRANKED_NOTIFICATIONS =
new BooleanFlag(109, false);
public static final BooleanFlag FSI_REQUIRES_KEYGUARD =
new BooleanFlag(110, false, true);
/***************************************/
// 200 - keyguard/lockscreen

View File

@@ -59,4 +59,7 @@ class NotifPipelineFlags @Inject constructor(
fun removeUnrankedNotifs(): Boolean =
featureFlags.isEnabled(Flags.REMOVE_UNRANKED_NOTIFICATIONS)
fun fullScreenIntentRequiresKeyguard(): Boolean =
featureFlags.isEnabled(Flags.FSI_REQUIRES_KEYGUARD)
}

View File

@@ -41,6 +41,7 @@ import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.util.ArrayList;
import java.util.List;
@@ -58,6 +59,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
private final List<NotificationInterruptSuppressor> mSuppressors = new ArrayList<>();
private final StatusBarStateController mStatusBarStateController;
private final KeyguardStateController mKeyguardStateController;
private final NotificationFilter mNotificationFilter;
private final ContentResolver mContentResolver;
private final PowerManager mPowerManager;
@@ -82,6 +84,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
NotificationFilter notificationFilter,
BatteryController batteryController,
StatusBarStateController statusBarStateController,
KeyguardStateController keyguardStateController,
HeadsUpManager headsUpManager,
NotificationInterruptLogger logger,
@Main Handler mainHandler,
@@ -94,6 +97,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mNotificationFilter = notificationFilter;
mStatusBarStateController = statusBarStateController;
mKeyguardStateController = keyguardStateController;
mHeadsUpManager = headsUpManager;
mLogger = logger;
mFlags = flags;
@@ -228,6 +232,28 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
return false;
}
// Check whether FSI requires the keyguard to be showing.
if (mFlags.fullScreenIntentRequiresKeyguard()) {
// If notification won't HUN and keyguard is showing, launch the FSI.
if (mKeyguardStateController.isShowing()) {
if (mKeyguardStateController.isOccluded()) {
mLogger.logFullscreen(entry, "Expected not to HUN while keyguard occluded");
} else {
// Likely LOCKED_SHADE, but launch FSI anyway
mLogger.logFullscreen(entry, "Keyguard is showing and not occluded");
}
return true;
}
// Detect the case determined by b/231322873 to launch FSI while device is in use,
// as blocked by the correct implementation, and report the event.
final int uid = entry.getSbn().getUid();
android.util.EventLog.writeEvent(0x534e4554, "231322873", uid, "no hun or keyguard");
mLogger.logNoFullscreenWarning(entry, "Expected not to HUN while not on keyguard");
return false;
}
// If the notification won't HUN for some other reason (DND/snooze/etc), launch FSI.
mLogger.logFullscreen(entry, "Expected not to HUN");
return true;

View File

@@ -399,7 +399,8 @@ class StatusBarNotificationPresenter implements NotificationPresenter,
return true;
}
if (sbn.getNotification().fullScreenIntent != null) {
if (sbn.getNotification().fullScreenIntent != null
&& !mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()) {
// we don't allow head-up on the lockscreen (unless there's a
// "showWhenLocked" activity currently showing) if
// the potential HUN has a fullscreen intent

View File

@@ -27,6 +27,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.google.common.truth.Truth.assertThat;
@@ -61,6 +62,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -87,6 +89,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
@Mock
StatusBarStateController mStatusBarStateController;
@Mock
KeyguardStateController mKeyguardStateController;
@Mock
HeadsUpManager mHeadsUpManager;
@Mock
NotificationInterruptLogger mLogger;
@@ -106,6 +110,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(false);
mNotifInterruptionStateProvider =
new NotificationInterruptStateProviderImpl(
@@ -116,6 +121,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
mNotificationFilter,
mBatteryController,
mStatusBarStateController,
mKeyguardStateController,
mHeadsUpManager,
mLogger,
mMockHandler,
@@ -426,6 +432,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
}
@Test
public void testShouldNotFullScreen_notPendingIntent_withStrictFlag() throws Exception {
when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
testShouldNotFullScreen_notPendingIntent();
}
@Test
public void testShouldNotFullScreen_notPendingIntent() throws RemoteException {
NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
@@ -440,6 +452,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
verify(mLogger, never()).logFullscreen(any(), any());
}
@Test
public void testShouldNotFullScreen_notHighImportance_withStrictFlag() throws Exception {
when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
testShouldNotFullScreen_notHighImportance();
}
@Test
public void testShouldNotFullScreen_notHighImportance() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_DEFAULT, /* silenced */ false);
@@ -454,6 +472,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
verify(mLogger, never()).logFullscreen(any(), any());
}
@Test
public void testShouldNotFullScreen_isGroupAlertSilenced_withStrictFlag() throws Exception {
when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
testShouldNotFullScreen_isGroupAlertSilenced();
}
@Test
public void testShouldNotFullScreen_isGroupAlertSilenced() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ true);
@@ -468,6 +492,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
verify(mLogger, never()).logFullscreen(any(), any());
}
@Test
public void testShouldFullScreen_notInteractive_withStrictFlag() throws Exception {
when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
testShouldFullScreen_notInteractive();
}
@Test
public void testShouldFullScreen_notInteractive() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
@@ -482,6 +512,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
verify(mLogger).logFullscreen(entry, "Device is not interactive");
}
@Test
public void testShouldFullScreen_isDreaming_withStrictFlag() throws Exception {
when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
testShouldFullScreen_isDreaming();
}
@Test
public void testShouldFullScreen_isDreaming() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
@@ -496,6 +532,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
verify(mLogger).logFullscreen(entry, "Device is dreaming");
}
@Test
public void testShouldFullScreen_onKeyguard_withStrictFlag() throws Exception {
when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
testShouldFullScreen_onKeyguard();
}
@Test
public void testShouldFullScreen_onKeyguard() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
@@ -510,6 +552,12 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
verify(mLogger).logFullscreen(entry, "Keyguard is showing");
}
@Test
public void testShouldNotFullScreen_willHun_withStrictFlag() throws Exception {
when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
testShouldNotFullScreen_willHun();
}
@Test
public void testShouldNotFullScreen_willHun() throws RemoteException {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
@@ -542,6 +590,66 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
verify(mLogger).logFullscreen(entry, "Expected not to HUN");
}
@Test
public void testShouldFullScreen_snoozed_occluding_withStrictRules() throws Exception {
when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mDreamManager.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isOccluded()).thenReturn(true);
assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
.isTrue();
verify(mLogger).logNoHeadsUpPackageSnoozed(entry);
verify(mLogger, never()).logNoFullscreen(any(), any());
verify(mLogger, never()).logNoFullscreenWarning(any(), any());
verify(mLogger).logFullscreen(entry, "Expected not to HUN while keyguard occluded");
}
@Test
public void testShouldFullScreen_snoozed_lockedShade_withStrictRules() throws Exception {
when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mDreamManager.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE_LOCKED);
when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isOccluded()).thenReturn(false);
assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
.isTrue();
verify(mLogger).logNoHeadsUpPackageSnoozed(entry);
verify(mLogger, never()).logNoFullscreen(any(), any());
verify(mLogger, never()).logNoFullscreenWarning(any(), any());
verify(mLogger).logFullscreen(entry, "Keyguard is showing and not occluded");
}
@Test
public void testShouldNotFullScreen_snoozed_unlocked_withStrictRules() throws Exception {
when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
when(mPowerManager.isInteractive()).thenReturn(true);
when(mPowerManager.isScreenOn()).thenReturn(true);
when(mDreamManager.isDreaming()).thenReturn(false);
when(mStatusBarStateController.getState()).thenReturn(SHADE);
when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
when(mKeyguardStateController.isShowing()).thenReturn(false);
when(mKeyguardStateController.isOccluded()).thenReturn(false);
assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
.isFalse();
verify(mLogger).logNoHeadsUpPackageSnoozed(entry);
verify(mLogger, never()).logNoFullscreen(any(), any());
verify(mLogger).logNoFullscreenWarning(entry, "Expected not to HUN while not on keyguard");
verify(mLogger, never()).logFullscreen(any(), any());
}
/**
* Bubbles can happen.
*/

View File

@@ -306,8 +306,13 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mNotificationInterruptStateProvider =
new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
mPowerManager,
mDreamManager, mAmbientDisplayConfiguration, mNotificationFilter,
mStatusBarStateController, mBatteryController, mHeadsUpManager,
mDreamManager,
mAmbientDisplayConfiguration,
mNotificationFilter,
mStatusBarStateController,
mKeyguardStateController,
mBatteryController,
mHeadsUpManager,
mock(NotificationInterruptLogger.class),
new Handler(TestableLooper.get(this).getLooper()),
mock(NotifPipelineFlags.class),
@@ -1036,15 +1041,28 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
AmbientDisplayConfiguration ambientDisplayConfiguration,
NotificationFilter filter,
StatusBarStateController controller,
KeyguardStateController keyguardStateController,
BatteryController batteryController,
HeadsUpManager headsUpManager,
NotificationInterruptLogger logger,
Handler mainHandler,
NotifPipelineFlags flags,
KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider) {
super(contentResolver, powerManager, dreamManager, ambientDisplayConfiguration, filter,
batteryController, controller, headsUpManager, logger, mainHandler,
flags, keyguardNotificationVisibilityProvider);
super(
contentResolver,
powerManager,
dreamManager,
ambientDisplayConfiguration,
filter,
batteryController,
controller,
keyguardStateController,
headsUpManager,
logger,
mainHandler,
flags,
keyguardNotificationVisibilityProvider
);
mUseHeadsUp = true;
}
}

View File

@@ -16,12 +16,14 @@ package com.android.systemui.statusbar.phone;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.metrics.LogMaker;
import android.support.test.metricshelper.MetricsAsserts;
@@ -80,6 +82,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
private FakeMetricsLogger mMetricsLogger;
private ShadeController mShadeController = mock(ShadeController.class);
private CentralSurfaces mCentralSurfaces = mock(CentralSurfaces.class);
private KeyguardStateController mKeyguardStateController = mock(KeyguardStateController.class);
private NotifPipelineFlags mNotifPipelineFlags = mock(NotifPipelineFlags.class);
private InitController mInitController = new InitController();
@Before
@@ -114,7 +118,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mock(ScrimController.class),
mock(NotificationShadeWindowController.class),
mock(DynamicPrivacyController.class),
mock(KeyguardStateController.class),
mKeyguardStateController,
mock(KeyguardIndicationController.class),
mCentralSurfaces,
mock(ShadeControllerImpl.class),
@@ -130,7 +134,7 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mInitController,
mNotificationInterruptStateProvider,
mock(NotificationRemoteInputManager.class),
mock(NotifPipelineFlags.class),
mNotifPipelineFlags,
mock(NotificationRemoteInputManager.Callback.class),
mock(NotificationListContainer.class));
mInitController.executePostInitTasks();
@@ -140,6 +144,19 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mInterruptSuppressor = suppressorCaptor.getValue();
}
@Test
public void testNoSuppressHeadsUp_default() {
Notification n = new Notification.Builder(getContext(), "a").build();
NotificationEntry entry = new NotificationEntryBuilder()
.setPkg("a")
.setOpPkg("a")
.setTag("a")
.setNotification(n)
.build();
assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
}
@Test
public void testSuppressHeadsUp_disabledStatusBar() {
Notification n = new Notification.Builder(getContext(), "a").build();
@@ -175,6 +192,63 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase {
mInterruptSuppressor.suppressAwakeHeadsUp(entry));
}
@Test
public void testNoSuppressHeadsUp_FSI_occludedKeygaurd() {
when(mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()).thenReturn(false);
Notification n = new Notification.Builder(getContext(), "a")
.setFullScreenIntent(mock(PendingIntent.class), true)
.build();
NotificationEntry entry = new NotificationEntryBuilder()
.setPkg("a")
.setOpPkg("a")
.setTag("a")
.setNotification(n)
.build();
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isOccluded()).thenReturn(true);
when(mCentralSurfaces.isOccluded()).thenReturn(true);
assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
}
@Test
public void testSuppressHeadsUp_FSI_nonOccludedKeygaurd() {
when(mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()).thenReturn(false);
Notification n = new Notification.Builder(getContext(), "a")
.setFullScreenIntent(mock(PendingIntent.class), true)
.build();
NotificationEntry entry = new NotificationEntryBuilder()
.setPkg("a")
.setOpPkg("a")
.setTag("a")
.setNotification(n)
.build();
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isOccluded()).thenReturn(false);
when(mCentralSurfaces.isOccluded()).thenReturn(false);
assertTrue(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
}
@Test
public void testNoSuppressHeadsUp_FSI_nonOccludedKeygaurd_withNewFlag() {
when(mNotifPipelineFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
Notification n = new Notification.Builder(getContext(), "a")
.setFullScreenIntent(mock(PendingIntent.class), true)
.build();
NotificationEntry entry = new NotificationEntryBuilder()
.setPkg("a")
.setOpPkg("a")
.setTag("a")
.setNotification(n)
.build();
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isOccluded()).thenReturn(false);
when(mCentralSurfaces.isOccluded()).thenReturn(false);
assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
}
@Test
public void testSuppressInterruptions_vrMode() {
Notification n = new Notification.Builder(getContext(), "a").build();

View File

@@ -321,6 +321,7 @@ public class BubblesTest extends SysuiTestCase {
mock(AmbientDisplayConfiguration.class),
mock(NotificationFilter.class),
mock(StatusBarStateController.class),
mock(KeyguardStateController.class),
mock(BatteryController.class),
mock(HeadsUpManager.class),
mock(NotificationInterruptLogger.class),

View File

@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
public class TestableNotificationInterruptStateProviderImpl
extends NotificationInterruptStateProviderImpl {
@@ -41,6 +42,7 @@ public class TestableNotificationInterruptStateProviderImpl
AmbientDisplayConfiguration ambientDisplayConfiguration,
NotificationFilter filter,
StatusBarStateController statusBarStateController,
KeyguardStateController keyguardStateController,
BatteryController batteryController,
HeadsUpManager headsUpManager,
NotificationInterruptLogger logger,
@@ -54,6 +56,7 @@ public class TestableNotificationInterruptStateProviderImpl
filter,
batteryController,
statusBarStateController,
keyguardStateController,
headsUpManager,
logger,
mainHandler,