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

Commit 9194bce0 authored by Jeff DeCew's avatar Jeff DeCew Committed by Android Build Coastguard Worker
Browse files

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

Bug: 231322873
Test: atest NotificationInterruptStateProviderImplTest
Merged-In: Id82d20c9f1f2001400871b5381f52b40fbdf81c5
Change-Id: Id82d20c9f1f2001400871b5381f52b40fbdf81c5
(cherry picked from commit d107a27f)
Merged-In: Id82d20c9f1f2001400871b5381f52b40fbdf81c5
parent aefdf086
Loading
Loading
Loading
Loading
+29 −0
Original line number Original line Diff line number Diff line
@@ -20,8 +20,10 @@ import android.service.notification.StatusBarNotification
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel.DEBUG
import com.android.systemui.log.LogLevel.DEBUG
import com.android.systemui.log.LogLevel.INFO
import com.android.systemui.log.LogLevel.INFO
import com.android.systemui.log.LogLevel.WARNING
import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import javax.inject.Inject
import javax.inject.Inject


class NotificationInterruptLogger @Inject constructor(
class NotificationInterruptLogger @Inject constructor(
@@ -212,6 +214,33 @@ class NotificationInterruptLogger @Inject constructor(
        })
        })
    }
    }


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

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

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

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

        // 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) {
    private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) {
@@ -219,13 +279,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
            return false;
            return false;
        }
        }


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


        if (!inUse) {
        if (!inUse) {
            mLogger.logNoHeadsUpNotInUse(sbn);
            mLogger.logNoHeadsUpNotInUse(sbn);
+138 −0
Original line number Original line Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;


import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
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;


import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertThat;
@@ -31,6 +32,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;
@@ -95,6 +97,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
    NotifPipelineFlags mFlags;
    NotifPipelineFlags mFlags;
    @Mock
    @Mock
    KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
    KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider;
    @Mock
    PendingIntent mPendingIntent;


    private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;
    private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider;


@@ -422,6 +426,122 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
        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.getSbn());
        verify(mLogger, never()).logNoFullscreen(any(), any());
        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
        verify(mLogger).logFullscreen(entry, "Expected not to HUN");
    }

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


        return createNotification(importance, n);
    }

    private NotificationEntry createNotification(int importance, Notification n) {
        return new NotificationEntryBuilder()
        return new NotificationEntryBuilder()
                .setPkg("a")
                .setPkg("a")
                .setOpPkg("a")
                .setOpPkg("a")
@@ -536,6 +660,20 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
                .build();
                .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
                        ? Notification.GROUP_ALERT_SUMMARY
                        : Notification.GROUP_ALERT_ALL)
                .build();

        return createNotification(importance, n);
    }

    private final NotificationInterruptSuppressor
    private final NotificationInterruptSuppressor
            mSuppressAwakeHeadsUp =
            mSuppressAwakeHeadsUp =
            new NotificationInterruptSuppressor() {
            new NotificationInterruptSuppressor() {