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

Commit a5ad61f2 authored by Yuri Lin's avatar Yuri Lin Committed by Android (Google) Code Review
Browse files

Merge "Add an enum for decisions about whether to launch FSI." into tm-qpr-dev

parents 89965d1c 7c1e219d
Loading
Loading
Loading
Loading
+94 −0
Original line number Diff line number Diff line
@@ -24,6 +24,79 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 * When a notification is heads-up when dozing, this is also called "pulsing."
 */
public interface NotificationInterruptStateProvider {
    /**
     * Enum representing a decision of whether to show a full screen intent. While many of the
     * relevant properties could overlap, the decision represents the deciding factor for whether
     * the full screen intent should or shouldn't launch.
     */
    enum FullScreenIntentDecision {
        /**
         * No full screen intent included, so there is nothing to show.
         */
        NO_FULL_SCREEN_INTENT(false),
        /**
         * Suppressed by DND settings.
         */
        NO_FSI_SUPPRESSED_BY_DND(false),
        /**
         * Full screen intent was suppressed *only* by DND, and if not for DND would have shown. We
         * track this separately in order to allow the intent to be shown if the DND decision
         * changes.
         */
        NO_FSI_SUPPRESSED_ONLY_BY_DND(false),
        /**
         * Notification importance not high enough to show FSI.
         */
        NO_FSI_NOT_IMPORTANT_ENOUGH(false),
        /**
         * Notification should not FSI due to having suppressive GroupAlertBehavior. This blocks a
         * potentially malicious use of flags that previously allowed apps to escalate a HUN to an
         * FSI even while the device was unlocked.
         */
        NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR(false),
        /**
         * Device screen is off, so the FSI should launch.
         */
        FSI_DEVICE_NOT_INTERACTIVE(true),
        /**
         * Device is currently dreaming, so FSI should launch.
         */
        FSI_DEVICE_IS_DREAMING(true),
        /**
         * Keyguard is showing, so FSI should launch.
         */
        FSI_KEYGUARD_SHOWING(true),
        /**
         * The notification is expected to show heads-up, so FSI is not needed.
         */
        NO_FSI_EXPECTED_TO_HUN(false),
        /**
         * The notification is not expected to HUN while the keyguard is occluded, so show FSI.
         */
        FSI_KEYGUARD_OCCLUDED(true),
        /**
         * The notification is not expected to HUN when the keyguard is showing but not occluded,
         * which likely means that the shade is showing over the lockscreen; show FSI in this case.
         */
        FSI_LOCKED_SHADE(true),
        /**
         * FSI requires keyguard to be showing, but there is no keyguard. This is a (potentially
         * malicious) warning state where we suppress the FSI because the device is in use knowing
         * that the HUN will probably not display.
         */
        NO_FSI_NO_HUN_OR_KEYGUARD(false),
        /**
         * No conditions blocking FSI launch.
         */
        FSI_EXPECTED_NOT_TO_HUN(true);

        public final boolean shouldLaunch;

        FullScreenIntentDecision(boolean shouldLaunch) {
            this.shouldLaunch = shouldLaunch;
        }
    }

    /**
     * If the device is awake (not dozing):
     *  Whether the notification should peek in from the top and alert the user.
@@ -65,6 +138,27 @@ public interface NotificationInterruptStateProvider {
     */
    boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry);

    /**
     * Whether an entry's full screen intent would be launched.
     *
     * This method differs from shouldLaunchFullScreenIntentWhenAdded by returning more information
     * on the decision, and only optionally logging the outcome. It should be used in cases where
     * the caller needs to know what the decision would be, but may not actually launch the full
     * screen intent.
     *
     * @param entry the entry to evaluate
     * @return FullScreenIntentDecision representing the decision for whether to show the intent
     */
    FullScreenIntentDecision getFullScreenIntentDecision(NotificationEntry entry);

    /**
     * Write the full screen launch decision for the given entry to logs.
     *
     * @param entry the NotificationEntry for which the decision applies
     * @param decision reason for launch or no-launch of FSI for entry
     */
    void logFullScreenIntentDecision(NotificationEntry entry, FullScreenIntentDecision decision);

    /**
     * Add a component that can suppress visual interruptions.
     */
+109 −31
Original line number Diff line number Diff line
@@ -211,20 +211,49 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
     */
    @Override
    public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) {
        FullScreenIntentDecision decision = getFullScreenIntentDecision(entry);
        logFullScreenIntentDecision(entry, decision);
        return decision.shouldLaunch;
    }

    // Given whether the relevant entry was suppressed by DND, and the full screen intent launch
    // decision independent of the DND decision, returns the combined FullScreenIntentDecision that
    // results. If the entry was suppressed by DND but the decision otherwise would launch the
    // FSI, then it is suppressed *only* by DND, whereas (because the DND decision happens before
    // all others) if the entry would not otherwise have launched the FSI, DND is the effective
    // suppressor.
    //
    // If the entry was not suppressed by DND, just returns the given decision.
    private FullScreenIntentDecision getDecisionGivenSuppression(FullScreenIntentDecision decision,
            boolean suppressedByDND) {
        if (suppressedByDND) {
            return decision.shouldLaunch
                    ? FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND
                    : FullScreenIntentDecision.NO_FSI_SUPPRESSED_BY_DND;
        }
        return decision;
    }

    @Override
    public FullScreenIntentDecision getFullScreenIntentDecision(NotificationEntry entry) {
        if (entry.getSbn().getNotification().fullScreenIntent == null) {
            return false;
            return FullScreenIntentDecision.NO_FULL_SCREEN_INTENT;
        }

        // Boolean indicating whether this FSI would have been suppressed by DND. Because we
        // want to be able to identify when something would have shown an FSI if not for being
        // suppressed, we need to keep track of this value for future decisions.
        boolean suppressedByDND = false;

        // Never show FSI when suppressed by DND
        if (entry.shouldSuppressFullScreenIntent()) {
            mLogger.logNoFullscreen(entry, "Suppressed by DND");
            return false;
            suppressedByDND = true;
        }

        // Never show FSI if importance is not HIGH
        if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) {
            mLogger.logNoFullscreen(entry, "Not important enough");
            return false;
            return getDecisionGivenSuppression(FullScreenIntentDecision.NO_FSI_NOT_IMPORTANT_ENOUGH,
                    suppressedByDND);
        }

        // If the notification has suppressive GroupAlertBehavior, block FSI and warn.
@@ -232,36 +261,35 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
        if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
            // b/231322873: Detect and report an event when a notification has both an FSI and a
            // suppressive groupAlertBehavior, and now correctly block the FSI from firing.
            final int uid = entry.getSbn().getUid();
            final String packageName = entry.getSbn().getPackageName();
            android.util.EventLog.writeEvent(0x534e4554, "231322873", uid, "groupAlertBehavior");
            mUiEventLogger.log(FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR, uid, packageName);
            mLogger.logNoFullscreenWarning(entry, "GroupAlertBehavior will prevent HUN");
            return false;
            return getDecisionGivenSuppression(
                    FullScreenIntentDecision.NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR,
                    suppressedByDND);
        }

        // If the screen is off, then launch the FullScreenIntent
        if (!mPowerManager.isInteractive()) {
            mLogger.logFullscreen(entry, "Device is not interactive");
            return true;
            return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE,
                    suppressedByDND);
        }

        // If the device is currently dreaming, then launch the FullScreenIntent
        if (isDreaming()) {
            mLogger.logFullscreen(entry, "Device is dreaming");
            return true;
            return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_DEVICE_IS_DREAMING,
                    suppressedByDND);
        }

        // If the keyguard is showing, then launch the FullScreenIntent
        if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
            mLogger.logFullscreen(entry, "Keyguard is showing");
            return true;
            return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_KEYGUARD_SHOWING,
                    suppressedByDND);
        }

        // If the notification should HUN, then we don't need FSI
        if (shouldHeadsUp(entry)) {
            mLogger.logNoFullscreen(entry, "Expected to HUN");
            return false;
        // Because this is not the heads-up decision-making point, and checking whether it would
        // HUN, don't log this specific check.
        if (checkHeadsUp(entry, /* log= */ false)) {
            return getDecisionGivenSuppression(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN,
                    suppressedByDND);
        }

        // Check whether FSI requires the keyguard to be showing.
@@ -270,27 +298,77 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
            // 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");
                    return getDecisionGivenSuppression(
                            FullScreenIntentDecision.FSI_KEYGUARD_OCCLUDED,
                            suppressedByDND);
                } else {
                    // Likely LOCKED_SHADE, but launch FSI anyway
                    mLogger.logFullscreen(entry, "Keyguard is showing and not occluded");
                    return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_LOCKED_SHADE,
                            suppressedByDND);
                }
                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.
            return getDecisionGivenSuppression(FullScreenIntentDecision.NO_FSI_NO_HUN_OR_KEYGUARD,
                    suppressedByDND);
        }

        // If the notification won't HUN for some other reason (DND/snooze/etc), launch FSI.
        return getDecisionGivenSuppression(FullScreenIntentDecision.FSI_EXPECTED_NOT_TO_HUN,
                suppressedByDND);
    }

    @Override
    public void logFullScreenIntentDecision(NotificationEntry entry,
            FullScreenIntentDecision decision) {
        final int uid = entry.getSbn().getUid();
        final String packageName = entry.getSbn().getPackageName();
            android.util.EventLog.writeEvent(0x534e4554, "231322873", uid, "no hun or keyguard");
        switch (decision) {
            case NO_FULL_SCREEN_INTENT:
                return;
            case NO_FSI_SUPPRESSED_BY_DND:
            case NO_FSI_SUPPRESSED_ONLY_BY_DND:
                mLogger.logNoFullscreen(entry, "Suppressed by DND");
                return;
            case NO_FSI_NOT_IMPORTANT_ENOUGH:
                mLogger.logNoFullscreen(entry, "Not important enough");
                return;
            case NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR:
                android.util.EventLog.writeEvent(0x534e4554, "231322873", uid,
                        "groupAlertBehavior");
                mUiEventLogger.log(FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR, uid,
                        packageName);
                mLogger.logNoFullscreenWarning(entry, "GroupAlertBehavior will prevent HUN");
                return;
            case FSI_DEVICE_NOT_INTERACTIVE:
                mLogger.logFullscreen(entry, "Device is not interactive");
                return;
            case FSI_DEVICE_IS_DREAMING:
                mLogger.logFullscreen(entry, "Device is dreaming");
                return;
            case FSI_KEYGUARD_SHOWING:
                mLogger.logFullscreen(entry, "Keyguard is showing");
                return;
            case NO_FSI_EXPECTED_TO_HUN:
                mLogger.logNoFullscreen(entry, "Expected to HUN");
                return;
            case FSI_KEYGUARD_OCCLUDED:
                mLogger.logFullscreen(entry,
                        "Expected not to HUN while keyguard occluded");
                return;
            case FSI_LOCKED_SHADE:
                mLogger.logFullscreen(entry, "Keyguard is showing and not occluded");
                return;
            case NO_FSI_NO_HUN_OR_KEYGUARD:
                android.util.EventLog.writeEvent(0x534e4554, "231322873", uid,
                        "no hun or keyguard");
                mUiEventLogger.log(FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD, uid, packageName);
                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.
                return;
            case FSI_EXPECTED_NOT_TO_HUN:
                mLogger.logFullscreen(entry, "Expected not to HUN");
        return true;
        }
    }

    private boolean isDreaming() {
+62 −4
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;

import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
@@ -61,6 +62,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -527,6 +529,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        when(mDreamManager.isDreaming()).thenReturn(false);
        when(mStatusBarStateController.getState()).thenReturn(SHADE);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.NO_FULL_SCREEN_INTENT);
        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isFalse();
        verify(mLogger, never()).logNoFullscreen(any(), any());
@@ -534,6 +538,44 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        verify(mLogger, never()).logFullscreen(any(), any());
    }

    @Test
    public void testShouldNotFullScreen_suppressedOnlyByDND() throws RemoteException {
        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
        modifyRanking(entry)
                .setSuppressedVisualEffects(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT)
                .build();
        when(mPowerManager.isInteractive()).thenReturn(false);
        when(mDreamManager.isDreaming()).thenReturn(false);
        when(mStatusBarStateController.getState()).thenReturn(SHADE);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.NO_FSI_SUPPRESSED_ONLY_BY_DND);
        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isFalse();
        verify(mLogger, never()).logFullscreen(any(), any());
        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
        verify(mLogger).logNoFullscreen(entry, "Suppressed by DND");
    }

    @Test
    public void testShouldNotFullScreen_suppressedByDNDAndOther() throws RemoteException {
        NotificationEntry entry = createFsiNotification(IMPORTANCE_LOW, /* silenced */ false);
        modifyRanking(entry)
                .setSuppressedVisualEffects(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT)
                .build();
        when(mPowerManager.isInteractive()).thenReturn(false);
        when(mDreamManager.isDreaming()).thenReturn(false);
        when(mStatusBarStateController.getState()).thenReturn(SHADE);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.NO_FSI_SUPPRESSED_BY_DND);
        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isFalse();
        verify(mLogger, never()).logFullscreen(any(), any());
        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
        verify(mLogger).logNoFullscreen(entry, "Suppressed by DND");
    }

    @Test
    public void testShouldNotFullScreen_notHighImportance_withStrictFlag() throws Exception {
        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
@@ -547,6 +589,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        when(mDreamManager.isDreaming()).thenReturn(false);
        when(mStatusBarStateController.getState()).thenReturn(SHADE);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.NO_FSI_NOT_IMPORTANT_ENOUGH);
        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isFalse();
        verify(mLogger).logNoFullscreen(entry, "Not important enough");
@@ -567,6 +611,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        when(mDreamManager.isDreaming()).thenReturn(true);
        when(mStatusBarStateController.getState()).thenReturn(KEYGUARD);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR);
        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isFalse();
        verify(mLogger, never()).logNoFullscreen(any(), any());
@@ -594,6 +640,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        when(mDreamManager.isDreaming()).thenReturn(false);
        when(mStatusBarStateController.getState()).thenReturn(SHADE);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE);
        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isTrue();
        verify(mLogger, never()).logNoFullscreen(any(), any());
@@ -614,6 +662,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        when(mDreamManager.isDreaming()).thenReturn(true);
        when(mStatusBarStateController.getState()).thenReturn(SHADE);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.FSI_DEVICE_IS_DREAMING);
        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isTrue();
        verify(mLogger, never()).logNoFullscreen(any(), any());
@@ -634,6 +684,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        when(mDreamManager.isDreaming()).thenReturn(false);
        when(mStatusBarStateController.getState()).thenReturn(KEYGUARD);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.FSI_KEYGUARD_SHOWING);
        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isTrue();
        verify(mLogger, never()).logNoFullscreen(any(), any());
@@ -655,6 +707,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        when(mDreamManager.isDreaming()).thenReturn(false);
        when(mStatusBarStateController.getState()).thenReturn(SHADE);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN);
        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isFalse();
        verify(mLogger).logNoFullscreen(entry, "Expected to HUN");
@@ -671,9 +725,10 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        when(mStatusBarStateController.getState()).thenReturn(SHADE);
        when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.FSI_EXPECTED_NOT_TO_HUN);
        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");
@@ -691,9 +746,10 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        when(mKeyguardStateController.isShowing()).thenReturn(true);
        when(mKeyguardStateController.isOccluded()).thenReturn(true);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.FSI_KEYGUARD_OCCLUDED);
        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");
@@ -711,9 +767,10 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        when(mKeyguardStateController.isShowing()).thenReturn(true);
        when(mKeyguardStateController.isOccluded()).thenReturn(false);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.FSI_LOCKED_SHADE);
        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");
@@ -731,9 +788,10 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        when(mKeyguardStateController.isShowing()).thenReturn(false);
        when(mKeyguardStateController.isOccluded()).thenReturn(false);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.NO_FSI_NO_HUN_OR_KEYGUARD);
        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());