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

Commit 55a6021f authored by Jeff DeCew's avatar Jeff DeCew
Browse files

Block FullScreenIntent while device is in use if notification has a silencing GroupAlertBehavior.

Bug: 231322873
Test: atest NotificationInterruptStateProviderImplTest
Change-Id: Id82d20c9f1f2001400871b5381f52b40fbdf81c5
parent 0869abd8
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.interruption
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel.DEBUG
import com.android.systemui.log.LogLevel.INFO
import com.android.systemui.log.LogLevel.WARNING
import com.android.systemui.log.dagger.NotificationInterruptLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
@@ -211,6 +212,33 @@ class NotificationInterruptLogger @Inject constructor(
        })
    }

    fun logNoFullscreen(entry: NotificationEntry, reason: String) {
        buffer.log(TAG, DEBUG, {
            str1 = entry.logKey
            str2 = reason
        }, {
            "No FullScreenIntent: $str2: $str1"
        })
    }

    fun logNoFullscreenWarning(entry: NotificationEntry, reason: String) {
        buffer.log(TAG, WARNING, {
            str1 = entry.logKey
            str2 = reason
        }, {
            "No FullScreenIntent: WARNING: $str2: $str1"
        })
    }

    fun logFullscreen(entry: NotificationEntry, reason: String) {
        buffer.log(TAG, DEBUG, {
            str1 = entry.logKey
            str2 = reason
        }, {
            "FullScreenIntent: $str2: $str1"
        })
    }

    fun keyguardHideNotification(entry: NotificationEntry) {
        buffer.log(TAG, DEBUG, {
            str1 = entry.logKey
+64 −10
Original line number Diff line number Diff line
@@ -177,9 +177,69 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
     */
    @Override
    public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) {
        return entry.getSbn().getNotification().fullScreenIntent != null
                && (!shouldHeadsUp(entry)
                || mStatusBarStateController.getState() == StatusBarState.KEYGUARD);
        if (entry.getSbn().getNotification().fullScreenIntent == null) {
            return false;
        }

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

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

        // If the notification has suppressive GroupAlertBehavior, block FSI and warn.
        StatusBarNotification sbn = entry.getSbn();
        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();
            android.util.EventLog.writeEvent(0x534e4554, "231322873", uid, "groupAlertBehavior");
            mLogger.logNoFullscreenWarning(entry, "GroupAlertBehavior will prevent HUN");
            return false;
        }

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

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

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

        // If the notification should HUN, then we don't need FSI
        if (shouldHeadsUp(entry)) {
            mLogger.logNoFullscreen(entry, "Expected to HUN");
            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;
    }

    private boolean isDreaming() {
        try {
            return mDreamManager.isDreaming();
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to query dream manager.", e);
            return false;
        }
    }

    private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) {
@@ -223,13 +283,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
            return false;
        }

        boolean isDreaming = false;
        try {
            isDreaming = mDreamManager.isDreaming();
        } catch (RemoteException e) {
            Log.e(TAG, "Failed to query dream manager.", e);
        }
        boolean inUse = mPowerManager.isScreenOn() && !isDreaming;
        boolean inUse = mPowerManager.isScreenOn() && !isDreaming();

        if (!inUse) {
            mLogger.logNoHeadsUpNotInUse(entry);
+168 −32
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
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.google.common.truth.Truth.assertThat;
@@ -32,6 +33,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -96,6 +98,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
    NotifPipelineFlags mFlags;
    @Mock
    KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
    @Mock
    PendingIntent mPendingIntent;

    private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;

@@ -422,6 +426,122 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
    }

    @Test
    public void testShouldNotFullScreen_notPendingIntent() throws RemoteException {
        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
        when(mPowerManager.isInteractive()).thenReturn(true);
        when(mDreamManager.isDreaming()).thenReturn(false);
        when(mStatusBarStateController.getState()).thenReturn(SHADE);

        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isFalse();
        verify(mLogger, never()).logNoFullscreen(any(), any());
        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
        verify(mLogger, never()).logFullscreen(any(), any());
    }

    @Test
    public void testShouldNotFullScreen_notHighImportance() throws RemoteException {
        NotificationEntry entry = createFsiNotification(IMPORTANCE_DEFAULT, /* silenced */ false);
        when(mPowerManager.isInteractive()).thenReturn(true);
        when(mDreamManager.isDreaming()).thenReturn(false);
        when(mStatusBarStateController.getState()).thenReturn(SHADE);

        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isFalse();
        verify(mLogger).logNoFullscreen(entry, "Not important enough");
        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
        verify(mLogger, never()).logFullscreen(any(), any());
    }

    @Test
    public void testShouldNotFullScreen_isGroupAlertSilenced() throws RemoteException {
        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ true);
        when(mPowerManager.isInteractive()).thenReturn(false);
        when(mDreamManager.isDreaming()).thenReturn(true);
        when(mStatusBarStateController.getState()).thenReturn(KEYGUARD);

        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isFalse();
        verify(mLogger, never()).logNoFullscreen(any(), any());
        verify(mLogger).logNoFullscreenWarning(entry, "GroupAlertBehavior will prevent HUN");
        verify(mLogger, never()).logFullscreen(any(), any());
    }

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

        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isTrue();
        verify(mLogger, never()).logNoFullscreen(any(), any());
        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
        verify(mLogger).logFullscreen(entry, "Device is not interactive");
    }

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

        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isTrue();
        verify(mLogger, never()).logNoFullscreen(any(), any());
        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
        verify(mLogger).logFullscreen(entry, "Device is dreaming");
    }

    @Test
    public void testShouldFullScreen_onKeyguard() throws RemoteException {
        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
        when(mPowerManager.isInteractive()).thenReturn(true);
        when(mDreamManager.isDreaming()).thenReturn(false);
        when(mStatusBarStateController.getState()).thenReturn(KEYGUARD);

        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isTrue();
        verify(mLogger, never()).logNoFullscreen(any(), any());
        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
        verify(mLogger).logFullscreen(entry, "Keyguard is showing");
    }

    @Test
    public void testShouldNotFullScreen_willHun() throws RemoteException {
        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);

        assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                .isFalse();
        verify(mLogger).logNoFullscreen(entry, "Expected to HUN");
        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
        verify(mLogger, never()).logFullscreen(any(), any());
    }

    @Test
    public void testShouldFullScreen_packageSnoozed() throws RemoteException {
        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);

        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");
    }

    /**
     * Bubbles can happen.
     */
@@ -549,6 +669,10 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
                .setContentText("content text")
                .build();

        return createNotification(importance, n);
    }

    private NotificationEntry createNotification(int importance, Notification n) {
        return new NotificationEntryBuilder()
                .setPkg("a")
                .setOpPkg("a")
@@ -559,6 +683,18 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
                .build();
    }

    private NotificationEntry createFsiNotification(int importance, boolean silent) {
        Notification n = new Notification.Builder(getContext(), "a")
                .setContentTitle("title")
                .setContentText("content text")
                .setFullScreenIntent(mPendingIntent, true)
                .setGroup("fsi")
                .setGroupAlertBehavior(silent ? GROUP_ALERT_SUMMARY : Notification.GROUP_ALERT_ALL)
                .build();

        return createNotification(importance, n);
    }

    private final NotificationInterruptSuppressor
            mSuppressAwakeHeadsUp =
            new NotificationInterruptSuppressor() {