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

Commit cd694efc authored by Julia Tuttle's avatar Julia Tuttle Committed by Android (Google) Code Review
Browse files

Merge "Suppress HUNs for notifications with old whens" into tm-qpr-dev

parents 6024e659 21ba4bf2
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -44,4 +44,8 @@ class NotifPipelineFlags @Inject constructor(
    val shouldFilterUnseenNotifsOnKeyguard: Boolean by lazy {
        featureFlags.isEnabled(Flags.FILTER_UNSEEN_NOTIFS_ON_KEYGUARD)
    }

    val isNoHunForOldWhenEnabled: Boolean by lazy {
        featureFlags.isEnabled(Flags.NO_HUN_FOR_OLD_WHEN)
    }
}
+30 −0
Original line number Diff line number Diff line
@@ -106,6 +106,36 @@ class NotificationInterruptLogger @Inject constructor(
        })
    }

    fun logNoHeadsUpOldWhen(
        entry: NotificationEntry,
        notifWhen: Long,
        notifAge: Long
    ) {
        buffer.log(TAG, DEBUG, {
            str1 = entry.logKey
            long1 = notifWhen
            long2 = notifAge
        }, {
            "No heads up: old when $long1 (age=$long2 ms): $str1"
        })
    }

    fun logMaybeHeadsUpDespiteOldWhen(
        entry: NotificationEntry,
        notifWhen: Long,
        notifAge: Long,
        reason: String
    ) {
        buffer.log(TAG, DEBUG, {
            str1 = entry.logKey
            str2 = reason
            long1 = notifWhen
            long2 = notifAge
        }, {
            "Maybe heads up: old when $long1 (age=$long2 ms) but $str2: $str1"
        })
    }

    fun logNoHeadsUpSuppressedBy(
        entry: NotificationEntry,
        suppressor: NotificationInterruptSuppressor
+56 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ 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 android.app.Notification;
import android.app.NotificationManager;
import android.content.ContentResolver;
import android.database.ContentObserver;
@@ -82,7 +83,10 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
        FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR(1235),

        @UiEvent(doc = "FSI suppressed for requiring neither HUN nor keyguard")
        FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD(1236);
        FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD(1236),

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

        private final int mId;

@@ -346,6 +350,10 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
            return false;
        }

        if (shouldSuppressHeadsUpWhenAwakeForOldWhen(entry, log)) {
            return false;
        }

        for (int i = 0; i < mSuppressors.size(); i++) {
            if (mSuppressors.get(i).suppressAwakeHeadsUp(entry)) {
                if (log) mLogger.logNoHeadsUpSuppressedBy(entry, mSuppressors.get(i));
@@ -470,4 +478,51 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
    private boolean isSnoozedPackage(StatusBarNotification sbn) {
        return mHeadsUpManager.isSnoozed(sbn.getPackageName());
    }

    private boolean shouldSuppressHeadsUpWhenAwakeForOldWhen(NotificationEntry entry, boolean log) {
        if (!mFlags.isNoHunForOldWhenEnabled()) {
            return false;
        }

        final Notification notification = entry.getSbn().getNotification();
        if (notification == null) {
            return false;
        }

        final long when = notification.when;
        final long now = System.currentTimeMillis();
        final long age = now - when;

        if (age < MAX_HUN_WHEN_AGE_MS) {
            return false;
        }

        if (when <= 0) {
            // Some notifications (including many system notifications) are posted with the "when"
            // field set to 0. Nothing in the Javadocs for Notification mentions a special meaning
            // for a "when" of 0, but Android didn't even exist at the dawn of the Unix epoch.
            // Therefore, assume that these notifications effectively don't have a "when" value,
            // and don't suppress HUNs.
            if (log) mLogger.logMaybeHeadsUpDespiteOldWhen(entry, when, age, "when <= 0");
            return false;
        }

        if (notification.fullScreenIntent != null) {
            if (log) mLogger.logMaybeHeadsUpDespiteOldWhen(entry, when, age, "full-screen intent");
            return false;
        }

        if (notification.isForegroundService()) {
            if (log) mLogger.logMaybeHeadsUpDespiteOldWhen(entry, when, age, "foreground service");
            return false;
        }

        if (log) mLogger.logNoHeadsUpOldWhen(entry, when, age);
        final int uid = entry.getSbn().getUid();
        final String packageName = entry.getSbn().getPackageName();
        mUiEventLogger.log(NotificationInterruptEvent.HUN_SUPPRESSED_OLD_WHEN, uid, packageName);
        return true;
    }

    public static final long MAX_HUN_WHEN_AGE_MS = 24 * 60 * 60 * 1000;
}
+134 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.interruption;


import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.Notification.GROUP_ALERT_SUMMARY;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -33,6 +34,8 @@ 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.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -390,6 +393,127 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
    }

    private long makeWhenHoursAgo(long hoursAgo) {
        return System.currentTimeMillis() - (1000 * 60 * 60 * hoursAgo);
    }

    @Test
    public void testShouldHeadsUp_oldWhen_flagDisabled() throws Exception {
        ensureStateForHeadsUpWhenAwake();
        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(false);

        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
        entry.getSbn().getNotification().when = makeWhenHoursAgo(25);

        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();

        verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
        verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
    }

    @Test
    public void testShouldHeadsUp_oldWhen_whenNow() throws Exception {
        ensureStateForHeadsUpWhenAwake();
        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);

        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);

        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();

        verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
        verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
    }

    @Test
    public void testShouldHeadsUp_oldWhen_whenRecent() throws Exception {
        ensureStateForHeadsUpWhenAwake();
        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);

        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
        entry.getSbn().getNotification().when = makeWhenHoursAgo(13);

        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();

        verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
        verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
    }

    @Test
    public void testShouldHeadsUp_oldWhen_whenZero() throws Exception {
        ensureStateForHeadsUpWhenAwake();
        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);

        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
        entry.getSbn().getNotification().when = 0L;

        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();

        verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
        verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(0L), anyLong(),
                eq("when <= 0"));
    }

    @Test
    public void testShouldHeadsUp_oldWhen_whenNegative() throws Exception {
        ensureStateForHeadsUpWhenAwake();
        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);

        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
        entry.getSbn().getNotification().when = -1L;

        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
        verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
        verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(-1L), anyLong(),
                eq("when <= 0"));
    }

    @Test
    public void testShouldHeadsUp_oldWhen_hasFullScreenIntent() throws Exception {
        ensureStateForHeadsUpWhenAwake();
        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
        long when = makeWhenHoursAgo(25);

        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silent= */ false);
        entry.getSbn().getNotification().when = when;

        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();

        verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
        verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(when), anyLong(),
                eq("full-screen intent"));
    }

    @Test
    public void testShouldHeadsUp_oldWhen_isForegroundService() throws Exception {
        ensureStateForHeadsUpWhenAwake();
        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
        long when = makeWhenHoursAgo(25);

        NotificationEntry entry = createFgsNotification(IMPORTANCE_HIGH);
        entry.getSbn().getNotification().when = when;

        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();

        verify(mLogger, never()).logNoHeadsUpOldWhen(any(), anyLong(), anyLong());
        verify(mLogger).logMaybeHeadsUpDespiteOldWhen(eq(entry), eq(when), anyLong(),
                eq("foreground service"));
    }

    @Test
    public void testShouldNotHeadsUp_oldWhen() throws Exception {
        ensureStateForHeadsUpWhenAwake();
        when(mFlags.isNoHunForOldWhenEnabled()).thenReturn(true);
        long when = makeWhenHoursAgo(25);

        NotificationEntry entry = createNotification(IMPORTANCE_HIGH);
        entry.getSbn().getNotification().when = when;

        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();

        verify(mLogger).logNoHeadsUpOldWhen(eq(entry), eq(when), anyLong());
        verify(mLogger, never()).logMaybeHeadsUpDespiteOldWhen(any(), anyLong(), anyLong(), any());
    }

    @Test
    public void testShouldNotFullScreen_notPendingIntent_withStrictFlag() throws Exception {
        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
@@ -763,6 +887,16 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
        return createNotification(importance, n);
    }

    private NotificationEntry createFgsNotification(int importance) {
        Notification n = new Notification.Builder(getContext(), "a")
                .setContentTitle("title")
                .setContentText("content text")
                .setFlag(FLAG_FOREGROUND_SERVICE, true)
                .build();

        return createNotification(importance, n);
    }

    private final NotificationInterruptSuppressor
            mSuppressAwakeHeadsUp =
            new NotificationInterruptSuppressor() {