Notification API hardening: add FLAG_SILENT
Replace Notification.GROUP_KEY_SILENT with FLAG_SILENT.
Prevents silent notifications to be forced grouped by GroupHelper.
Flag: android.service.notification.notification_silent_flag
Test: atest NotificationTest NotificationManagerServiceTest
Test: atest NotificationInterruptStateProviderImplTest
Test: atest VisualInterruptionDecisionProviderImplTest
Bug: 336488844
Change-Id: Icd0ae4d647223e481a1857f98b2732ccb52eddca
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 007fa5d..db979a5 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -762,6 +762,16 @@
@FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
public static final int FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY = 0x00010000;
+ /**
+ * Bit to be bitwise-ored into the {@link #flags} field that should be
+ * set by the system if this notification is silent.
+ *
+ * This flag is for internal use only; applications cannot set this flag directly.
+ * @hide
+ */
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG)
+ public static final int FLAG_SILENT = 1 << 17; //0x00020000
+
private static final List<Class<? extends Style>> PLATFORM_STYLE_CLASSES = Arrays.asList(
BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
@@ -784,7 +794,8 @@
FLAG_BUBBLE,
FLAG_NO_DISMISS,
FLAG_FSI_REQUESTED_BUT_DENIED,
- FLAG_USER_INITIATED_JOB
+ FLAG_USER_INITIATED_JOB,
+ FLAG_SILENT
})
@Retention(RetentionPolicy.SOURCE)
public @interface NotificationFlags{};
@@ -1692,6 +1703,7 @@
*
* @hide
*/
+ @Deprecated
public static final String GROUP_KEY_SILENT = "silent";
private int mGroupAlertBehavior = GROUP_ALERT_ALL;
@@ -3984,6 +3996,13 @@
}
}
+ if (android.service.notification.Flags.notificationSilentFlag()) {
+ if ((flags & FLAG_SILENT) != 0) {
+ flagStrings.add("SILENT");
+ flags &= ~FLAG_SILENT;
+ }
+ }
+
if (flagStrings.isEmpty()) {
return "0";
}
@@ -4320,6 +4339,31 @@
}
/**
+ * Sets the FLAG_SILENT flag to mark the notification as silent and clears the group key.
+ * @hide
+ */
+ public void fixSilentGroup() {
+ if (android.service.notification.Flags.notificationSilentFlag()) {
+ if (GROUP_KEY_SILENT.equals(mGroupKey)) {
+ mGroupKey = null;
+ flags |= FLAG_SILENT;
+ }
+ }
+ }
+
+ /**
+ * @return whether this notification is silent. See {@link Builder#setSilent()}
+ * @hide
+ */
+ public boolean isSilent() {
+ if (android.service.notification.Flags.notificationSilentFlag()) {
+ return (flags & Notification.FLAG_SILENT) != 0;
+ } else {
+ return GROUP_KEY_SILENT.equals(getGroup()) && suppressAlertingDueToGrouping();
+ }
+ }
+
+ /**
* Builder class for {@link Notification} objects.
*
* Provides a convenient way to set the various fields of a {@link Notification} and generate
@@ -4770,8 +4814,12 @@
mN.defaults &= ~DEFAULT_VIBRATE;
setDefaults(mN.defaults);
- if (TextUtils.isEmpty(mN.mGroupKey)) {
- setGroup(GROUP_KEY_SILENT);
+ if (android.service.notification.Flags.notificationSilentFlag()) {
+ mN.flags |= FLAG_SILENT;
+ } else {
+ if (TextUtils.isEmpty(mN.mGroupKey)) {
+ setGroup(GROUP_KEY_SILENT);
+ }
}
return this;
}
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index e9ad1c2..ef6ff05 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -84,6 +84,8 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.text.Spannable;
@@ -545,12 +547,26 @@
}
@Test
+ @DisableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG)
public void testBuilder_setSilent_emptyGroupKey_groupKeySilent() {
Notification emptyGroupKeyNotif = new Notification.Builder(mContext, "channelId")
.setGroup("")
.setSilent(true)
.build();
- assertEquals(GROUP_KEY_SILENT, emptyGroupKeyNotif.getGroup());
+ assertThat(emptyGroupKeyNotif.getGroup()).isEqualTo(GROUP_KEY_SILENT);
+ assertThat(emptyGroupKeyNotif.isSilent()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG)
+ public void testBuilder_setSilent_flagSilent() {
+ final String groupKey = "groupKey";
+ Notification emptyGroupKeyNotif = new Notification.Builder(mContext, "channelId")
+ .setGroup(groupKey)
+ .setSilent(true)
+ .build();
+ assertThat(emptyGroupKeyNotif.getGroup()).isEqualTo(groupKey);
+ assertThat(emptyGroupKeyNotif.isSilent()).isTrue();
}
@Test
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index 5e91786..e04e0fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -39,6 +39,7 @@
import android.provider.Settings
import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED
import android.provider.Settings.Global.HEADS_UP_OFF
+import android.service.notification.Flags
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage
@@ -221,6 +222,15 @@
entry.sbn.let { it.isGroup && it.notification.suppressAlertingDueToGrouping() }
}
+class HunSilentNotificationSuppressor() :
+ VisualInterruptionFilter(
+ types = setOf(PEEK, PULSE),
+ reason = "notification isSilent"
+ ) {
+ override fun shouldSuppress(entry: NotificationEntry) =
+ entry.sbn.let { Flags.notificationSilentFlag() && it.notification.isSilent }
+}
+
class HunJustLaunchedFsiSuppressor() :
VisualInterruptionFilter(types = setOf(PEEK, PULSE), reason = "just launched FSI") {
override fun shouldSuppress(entry: NotificationEntry) = entry.hasJustLaunchedFullScreenIntent()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt
index b77748e..576c7ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/FullScreenIntentDecisionProvider.kt
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSED_ONLY_BY_DND
import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_BUBBLE_METADATA
import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR
+import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_SILENT_NOTIFICATION
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR
@@ -94,6 +95,7 @@
uiEventId = FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA,
eventLogData = EventLogData("274759612", "bubbleMetadata")
),
+ NO_FSI_SUPPRESSIVE_SILENT_NOTIFICATION(false, "suppressive setSilent notification"),
NO_FSI_PACKAGE_SUSPENDED(false, "package suspended"),
FSI_DEVICE_NOT_INTERACTIVE(true, "device is not interactive"),
FSI_DEVICE_DREAMING(true, "device is dreaming"),
@@ -154,6 +156,12 @@
return NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR
}
+ if (android.service.notification.Flags.notificationSilentFlag()) {
+ if (sbn.notification.isSilent) {
+ return NO_FSI_SUPPRESSIVE_SILENT_NOTIFICATION
+ }
+ }
+
val bubbleMetadata = notification.bubbleMetadata
if (bubbleMetadata != null && bubbleMetadata.isNotificationSuppressed) {
return NO_FSI_SUPPRESSIVE_BUBBLE_METADATA
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index a2f97bd..e9efa56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -193,6 +193,14 @@
})
}
+ fun logNoAlertingSilentNotification(entry: NotificationEntry) {
+ buffer.log(TAG, DEBUG, {
+ str1 = entry.logKey
+ }, {
+ "No alerting: suppressed due to silent notification: $str1"
+ })
+ }
+
fun logNoAlertingSuppressedBy(
entry: NotificationEntry,
suppressor: NotificationInterruptSuppressor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index c084482..2b02790 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -67,6 +67,11 @@
*/
NO_FSI_SUPPRESSIVE_BUBBLE_METADATA(false),
/**
+ * Notification should not FSI due to being explicitly silent.
+ * see {@link android.app.Notification#isSilent}
+ */
+ NO_FSI_SUPPRESSIVE_SILENT_NOTIFICATION(false),
+ /**
* Device screen is off, so the FSI should launch.
*/
FSI_DEVICE_NOT_INTERACTIVE(true),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index fea360d..450067a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -295,8 +295,17 @@
// 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.
return getDecisionGivenSuppression(
- FullScreenIntentDecision.NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR,
+ FullScreenIntentDecision.NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR,
+ suppressedByDND);
+ }
+
+ // If the notification is explicitly silent, block FSI and warn.
+ if (android.service.notification.Flags.notificationSilentFlag()) {
+ if (sbn.getNotification().isSilent()) {
+ return getDecisionGivenSuppression(
+ FullScreenIntentDecision.NO_FSI_SUPPRESSIVE_SILENT_NOTIFICATION,
suppressedByDND);
+ }
}
// If the notification has suppressive BubbleMetadata, block FSI and warn.
@@ -587,8 +596,18 @@
StatusBarNotification sbn = entry.getSbn();
// Don't alert notifications that are suppressed due to group alert behavior
+ if (android.service.notification.Flags.notificationSilentFlag()) {
+ if (sbn.getNotification().isSilent()) {
+ if (log) {
+ mLogger.logNoAlertingSilentNotification(entry);
+ }
+ return false;
+ }
+ }
+
if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
- if (log) mLogger.logNoAlertingGroupAlertBehavior(entry);
+ if (log)
+ mLogger.logNoAlertingGroupAlertBehavior(entry);
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index 96f94ca..1c476ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -176,6 +176,7 @@
addFilter(BubbleNotAllowedSuppressor())
addFilter(BubbleNoMetadataSuppressor())
addFilter(HunGroupAlertBehaviorSuppressor())
+ addFilter(HunSilentNotificationSuppressor())
addFilter(HunJustLaunchedFsiSuppressor())
addFilter(AlertAppSuspendedSuppressor())
addFilter(AlertKeyguardVisibilitySuppressor(keyguardNotificationVisibilityProvider))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index bfe5c6e..61d14b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -60,6 +60,7 @@
import android.os.PowerManager;
import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -244,6 +245,50 @@
}
@Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG)
+ public void testShouldNotHeadsUp_silentNotification() {
+ // GIVEN state for "heads up when awake" is true
+ ensureStateForHeadsUpWhenAwake();
+
+ // WHEN the alert for a grouped notification is suppressed
+ // see {@link android.app.Notification#GROUP_ALERT_CHILDREN}
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(new Notification.Builder(getContext(), "a")
+ .setSilent(true)
+ .build())
+ .setImportance(IMPORTANCE_DEFAULT)
+ .build();
+
+ // THEN this entry shouldn't HUN
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse();
+ }
+
+ @Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG)
+ public void testShouldNotHeadsUp_silentNotificationFalse() {
+ // GIVEN state for "heads up when awake" is true
+ ensureStateForHeadsUpWhenAwake();
+
+ // WHEN the alert for a grouped notification is suppressed
+ // see {@link android.app.Notification#GROUP_ALERT_CHILDREN}
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg("a")
+ .setOpPkg("a")
+ .setTag("a")
+ .setNotification(new Notification.Builder(getContext(), "a")
+ .setSilent(false)
+ .build())
+ .setImportance(IMPORTANCE_HIGH)
+ .build();
+
+ // THEN this entry shouldn't HUN
+ assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+ }
+
+ @Test
public void testShouldHeadsUpWhenDozing() {
ensureStateForHeadsUpWhenDozing();
@@ -685,6 +730,26 @@
}
@Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG)
+ public void testShouldNotFullScreen_silentNotification() {
+ Notification n = new Notification.Builder(getContext(), "a")
+ .setContentTitle("title")
+ .setContentText("content text")
+ .setFullScreenIntent(mPendingIntent, true)
+ .setSilent(true)
+ .build();
+ NotificationEntry entry = createNotification(IMPORTANCE_HIGH, n);
+
+ assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
+ .isEqualTo(FullScreenIntentDecision.NO_FSI_SUPPRESSIVE_SILENT_NOTIFICATION);
+ assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
+ .isFalse();
+ verify(mLogger).logNoFullscreen(entry, "NO_FSI_SUPPRESSIVE_SILENT_NOTIFICATION");
+ verify(mLogger, never()).logFullscreen(any(), any());
+ verify(mLogger, never()).logNoFullscreenWarning(any(), any());
+ }
+
+ @Test
public void testShouldFullScreen_notInteractive() {
NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder("foo")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index 378705a..d5ab62b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -49,6 +49,7 @@
import android.hardware.display.FakeAmbientDisplayConfiguration
import android.os.Looper
import android.os.PowerManager
+import android.platform.test.annotations.EnableFlags
import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED
import android.provider.Settings.Global.HEADS_UP_OFF
import android.provider.Settings.Global.HEADS_UP_ON
@@ -532,6 +533,32 @@
}
@Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG)
+ fun testShouldNotHeadsUp_silentNotification() {
+ withPeekAndPulseEntry({
+ isGrouped = false
+ isGroupSummary = false
+ isSilent = true
+ }) {
+ assertShouldNotHeadsUp(it)
+ assertNoEventsLogged()
+ }
+ }
+
+ @Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG)
+ fun testShouldHeadsUp_silentNotificationFalse() {
+ withPeekAndPulseEntry({
+ isGrouped = false
+ isGroupSummary = false
+ isSilent = false
+ }) {
+ assertShouldHeadsUp(it)
+ assertNoEventsLogged()
+ }
+ }
+
+ @Test
fun testShouldNotHeadsUp_justLaunchedFsi() {
withPeekAndPulseEntry({ hasJustLaunchedFsi = true }) {
assertShouldNotHeadsUp(it)
@@ -1163,6 +1190,7 @@
var groupAlertBehavior: Int? = null
var hasBubbleMetadata = false
var hasFsi = false
+ var isSilent = false
// Set on Notification:
var isForegroundService = false
@@ -1233,6 +1261,8 @@
}
groupAlertBehavior?.let { nb.setGroupAlertBehavior(it) }
+ nb.setSilent(isSilent)
+
if (hasBubbleMetadata) {
nb.setBubbleMetadata(buildBubbleMetadata())
}
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index a7e14d9..614a0a5 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -604,6 +604,13 @@
}
}
+ // Suppressed because notification was explicitly flagged as silent
+ if (android.service.notification.Flags.notificationSilentFlag()) {
+ if (notification.isSilent()) {
+ return true;
+ }
+ }
+
// Suppressed for being too recently noisy
final String pkg = record.getSbn().getPackageName();
if (mUsageStats.isAlertRateLimited(pkg)) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 9e49827..1c40f44 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7961,6 +7961,10 @@
notification.setTimeoutAfter(NOTIFICATION_TTL);
}
}
+
+ if (notificationForceGrouping()) {
+ notification.fixSilentGroup();
+ }
}
/**
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 3da8031..2233aa2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -84,6 +84,8 @@
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
@@ -1221,6 +1223,44 @@
}
@Test
+ @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG)
+ public void testSilentNotification_flagSilent() throws Exception {
+ final Notification n = new Builder(getContext(), "test")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setSilent(true)
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 0, mTag, mUid,
+ mPid, n, mUser, null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn,
+ new NotificationChannel("test", "test", IMPORTANCE_HIGH));
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverBeep();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ assertTrue(mAttentionHelper.shouldMuteNotificationLocked(r, DEFAULT_SIGNALS));
+ }
+
+ @Test
+ @DisableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_SILENT_FLAG)
+ public void testSilentNotification_groupKeySilent() throws Exception {
+ final Notification n = new Builder(getContext(), "test")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setSilent(true)
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 0, mTag, mUid,
+ mPid, n, mUser, null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn,
+ new NotificationChannel("test", "test", IMPORTANCE_HIGH));
+
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverBeep();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ assertTrue(mAttentionHelper.shouldMuteNotificationLocked(r, DEFAULT_SIGNALS));
+ }
+
+ @Test
public void testHonorAlertOnlyOnceForBuzz() throws Exception {
NotificationRecord r = getBuzzyNotification();
NotificationRecord s = getBuzzyOnceNotification();