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

Commit 2c16dedf authored by Julia Tuttle's avatar Julia Tuttle Committed by Automerger Merge Worker
Browse files

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

Merge "Suppress HUNs for notifications with old whens" into tm-qpr-dev am: cd694efc am: 48c7b20a

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20168065



Change-Id: I8abecc58d277b683a4482473f7b1c6d0c44eef4c
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 30ff024a 48c7b20a
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() {