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

Commit 3a710b8b authored by Julia Tuttle's avatar Julia Tuttle
Browse files

Bypass HUN snooze for notifs with FSI

If an app posts a time-sensitive (alarm, call, etc.) notification that
can either HUN or FSI, it's possible for *neither* alerting behavior to
happen if the user has snoozed HUNs for the package and System UI has
suppressed FSIs because the screen is on and unlocked and the
FSI_REQUIRES_KEYGUARD flag is set.

In this case, assume any notification with an FSI is time-sensitive
(like an alarm or incoming call) and ignore whether HUNs have been
snoozed for the package.

Bug: 245356701
Test: atest NotificationInterruptStateProviderImplTest
Test: manual tests of relevant FSI/HUN snooze scenarios
Change-Id: Idee76ef9fc9d303903617ffd5bc79aad0756b2a0
parent 0db8b1de
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -70,7 +70,15 @@ class NotificationInterruptLogger @Inject constructor(
        buffer.log(TAG, DEBUG, {
            str1 = entry.logKey
        }, {
            "No alerting: snoozed package: $str1"
            "No heads up: snoozed package: $str1"
        })
    }

    fun logHeadsUpPackageSnoozeBypassedHasFsi(entry: NotificationEntry) {
        buffer.log(TAG, DEBUG, {
            str1 = entry.logKey
        }, {
            "Heads up: package snooze bypassed because notification has full-screen intent: $str1"
        })
    }

+27 −2
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.interruption;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD;
import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR;
import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI;

import android.app.Notification;
import android.app.NotificationManager;
@@ -87,7 +88,10 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
        FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD(1236),

        @UiEvent(doc = "HUN suppressed for old when")
        HUN_SUPPRESSED_OLD_WHEN(1237);
        HUN_SUPPRESSED_OLD_WHEN(1237),

        @UiEvent(doc = "HUN snooze bypassed for potentially suppressed FSI")
        HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI(1269);

        private final int mId;

@@ -409,7 +413,15 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
            return false;
        }

        if (isSnoozedPackage(sbn)) {
        final boolean isSnoozedPackage = isSnoozedPackage(sbn);
        final boolean fsiRequiresKeyguard = mFlags.fullScreenIntentRequiresKeyguard();
        final boolean hasFsi = sbn.getNotification().fullScreenIntent != null;

        // Assume any notification with an FSI is time-sensitive (like an alarm or incoming call)
        // and ignore whether HUNs have been snoozed for the package.
        final boolean shouldBypassSnooze = fsiRequiresKeyguard && hasFsi;

        if (isSnoozedPackage && !shouldBypassSnooze) {
            if (log) mLogger.logNoHeadsUpPackageSnoozed(entry);
            return false;
        }
@@ -447,6 +459,19 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
                return false;
            }
        }

        if (isSnoozedPackage) {
            if (log) {
                mLogger.logHeadsUpPackageSnoozeBypassedHasFsi(entry);
                final int uid = entry.getSbn().getUid();
                final String packageName = entry.getSbn().getPackageName();
                mUiEventLogger.log(HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI, uid,
                        packageName);
            }

            return true;
        }

        if (log) mLogger.logHeadsUp(entry);
        return true;
    }
+84 −14
Original line number Diff line number Diff line
@@ -741,7 +741,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
    }

    @Test
    public void testShouldFullScreen_snoozed_occluding_withStrictRules() throws Exception {
    public void testShouldNotFullScreen_snoozed_occluding_withStrictRules() throws Exception {
        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
        when(mPowerManager.isInteractive()).thenReturn(true);
@@ -753,16 +753,41 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        when(mKeyguardStateController.isOccluded()).thenReturn(true);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.FSI_KEYGUARD_OCCLUDED);
                .isEqualTo(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN);
        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isTrue();
        verify(mLogger, never()).logNoFullscreen(any(), any());
                .isFalse();
        verify(mLogger).logNoFullscreen(entry, "Expected to HUN");
        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
        verify(mLogger).logFullscreen(entry, "Expected not to HUN while keyguard occluded");
        verify(mLogger, never()).logFullscreen(any(), any());
    }

    @Test
    public void testShouldFullScreen_snoozed_lockedShade_withStrictRules() throws Exception {
    public void testShouldHeadsUp_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.shouldHeadsUp(entry)).isTrue();

        verify(mLogger).logHeadsUpPackageSnoozeBypassedHasFsi(entry);
        verify(mLogger, never()).logHeadsUp(any());

        assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1);
        UiEventLoggerFake.FakeUiEvent fakeUiEvent = mUiEventLoggerFake.get(0);
        assertThat(fakeUiEvent.eventId).isEqualTo(
                NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI.getId());
        assertThat(fakeUiEvent.uid).isEqualTo(entry.getSbn().getUid());
        assertThat(fakeUiEvent.packageName).isEqualTo(entry.getSbn().getPackageName());
    }

    @Test
    public void testShouldNotFullScreen_snoozed_lockedShade_withStrictRules() throws Exception {
        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
        when(mPowerManager.isInteractive()).thenReturn(true);
@@ -774,12 +799,37 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        when(mKeyguardStateController.isOccluded()).thenReturn(false);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.FSI_LOCKED_SHADE);
                .isEqualTo(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN);
        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isTrue();
        verify(mLogger, never()).logNoFullscreen(any(), any());
                .isFalse();
        verify(mLogger).logNoFullscreen(entry, "Expected to HUN");
        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
        verify(mLogger).logFullscreen(entry, "Keyguard is showing and not occluded");
        verify(mLogger, never()).logFullscreen(any(), any());
    }

    @Test
    public void testShouldHeadsUp_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.shouldHeadsUp(entry)).isTrue();

        verify(mLogger).logHeadsUpPackageSnoozeBypassedHasFsi(entry);
        verify(mLogger, never()).logHeadsUp(any());

        assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1);
        UiEventLoggerFake.FakeUiEvent fakeUiEvent = mUiEventLoggerFake.get(0);
        assertThat(fakeUiEvent.eventId).isEqualTo(
                NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI.getId());
        assertThat(fakeUiEvent.uid).isEqualTo(entry.getSbn().getUid());
        assertThat(fakeUiEvent.packageName).isEqualTo(entry.getSbn().getPackageName());
    }

    @Test
@@ -795,21 +845,41 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        when(mKeyguardStateController.isOccluded()).thenReturn(false);

        assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
                .isEqualTo(FullScreenIntentDecision.NO_FSI_NO_HUN_OR_KEYGUARD);
                .isEqualTo(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN);
        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isFalse();
        verify(mLogger, never()).logNoFullscreen(any(), any());
        verify(mLogger).logNoFullscreenWarning(entry, "Expected not to HUN while not on keyguard");
        verify(mLogger).logNoFullscreen(entry, "Expected to HUN");
        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
        verify(mLogger, never()).logFullscreen(any(), any());
    }

    @Test
    public void testShouldHeadsUp_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.shouldHeadsUp(entry)).isTrue();

        verify(mLogger).logHeadsUpPackageSnoozeBypassedHasFsi(entry);
        verify(mLogger, never()).logHeadsUp(any());

        assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1);
        UiEventLoggerFake.FakeUiEvent fakeUiEvent = mUiEventLoggerFake.get(0);
        assertThat(fakeUiEvent.eventId).isEqualTo(
                NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD.getId());
                NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI.getId());
        assertThat(fakeUiEvent.uid).isEqualTo(entry.getSbn().getUid());
        assertThat(fakeUiEvent.packageName).isEqualTo(entry.getSbn().getPackageName());
    }

    /* TODO: Verify the FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD UiEvent some other way. */

    /**
     * Bubbles can happen.
     */