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

Commit 216db747 authored by Valentin Iftime's avatar Valentin Iftime Committed by Iavor-Valentin Iftime
Browse files

Skip playing notification sound or vibration when cooldown is muted

 Do no play the notification sound or vibration at 0 volume, but skip playback.
 However log the playback as usual and behave as if playback happened.

Flag: com.android.server.notification.polite_notifications

Test: atest NotificationAttentionHelperTest
Bug: 364349386
Change-Id: I4cba12ff4fd31e9da466aaca6ede8c633688cc2c
parent 28cafbcb
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ option java_package com.android.server
# replaces 27510 with a row per notification
27531 notification_visibility (key|3),(visibile|1),(lifespan|1),(freshness|1),(exposure|1),(rank|1)
# a notification emited noise, vibration, or light
27532 notification_alert (key|3),(buzz|1),(beep|1),(blink|1),(politeness|1)
27532 notification_alert (key|3),(buzz|1),(beep|1),(blink|1),(politeness|1),(mute_reason|1)
# a notification was added to a autogroup
27533 notification_autogrouped (key|3)
# notification was removed from an autogroup
+66 −32
Original line number Diff line number Diff line
@@ -118,6 +118,37 @@ public final class NotificationAttentionHelper {
            Intent.ACTION_MANAGED_PROFILE_AVAILABLE, new Pair<>(Intent.EXTRA_QUIET_MODE, false)
    );

    // Bits 1, 2, 3, 4 are already taken by: beep|buzz|blink|cooldown
    static final int MUTE_REASON_NOT_MUTED = 0;
    static final int MUTE_REASON_NOT_AUDIBLE = 1 << 5;
    static final int MUTE_REASON_SILENT_UPDATE = 1 << 6;
    static final int MUTE_REASON_POST_SILENTLY = 1 << 7;
    static final int MUTE_REASON_LISTENER_HINT = 1 << 8;
    static final int MUTE_REASON_DND = 1 << 9;
    static final int MUTE_REASON_GROUP_ALERT = 1 << 10;
    static final int MUTE_REASON_FLAG_SILENT = 1 << 11;
    static final int MUTE_REASON_RATE_LIMIT = 1 << 12;
    static final int MUTE_REASON_OTHER_INSISTENT_PLAYING = 1 << 13;
    static final int MUTE_REASON_SUPPRESSED_BUBBLE = 1 << 14;
    static final int MUTE_REASON_COOLDOWN = 1 << 15;

    @IntDef(prefix = { "MUTE_REASON_" }, value = {
        MUTE_REASON_NOT_MUTED,
        MUTE_REASON_NOT_AUDIBLE,
        MUTE_REASON_SILENT_UPDATE,
        MUTE_REASON_POST_SILENTLY,
        MUTE_REASON_LISTENER_HINT,
        MUTE_REASON_DND,
        MUTE_REASON_GROUP_ALERT,
        MUTE_REASON_FLAG_SILENT,
        MUTE_REASON_RATE_LIMIT,
        MUTE_REASON_OTHER_INSISTENT_PLAYING,
        MUTE_REASON_SUPPRESSED_BUBBLE,
        MUTE_REASON_COOLDOWN,
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface MuteReason {}

    private final Context mContext;
    private final PackageManager mPackageManager;
    private final TelephonyManager mTelephonyManager;
@@ -388,6 +419,7 @@ public final class NotificationAttentionHelper {
        boolean buzz = false;
        boolean beep = false;
        boolean blink = false;
        @MuteReason int shouldMuteReason = MUTE_REASON_NOT_MUTED;

        final String key = record.getKey();

@@ -395,10 +427,6 @@ public final class NotificationAttentionHelper {
            Log.d(TAG, "buzzBeepBlinkLocked " + record);
        }

        if (isPoliteNotificationFeatureEnabled(record)) {
            mStrategy.onNotificationPosted(record);
        }

        // Should this notification make noise, vibe, or use the LED?
        final boolean aboveThreshold =
                mIsAutomotive
@@ -443,7 +471,8 @@ public final class NotificationAttentionHelper {
                boolean vibrateOnly =
                        hasValidVibrate && mNotificationCooldownVibrateUnlocked && mUserPresent;
                boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
                if (hasAudibleAlert && !shouldMuteNotificationLocked(record, signals)) {
                shouldMuteReason = shouldMuteNotificationLocked(record, signals, hasAudibleAlert);
                if (shouldMuteReason == MUTE_REASON_NOT_MUTED) {
                    if (!sentAccessibilityEvent) {
                        sendAccessibilityEvent(record);
                        sentAccessibilityEvent = true;
@@ -541,15 +570,17 @@ public final class NotificationAttentionHelper {
            }
        }
        final int buzzBeepBlinkLoggingCode =
                (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0) | getPoliteBit(record);
                (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)
                | getPoliteBit(record) | shouldMuteReason;
        if (buzzBeepBlinkLoggingCode > 0) {
            MetricsLogger.action(record.getLogMaker()
                    .setCategory(MetricsEvent.NOTIFICATION_ALERT)
                    .setType(MetricsEvent.TYPE_OPEN)
                    .setSubtype(buzzBeepBlinkLoggingCode));
            EventLogTags.writeNotificationAlert(key, buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0,
                    getPolitenessState(record));
                    getPolitenessState(record), shouldMuteReason);
        }

        if (Flags.politeNotifications()) {
            // Update last alert time
            if (buzz || beep) {
@@ -594,41 +625,46 @@ public final class NotificationAttentionHelper {
                mNMP.getNotificationByKey(mVibrateNotificationKey));
    }

    boolean shouldMuteNotificationLocked(final NotificationRecord record, final Signals signals) {
    @MuteReason int shouldMuteNotificationLocked(final NotificationRecord record,
            final Signals signals, boolean hasAudibleAlert) {
        // Suppressed because no audible alert
        if (!hasAudibleAlert) {
            return MUTE_REASON_NOT_AUDIBLE;
        }
        // Suppressed because it's a silent update
        final Notification notification = record.getNotification();
        if (record.isUpdate && (notification.flags & FLAG_ONLY_ALERT_ONCE) != 0) {
            return true;
            return MUTE_REASON_SILENT_UPDATE;
        }

        // Suppressed because a user manually unsnoozed something (or similar)
        if (record.shouldPostSilently()) {
            return true;
            return MUTE_REASON_POST_SILENTLY;
        }

        // muted by listener
        final String disableEffects = disableNotificationEffects(record, signals.listenerHints);
        if (disableEffects != null) {
            ZenLog.traceDisableEffects(record, disableEffects);
            return true;
            return MUTE_REASON_LISTENER_HINT;
        }

        // suppressed due to DND
        if (record.isIntercepted()) {
            return true;
            return MUTE_REASON_DND;
        }

        // Suppressed because another notification in its group handles alerting
        if (record.getSbn().isGroup()) {
            if (notification.suppressAlertingDueToGrouping()) {
                return true;
                return MUTE_REASON_GROUP_ALERT;
            }
        }

        // Suppressed because notification was explicitly flagged as silent
        if (android.service.notification.Flags.notificationSilentFlag()) {
            if (notification.isSilent()) {
                return true;
                return MUTE_REASON_FLAG_SILENT;
            }
        }

@@ -636,12 +672,12 @@ public final class NotificationAttentionHelper {
        final String pkg = record.getSbn().getPackageName();
        if (mUsageStats.isAlertRateLimited(pkg)) {
            Slog.e(TAG, "Muting recently noisy " + record.getKey());
            return true;
            return MUTE_REASON_RATE_LIMIT;
        }

        // A different looping ringtone, such as an incoming call is playing
        if (isCurrentlyInsistent() && !isInsistentUpdate(record)) {
            return true;
            return MUTE_REASON_OTHER_INSISTENT_PLAYING;
        }

        // Suppressed since it's a non-interruptive update to a bubble-suppressed notification
@@ -650,11 +686,23 @@ public final class NotificationAttentionHelper {
        if (record.isUpdate && !record.isInterruptive() && isBubbleOrOverflowed
                && record.getNotification().getBubbleMetadata() != null) {
            if (record.getNotification().getBubbleMetadata().isNotificationSuppressed()) {
                return true;
                return MUTE_REASON_SUPPRESSED_BUBBLE;
            }
        }

        return false;
        if (isPoliteNotificationFeatureEnabled(record)) {
            // Notify the politeness strategy that an alerting notification is posted
            if (!isInsistentUpdate(record)) {
                mStrategy.onNotificationPosted(record);
            }

            // Suppress if politeness is muted and it's not an update for insistent
            if (getPolitenessState(record) == PolitenessStrategy.POLITE_STATE_MUTED) {
                return MUTE_REASON_COOLDOWN;
            }
        }

        return MUTE_REASON_NOT_MUTED;
    }

    private boolean isLoopingRingtoneNotification(final NotificationRecord playingRecord) {
@@ -1201,12 +1249,6 @@ public final class NotificationAttentionHelper {
            mApplyPerPackage = applyPerPackage;
        }

        boolean shouldIgnoreNotification(final NotificationRecord record) {
            // Ignore auto-group summaries => don't count them as app-posted notifications
            // for the cooldown budget
            return (record.getSbn().isGroup() && GroupHelper.isAggregatedGroup(record));
        }

        /**
         * Get the key that determines the grouping for the cooldown behavior.
         *
@@ -1358,10 +1400,6 @@ public final class NotificationAttentionHelper {

        @Override
        public void onNotificationPosted(final NotificationRecord record) {
            if (shouldIgnoreNotification(record)) {
                return;
            }

            long timeSinceLastNotif =
                    System.currentTimeMillis() - getLastNotificationUpdateTimeMs(record);

@@ -1434,10 +1472,6 @@ public final class NotificationAttentionHelper {
        @Override
        void onNotificationPosted(NotificationRecord record) {
            if (isAvalancheActive()) {
                if (shouldIgnoreNotification(record)) {
                    return;
                }

                long timeSinceLastNotif =
                    System.currentTimeMillis() - getLastNotificationUpdateTimeMs(record);

+148 −36
Original line number Diff line number Diff line
@@ -31,6 +31,12 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.media.AudioAttributes.USAGE_NOTIFICATION;
import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;

import static com.android.server.notification.NotificationAttentionHelper.MUTE_REASON_COOLDOWN;
import static com.android.server.notification.NotificationAttentionHelper.MUTE_REASON_FLAG_SILENT;
import static com.android.server.notification.NotificationAttentionHelper.MUTE_REASON_GROUP_ALERT;
import static com.android.server.notification.NotificationAttentionHelper.MUTE_REASON_NOT_MUTED;
import static com.android.server.notification.NotificationAttentionHelper.MUTE_REASON_OTHER_INSISTENT_PLAYING;

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertEquals;
@@ -106,6 +112,7 @@ import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.Notificat
import com.android.internal.config.sysui.TestableFlagResolver;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.InstanceIdSequenceFake;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.IntPair;
import com.android.server.UiServiceTestCase;
import com.android.server.lights.LightsManager;
@@ -1276,7 +1283,8 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
        verifyNeverBeep();
        assertFalse(r.isInterruptive());
        assertEquals(-1, r.getLastAudiblyAlertedMs());
        assertTrue(mAttentionHelper.shouldMuteNotificationLocked(r, DEFAULT_SIGNALS));
        assertThat(mAttentionHelper.shouldMuteNotificationLocked(r, DEFAULT_SIGNALS,
                true)).isEqualTo(MUTE_REASON_FLAG_SILENT);
    }

    @Test
@@ -1295,7 +1303,8 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
        verifyNeverBeep();
        assertFalse(r.isInterruptive());
        assertEquals(-1, r.getLastAudiblyAlertedMs());
        assertTrue(mAttentionHelper.shouldMuteNotificationLocked(r, DEFAULT_SIGNALS));
        assertThat(mAttentionHelper.shouldMuteNotificationLocked(r, DEFAULT_SIGNALS,
                true)).isEqualTo(MUTE_REASON_GROUP_ALERT);
    }

    @Test
@@ -1861,7 +1870,9 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
        verifyBeepLooped();

        NotificationRecord interrupter = getBeepyOtherNotification();
        assertTrue(mAttentionHelper.shouldMuteNotificationLocked(interrupter, DEFAULT_SIGNALS));
        assertThat(
                mAttentionHelper.shouldMuteNotificationLocked(interrupter, DEFAULT_SIGNALS,
                true)).isEqualTo(MUTE_REASON_OTHER_INSISTENT_PLAYING);
        mAttentionHelper.buzzBeepBlinkLocked(interrupter, DEFAULT_SIGNALS);

        verifyBeep(1);
@@ -1879,16 +1890,16 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
        ringtoneChannel.enableVibration(true);
        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
        mService.addNotification(ringtoneNotification);
        assertFalse(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
            DEFAULT_SIGNALS));
        assertThat(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
                DEFAULT_SIGNALS, true)).isEqualTo(MUTE_REASON_NOT_MUTED);
        mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
        verifyBeepLooped();
        verifyDelayedVibrateLooped();
        Mockito.reset(mVibrator);
        Mockito.reset(mRingtonePlayer);

        assertFalse(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
            DEFAULT_SIGNALS));
        assertThat(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
                DEFAULT_SIGNALS, true)).isEqualTo(MUTE_REASON_NOT_MUTED);
        mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);

        // beep wasn't reset
@@ -1907,8 +1918,8 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
        ringtoneChannel.enableVibration(true);
        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
        mService.addNotification(ringtoneNotification);
        assertFalse(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
            DEFAULT_SIGNALS));
        assertThat(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
                DEFAULT_SIGNALS, true)).isEqualTo(MUTE_REASON_NOT_MUTED);
        mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
        verifyBeepLooped();
        verifyDelayedVibrateLooped();
@@ -1930,8 +1941,8 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
        ringtoneChannel.enableVibration(true);
        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
        mService.addNotification(ringtoneNotification);
        assertFalse(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
            DEFAULT_SIGNALS));
        assertThat(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
                DEFAULT_SIGNALS, true)).isEqualTo(MUTE_REASON_NOT_MUTED);
        mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
        verifyBeepLooped();
        verifyNeverVibrate();
@@ -1951,14 +1962,15 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
            new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
        ringtoneChannel.enableVibration(true);
        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
        assertFalse(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
            DEFAULT_SIGNALS));
        assertThat(mAttentionHelper.shouldMuteNotificationLocked(ringtoneNotification,
                DEFAULT_SIGNALS, true)).isEqualTo(MUTE_REASON_NOT_MUTED);

        mAttentionHelper.buzzBeepBlinkLocked(ringtoneNotification, DEFAULT_SIGNALS);
        verifyVibrateLooped();

        NotificationRecord interrupter = getBuzzyOtherNotification();
        assertTrue(mAttentionHelper.shouldMuteNotificationLocked(interrupter, DEFAULT_SIGNALS));
        assertThat(mAttentionHelper.shouldMuteNotificationLocked(interrupter,
                DEFAULT_SIGNALS, true)).isEqualTo(MUTE_REASON_OTHER_INSISTENT_PLAYING);
        mAttentionHelper.buzzBeepBlinkLocked(interrupter, DEFAULT_SIGNALS);

        verifyVibrate(1);
@@ -2260,10 +2272,13 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {

        // 2nd update should beep at 0% volume
        Mockito.reset(mRingtonePlayer);
        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
        verifyBeepVolume(0.0f);
        int buzzBeepBlink = mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
        verifyNeverBeep();
        assertThat(buzzBeepBlink).isEqualTo(MetricsEvent.ALERT_MUTED | MUTE_REASON_COOLDOWN);
        assertThat(mAttentionHelper.shouldMuteNotificationLocked(r, DEFAULT_SIGNALS, true))
                .isEqualTo(MUTE_REASON_COOLDOWN);

        verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
        verify(mAccessibilityService, times(2)).sendAccessibilityEvent(any(), anyInt());
        assertEquals(-1, r.getLastAudiblyAlertedMs());
    }

@@ -2305,8 +2320,9 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {

        // 2nd update should beep at 0% volume
        Mockito.reset(mRingtonePlayer);
        mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
        verifyBeepVolume(0.0f);
        int buzzBeepBlink = mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
        verifyNeverBeep();
        assertThat(buzzBeepBlink).isEqualTo(MetricsEvent.ALERT_MUTED | MUTE_REASON_COOLDOWN);

        verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
        assertEquals(-1, r3.getLastAudiblyAlertedMs());
@@ -2381,9 +2397,10 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
                false, null, Notification.GROUP_ALERT_ALL, false, mUser, "anotherPkg");

        // update should beep at 0% volume
        mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
        int buzzBeepBlink = mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
        assertEquals(-1, r2.getLastAudiblyAlertedMs());
        verifyBeepVolume(0.0f);
        verifyNeverBeep();
        assertThat(buzzBeepBlink).isEqualTo(MetricsEvent.ALERT_MUTED | MUTE_REASON_COOLDOWN);

        // Use different package for next notifications
        NotificationRecord r3 = getNotificationRecord(mId, false /* insistent */, false /* once */,
@@ -2392,8 +2409,9 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {

        // 2nd update should beep at 0% volume
        Mockito.reset(mRingtonePlayer);
        mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
        verifyBeepVolume(0.0f);
        buzzBeepBlink = mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
        verifyNeverBeep();
        assertThat(buzzBeepBlink).isEqualTo(MetricsEvent.ALERT_MUTED | MUTE_REASON_COOLDOWN);

        verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
        assertEquals(-1, r3.getLastAudiblyAlertedMs());
@@ -2493,8 +2511,9 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {

        // Regular notification: should beep at 0% volume
        NotificationRecord r = getBeepyNotification();
        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
        verifyBeepVolume(0.0f);
        int buzzBeepBlink = mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
        verifyNeverBeep();
        assertThat(buzzBeepBlink).isEqualTo(MetricsEvent.ALERT_MUTED | MUTE_REASON_COOLDOWN);
        assertEquals(-1, r.getLastAudiblyAlertedMs());
        Mockito.reset(mRingtonePlayer);

@@ -2525,8 +2544,9 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {

        // 2nd update should beep at 0% volume
        Mockito.reset(mRingtonePlayer);
        mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
        verifyBeepVolume(0.0f);
        buzzBeepBlink = mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
        verifyNeverBeep();
        assertThat(buzzBeepBlink).isEqualTo(MetricsEvent.ALERT_MUTED | MUTE_REASON_COOLDOWN);

        // Set important conversation
        mChannel.setImportantConversation(true);
@@ -2751,9 +2771,10 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
        Mockito.reset(mRingtonePlayer);

        // next update at 0% volume
        mAttentionHelper.buzzBeepBlinkLocked(summary, DEFAULT_SIGNALS);
        int buzzBeepBlink = mAttentionHelper.buzzBeepBlinkLocked(summary, DEFAULT_SIGNALS);
        assertEquals(-1, summary.getLastAudiblyAlertedMs());
        verifyBeepVolume(0.0f);
        verifyNeverBeep();
        assertThat(buzzBeepBlink).isEqualTo(MetricsEvent.ALERT_MUTED | MUTE_REASON_COOLDOWN);

        verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
    }
@@ -2823,9 +2844,10 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {

        // 2nd update should beep at 0% volume
        Mockito.reset(mRingtonePlayer);
        mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
        int buzzBeepBlink = mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
        assertEquals(-1, r2.getLastAudiblyAlertedMs());
        verifyBeepVolume(0.0f);
        verifyNeverBeep();
        assertThat(buzzBeepBlink).isEqualTo(MetricsEvent.ALERT_MUTED | MUTE_REASON_COOLDOWN);

        // Use different package for next notifications
        NotificationRecord r3 = getNotificationRecord(mId, false /* insistent */, false /* once */,
@@ -2890,6 +2912,94 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
        assertNotEquals(-1, r3.getLastAudiblyAlertedMs());
    }

    @Test
    public void testBeepVolume_politeNotif_groupAlertSummary() throws Exception {
        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
        mSetFlagsRule.disableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
        TestableFlagResolver flagResolver = new TestableFlagResolver();
        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
        // NOTIFICATION_COOLDOWN_ALL setting is enabled
        Settings.System.putInt(getContext().getContentResolver(),
                Settings.System.NOTIFICATION_COOLDOWN_ALL, 1);
        initAttentionHelper(flagResolver);

        // child should beep at 0% volume
        NotificationRecord child = getBeepyNotificationRecord("a", GROUP_ALERT_SUMMARY);
        mAttentionHelper.buzzBeepBlinkLocked(child, DEFAULT_SIGNALS);
        verifyNeverBeep();
        assertFalse(child.isInterruptive());
        assertEquals(-1, child.getLastAudiblyAlertedMs());
        Mockito.reset(mRingtonePlayer);

        // child should beep at 0% volume
        child = getBeepyNotificationRecord("a", GROUP_ALERT_SUMMARY);
        mAttentionHelper.buzzBeepBlinkLocked(child, DEFAULT_SIGNALS);
        verifyNeverBeep();
        assertFalse(child.isInterruptive());
        assertEquals(-1, child.getLastAudiblyAlertedMs());
        Mockito.reset(mRingtonePlayer);

        // summary 100% volume (GROUP_ALERT_SUMMARY)
        NotificationRecord summary = getBeepyNotificationRecord("a", GROUP_ALERT_SUMMARY);
        summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
        mAttentionHelper.buzzBeepBlinkLocked(summary, DEFAULT_SIGNALS);
        assertNotEquals(-1, summary.getLastAudiblyAlertedMs());
        verifyBeepVolume(1.0f);
        Mockito.reset(mRingtonePlayer);

        // next update at 50% volume because only summary was tracked as alerting
        mAttentionHelper.buzzBeepBlinkLocked(summary, DEFAULT_SIGNALS);
        assertNotEquals(-1, summary.getLastAudiblyAlertedMs());
        verifyBeepVolume(0.5f);

        verify(mAccessibilityService, times(4)).sendAccessibilityEvent(any(), anyInt());
    }

    @Test
    public void testBeepVolume_politeNotif_groupAlertChildren() throws Exception {
        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
        mSetFlagsRule.disableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
        TestableFlagResolver flagResolver = new TestableFlagResolver();
        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
        // NOTIFICATION_COOLDOWN_ALL setting is enabled
        Settings.System.putInt(getContext().getContentResolver(),
                Settings.System.NOTIFICATION_COOLDOWN_ALL, 1);
        initAttentionHelper(flagResolver);

        // summary 0% volume (GROUP_ALERT_CHILDREN)
        NotificationRecord summary = getBeepyNotificationRecord("a", GROUP_ALERT_CHILDREN);
        summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
        mAttentionHelper.buzzBeepBlinkLocked(summary, DEFAULT_SIGNALS);
        verifyNeverBeep();
        assertFalse(summary.isInterruptive());
        assertEquals(-1, summary.getLastAudiblyAlertedMs());
        Mockito.reset(mRingtonePlayer);

        // child should beep at 100% volume
        NotificationRecord child = getBeepyNotificationRecord("a", GROUP_ALERT_CHILDREN);
        mAttentionHelper.buzzBeepBlinkLocked(child, DEFAULT_SIGNALS);
        assertNotEquals(-1, child.getLastAudiblyAlertedMs());
        verifyBeepVolume(1.0f);
        Mockito.reset(mRingtonePlayer);

        // child should beep at 50% volume
        child = getBeepyNotificationRecord("a", GROUP_ALERT_CHILDREN);
        mAttentionHelper.buzzBeepBlinkLocked(child, DEFAULT_SIGNALS);
        assertNotEquals(-1, child.getLastAudiblyAlertedMs());
        verifyBeepVolume(0.5f);
        Mockito.reset(mRingtonePlayer);

        // child should beep at 0% volume
        mAttentionHelper.buzzBeepBlinkLocked(child, DEFAULT_SIGNALS);
        verifyNeverBeep();
        assertTrue(child.isInterruptive());
        assertEquals(-1, child.getLastAudiblyAlertedMs());

        verify(mAccessibilityService, times(4)).sendAccessibilityEvent(any(), anyInt());
    }

    @Test
    public void testVibrationIntensity_politeNotif() throws Exception {
        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
@@ -2914,8 +3024,9 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
        Mockito.reset(vibratorHelper);

        // 2nd update should buzz at 0% intensity
        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
        verify(vibratorHelper, times(1)).scale(any(), eq(0.0f));
        int buzzBeepBlink = mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
        verifyNeverVibrate();
        assertThat(buzzBeepBlink).isEqualTo(MetricsEvent.ALERT_MUTED | MUTE_REASON_COOLDOWN);
    }

    @Test
@@ -3007,10 +3118,11 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {

        // 2nd update should beep at 0% volume
        Mockito.reset(mRingtonePlayer);
        mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS);
        verifyBeepVolume(0.0f);
        int buzzBeepBlink = mAttentionHelper.buzzBeepBlinkLocked(r, WORK_PROFILE_SIGNALS);
        verifyNeverBeep();
        assertThat(buzzBeepBlink).isEqualTo(MetricsEvent.ALERT_MUTED | MUTE_REASON_COOLDOWN);

        verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
        verify(mAccessibilityService, times(2)).sendAccessibilityEvent(any(), anyInt());
        assertEquals(-1, r.getLastAudiblyAlertedMs());
    }