summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java10
-rw-r--r--services/core/java/com/android/server/notification/NotificationAttentionHelper.java302
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java188
3 files changed, 352 insertions, 148 deletions
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 1bd098291a2a..eeea17bf39dd 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -68,10 +68,10 @@ public class SystemUiSystemPropertiesFlags {
// TODO b/291899544: for released flags, use resource config values
/** Value used by polite notif. feature */
public static final Flag NOTIF_COOLDOWN_T1 = devFlag(
- "persist.debug.sysui.notification.notif_cooldown_t1", 5000);
+ "persist.debug.sysui.notification.notif_cooldown_t1", 60000);
/** Value used by polite notif. feature */
public static final Flag NOTIF_COOLDOWN_T2 = devFlag(
- "persist.debug.sysui.notification.notif_cooldown_t2", 3000);
+ "persist.debug.sysui.notification.notif_cooldown_t2", 5000);
/** Value used by polite notif. feature */
public static final Flag NOTIF_VOLUME1 = devFlag(
"persist.debug.sysui.notification.notif_volume1", 30);
@@ -80,12 +80,6 @@ public class SystemUiSystemPropertiesFlags {
/** Value used by polite notif. feature. -1 to ignore the counter */
public static final Flag NOTIF_COOLDOWN_COUNTER_RESET = devFlag(
"persist.debug.sysui.notification.notif_cooldown_counter_reset", 10);
- /**
- * Value used by polite notif. feature: cooldown behavior/strategy. Valid values: rule1,
- * rule2
- */
- public static final Flag NOTIF_COOLDOWN_RULE = devFlag(
- "persist.debug.sysui.notification.notif_cooldown_rule", "rule1");
/** b/303716154: For debugging only: use short bitmap duration. */
public static final Flag DEBUG_SHORT_BITMAP_DURATION = devFlag(
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index 0117c3ca633b..a6f71c29b380 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -94,8 +94,6 @@ public final class NotificationAttentionHelper {
private static final float DEFAULT_VOLUME = 1.0f;
// TODO (b/291899544): remove for release
- private static final String POLITE_STRATEGY1 = "rule1";
- private static final String POLITE_STRATEGY2 = "rule2";
private static final int DEFAULT_NOTIFICATION_COOLDOWN_ENABLED = 1;
private static final int DEFAULT_NOTIFICATION_COOLDOWN_ENABLED_FOR_WORK = 0;
private static final int DEFAULT_NOTIFICATION_COOLDOWN_ALL = 1;
@@ -146,7 +144,6 @@ public final class NotificationAttentionHelper {
private boolean mNotificationCooldownApplyToAll;
private boolean mNotificationCooldownVibrateUnlocked;
- private boolean mEnablePoliteNotificationsFeature;
private final PolitenessStrategy mStrategy;
private int mCurrentWorkProfileId = UserHandle.USER_NULL;
@@ -192,9 +189,7 @@ public final class NotificationAttentionHelper {
.build();
mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume);
- mEnablePoliteNotificationsFeature = Flags.politeNotifications();
-
- if (mEnablePoliteNotificationsFeature) {
+ if (Flags.politeNotifications()) {
mStrategy = getPolitenessStrategy();
} else {
mStrategy = null;
@@ -205,21 +200,23 @@ public final class NotificationAttentionHelper {
}
private PolitenessStrategy getPolitenessStrategy() {
- final String politenessStrategy = mFlagResolver.getStringValue(
- NotificationFlags.NOTIF_COOLDOWN_RULE);
+ if (Flags.crossAppPoliteNotifications()) {
+ PolitenessStrategy appStrategy = new StrategyPerApp(
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET));
- if (POLITE_STRATEGY2.equals(politenessStrategy)) {
- return new Strategy2(mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
+ return new StrategyGlobal(
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2),
mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
- mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2));
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
+ appStrategy);
} else {
- if (!POLITE_STRATEGY1.equals(politenessStrategy)) {
- Log.w(TAG, "Invalid cooldown strategy: " + politenessStrategy + ". Defaulting to "
- + POLITE_STRATEGY1);
- }
-
- return new Strategy1(mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
+ return new StrategyPerApp(
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2),
mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
@@ -266,7 +263,7 @@ public final class NotificationAttentionHelper {
mContext.getContentResolver().registerContentObserver(
SettingsObserver.NOTIFICATION_LIGHT_PULSE_URI, false, mSettingsObserver,
UserHandle.USER_ALL);
- if (mEnablePoliteNotificationsFeature) {
+ if (Flags.politeNotifications()) {
mContext.getContentResolver().registerContentObserver(
SettingsObserver.NOTIFICATION_COOLDOWN_ENABLED_URI, false, mSettingsObserver,
UserHandle.USER_ALL);
@@ -280,7 +277,7 @@ public final class NotificationAttentionHelper {
}
private void loadUserSettings() {
- if (mEnablePoliteNotificationsFeature) {
+ if (Flags.politeNotifications()) {
try {
mCurrentWorkProfileId = getManagedProfileId(ActivityManager.getCurrentUser());
@@ -301,6 +298,7 @@ public final class NotificationAttentionHelper {
mContext.getContentResolver(),
Settings.System.NOTIFICATION_COOLDOWN_ALL, DEFAULT_NOTIFICATION_COOLDOWN_ALL,
UserHandle.USER_CURRENT) != 0;
+ mStrategy.setApplyCooldownPerPackage(mNotificationCooldownApplyToAll);
if (Flags.vibrateWhileUnlocked()) {
mNotificationCooldownVibrateUnlocked = Settings.System.getIntForUser(
mContext.getContentResolver(),
@@ -484,10 +482,10 @@ public final class NotificationAttentionHelper {
getPolitenessState(record));
}
record.setAudiblyAlerted(buzz || beep);
- if (mEnablePoliteNotificationsFeature) {
+ if (Flags.politeNotifications()) {
// Update last alert time
if (buzz || beep) {
- record.getChannel().setLastNotificationUpdateTimeMs(System.currentTimeMillis());
+ mStrategy.setLastNotificationUpdateTimeMs(record, System.currentTimeMillis());
}
}
return buzzBeepBlinkLoggingCode;
@@ -620,7 +618,7 @@ public final class NotificationAttentionHelper {
private boolean isPoliteNotificationFeatureEnabled(final NotificationRecord record) {
// Check feature flag
- if (!mEnablePoliteNotificationsFeature) {
+ if (!Flags.politeNotifications()) {
return false;
}
@@ -1066,9 +1064,13 @@ public final class NotificationAttentionHelper {
// Volume for muted state
protected final float mVolumeMuted;
+ protected boolean mApplyPerPackage;
+ protected final Map<String, Long> mLastUpdatedTimestampByPackage;
+
public PolitenessStrategy(int timeoutPolite, int timeoutMuted, int volumePolite,
int volumeMuted) {
mVolumeStates = new HashMap<>();
+ mLastUpdatedTimestampByPackage = new HashMap<>();
this.mTimeoutPolite = timeoutPolite;
this.mTimeoutMuted = timeoutMuted;
@@ -1078,10 +1080,38 @@ public final class NotificationAttentionHelper {
abstract void onNotificationPosted(NotificationRecord record);
+ /**
+ * Set true if the cooldown strategy should apply per app(package).
+ * Otherwise apply per conversation channel.
+ * @param applyPerPackage if the cooldown should be applied per app
+ */
+ void setApplyCooldownPerPackage(boolean applyPerPackage) {
+ mApplyPerPackage = applyPerPackage;
+ }
+
+ boolean shouldIgnoreNotification(final NotificationRecord record) {
+ // Ignore group summaries
+ return (record.getSbn().isGroup() && record.getSbn().getNotification()
+ .isGroupSummary());
+ }
+
+ /**
+ * Get the key that determines the grouping for the cooldown behavior.
+ *
+ * @param record the notification being posted
+ * @return the key to group this notification under
+ */
String getChannelKey(final NotificationRecord record) {
- // use conversationId if it's a conversation
+ // Use conversationId if it's a conversation
String channelId = record.getChannel().getConversationId() != null
? record.getChannel().getConversationId() : record.getChannel().getId();
+
+ // Use only the package name to apply cooldown per app, unless the user explicitly
+ // changed the channel notification sound => treat separately
+ if (mApplyPerPackage && !record.getChannel().hasUserSetSound()) {
+ channelId = "";
+ }
+
return record.getSbn().getNormalizedUserId() + ":" + record.getSbn().getPackageName()
+ ":" + channelId;
}
@@ -1123,12 +1153,59 @@ public final class NotificationAttentionHelper {
final String key = getChannelKey(record);
// reset to default state after user interaction
mVolumeStates.put(key, POLITE_STATE_DEFAULT);
- record.getChannel().setLastNotificationUpdateTimeMs(0);
+ setLastNotificationUpdateTimeMs(record, 0);
}
public final @PolitenessState int getPolitenessState(final NotificationRecord record) {
return mVolumeStates.getOrDefault(getChannelKey(record), POLITE_STATE_DEFAULT);
}
+
+ void setLastNotificationUpdateTimeMs(final NotificationRecord record,
+ long timestampMillis) {
+ record.getChannel().setLastNotificationUpdateTimeMs(timestampMillis);
+ mLastUpdatedTimestampByPackage.put(record.getSbn().getPackageName(), timestampMillis);
+ }
+
+ long getLastNotificationUpdateTimeMs(final NotificationRecord record) {
+ if (record.getChannel().hasUserSetSound() || !mApplyPerPackage) {
+ return record.getChannel().getLastNotificationUpdateTimeMs();
+ } else {
+ return mLastUpdatedTimestampByPackage.getOrDefault(record.getSbn().getPackageName(),
+ 0L);
+ }
+ }
+
+ @PolitenessState int getNextState(@PolitenessState final int currState,
+ final long timeSinceLastNotif) {
+ @PolitenessState int nextState = currState;
+ switch (currState) {
+ case POLITE_STATE_DEFAULT:
+ if (timeSinceLastNotif < mTimeoutPolite) {
+ nextState = POLITE_STATE_POLITE;
+ }
+ break;
+ case POLITE_STATE_POLITE:
+ if (timeSinceLastNotif < mTimeoutMuted) {
+ nextState = POLITE_STATE_MUTED;
+ } else if (timeSinceLastNotif > mTimeoutPolite) {
+ nextState = POLITE_STATE_DEFAULT;
+ } else {
+ nextState = POLITE_STATE_POLITE;
+ }
+ break;
+ case POLITE_STATE_MUTED:
+ if (timeSinceLastNotif > mTimeoutMuted) {
+ nextState = POLITE_STATE_POLITE;
+ } else {
+ nextState = POLITE_STATE_MUTED;
+ }
+ break;
+ default:
+ Log.w(TAG, "getNextState unexpected volume state: " + currState);
+ break;
+ }
+ return nextState;
+ }
}
// TODO b/270456865: Only one of the two strategies will be released.
@@ -1145,72 +1222,51 @@ public final class NotificationAttentionHelper {
* after timeoutMuted.
* - Transitions back to the default state after a user interaction with a notification.
*/
- public static class Strategy1 extends PolitenessStrategy {
+ private static class StrategyPerApp extends PolitenessStrategy {
// Keep track of the number of notifications posted per channel
private final Map<String, Integer> mNumPosted;
// Reset to default state if number of posted notifications exceed this value when muted
private final int mMaxPostedForReset;
- public Strategy1(int timeoutPolite, int timeoutMuted, int volumePolite, int volumeMuted,
- int maxPosted) {
+ public StrategyPerApp(int timeoutPolite, int timeoutMuted, int volumePolite,
+ int volumeMuted, int maxPosted) {
super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted);
mNumPosted = new HashMap<>();
mMaxPostedForReset = maxPosted;
if (DEBUG) {
- Log.i(TAG, "Strategy1: " + timeoutPolite + " " + timeoutMuted);
+ Log.i(TAG, "StrategyPerApp: " + timeoutPolite + " " + timeoutMuted);
}
}
@Override
public void onNotificationPosted(final NotificationRecord record) {
- long timeSinceLastNotif = System.currentTimeMillis()
- - record.getChannel().getLastNotificationUpdateTimeMs();
+ if (shouldIgnoreNotification(record)) {
+ return;
+ }
+
+ long timeSinceLastNotif =
+ System.currentTimeMillis() - getLastNotificationUpdateTimeMs(record);
final String key = getChannelKey(record);
- @PolitenessState int volState = getPolitenessState(record);
+ @PolitenessState final int currState = getPolitenessState(record);
+ @PolitenessState int nextState = getNextState(currState, timeSinceLastNotif);
+ // Reset to default state if number of posted notifications exceed this value when muted
int numPosted = mNumPosted.getOrDefault(key, 0) + 1;
mNumPosted.put(key, numPosted);
-
- switch (volState) {
- case POLITE_STATE_DEFAULT:
- if (timeSinceLastNotif < mTimeoutPolite) {
- volState = POLITE_STATE_POLITE;
- }
- break;
- case POLITE_STATE_POLITE:
- if (timeSinceLastNotif < mTimeoutMuted) {
- volState = POLITE_STATE_MUTED;
- } else if (timeSinceLastNotif > mTimeoutPolite) {
- volState = POLITE_STATE_DEFAULT;
- } else {
- volState = POLITE_STATE_POLITE;
- }
- break;
- case POLITE_STATE_MUTED:
- if (timeSinceLastNotif > mTimeoutMuted) {
- volState = POLITE_STATE_POLITE;
- } else {
- volState = POLITE_STATE_MUTED;
- }
- if (numPosted >= mMaxPostedForReset) {
- volState = POLITE_STATE_DEFAULT;
- mNumPosted.put(key, 0);
- }
- break;
- default:
- Log.w(TAG, "onNotificationPosted unexpected volume state: " + volState);
- break;
+ if (currState == POLITE_STATE_MUTED && numPosted >= mMaxPostedForReset) {
+ nextState = POLITE_STATE_DEFAULT;
+ mNumPosted.put(key, 0);
}
if (DEBUG) {
Log.i(TAG, "onNotificationPosted time delta: " + timeSinceLastNotif + " vol state: "
- + volState + " key: " + key + " numposted " + numPosted);
+ + nextState + " key: " + key + " numposted " + numPosted);
}
- mVolumeStates.put(key, volState);
+ mVolumeStates.put(key, nextState);
}
@Override
@@ -1221,61 +1277,98 @@ public final class NotificationAttentionHelper {
}
/**
- * Polite notification strategy 2:
- * - Transitions from default (loud) => muted state if a notification
- * alerts the same channel before timeoutPolite.
- * - Transitions from polite => default state if a notification
- * alerts the same channel before timeoutMuted.
- * - Transitions from muted => default state if a notification alerts after timeoutMuted,
- * otherwise transitions to the polite state.
- * - Transitions back to the default state after a user interaction with a notification.
+ * Global (cross-app) strategy.
*/
- public static class Strategy2 extends PolitenessStrategy {
- public Strategy2(int timeoutPolite, int timeoutMuted, int volumePolite, int volumeMuted) {
+ private static class StrategyGlobal extends PolitenessStrategy {
+ private static final String COMMON_KEY = "cross_app_common_key";
+
+ private final PolitenessStrategy mAppStrategy;
+ private long mLastNotificationTimestamp = 0;
+
+ public StrategyGlobal(int timeoutPolite, int timeoutMuted, int volumePolite,
+ int volumeMuted, PolitenessStrategy appStrategy) {
super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted);
+ mAppStrategy = appStrategy;
+
if (DEBUG) {
- Log.i(TAG, "Strategy2: " + timeoutPolite + " " + timeoutMuted);
+ Log.i(TAG, "StrategyGlobal: " + timeoutPolite + " " + timeoutMuted);
}
}
@Override
- public void onNotificationPosted(final NotificationRecord record) {
- long timeSinceLastNotif = System.currentTimeMillis()
- - record.getChannel().getLastNotificationUpdateTimeMs();
+ void onNotificationPosted(NotificationRecord record) {
+ if (shouldIgnoreNotification(record)) {
+ return;
+ }
+
+ long timeSinceLastNotif =
+ System.currentTimeMillis() - getLastNotificationUpdateTimeMs(record);
final String key = getChannelKey(record);
- @PolitenessState int volState = getPolitenessState(record);
+ @PolitenessState final int currState = getPolitenessState(record);
+ @PolitenessState int nextState = getNextState(currState, timeSinceLastNotif);
- switch (volState) {
- case POLITE_STATE_DEFAULT:
- if (timeSinceLastNotif < mTimeoutPolite) {
- volState = POLITE_STATE_MUTED;
- }
- break;
- case POLITE_STATE_POLITE:
- if (timeSinceLastNotif > mTimeoutMuted) {
- volState = POLITE_STATE_DEFAULT;
- }
- break;
- case POLITE_STATE_MUTED:
- if (timeSinceLastNotif > mTimeoutMuted) {
- volState = POLITE_STATE_DEFAULT;
- } else {
- volState = POLITE_STATE_POLITE;
- }
- break;
- default:
- Log.w(TAG, "onNotificationPosted unexpected volume state: " + volState);
- break;
+ if (DEBUG) {
+ Log.i(TAG, "StrategyGlobal onNotificationPosted time delta: " + timeSinceLastNotif
+ + " vol state: " + nextState + " key: " + key);
}
- if (DEBUG) {
- Log.i(TAG, "onNotificationPosted time delta: " + timeSinceLastNotif + " vol state: "
- + volState + " key: " + key);
+ mVolumeStates.put(key, nextState);
+
+ mAppStrategy.onNotificationPosted(record);
+ }
+
+ @Override
+ public float getSoundVolume(final NotificationRecord record) {
+ final @PolitenessState int globalVolState = getPolitenessState(record);
+ final @PolitenessState int appVolState = mAppStrategy.getPolitenessState(record);
+
+ // Prioritize the most polite outcome
+ if (globalVolState > appVolState) {
+ return super.getSoundVolume(record);
+ } else {
+ return mAppStrategy.getSoundVolume(record);
}
+ }
+
+ @Override
+ public void onUserInteraction(final NotificationRecord record) {
+ super.onUserInteraction(record);
+ mAppStrategy.onUserInteraction(record);
+ }
- mVolumeStates.put(key, volState);
+ @Override
+ String getChannelKey(final NotificationRecord record) {
+ // If the user explicitly changed the channel notification sound:
+ // handle as a separate channel
+ if (record.getChannel().hasUserSetSound()) {
+ return super.getChannelKey(record);
+ } else {
+ // Use one global key per user
+ return record.getSbn().getNormalizedUserId() + ":" + COMMON_KEY;
+ }
+ }
+
+ @Override
+ public void setLastNotificationUpdateTimeMs(NotificationRecord record,
+ long timestampMillis) {
+ super.setLastNotificationUpdateTimeMs(record, timestampMillis);
+ mLastNotificationTimestamp = timestampMillis;
+ }
+
+ long getLastNotificationUpdateTimeMs(final NotificationRecord record) {
+ if (record.getChannel().hasUserSetSound()) {
+ return super.getLastNotificationUpdateTimeMs(record);
+ } else {
+ return mLastNotificationTimestamp;
+ }
+ }
+
+ @Override
+ void setApplyCooldownPerPackage(boolean applyPerPackage) {
+ super.setApplyCooldownPerPackage(applyPerPackage);
+ mAppStrategy.setApplyCooldownPerPackage(applyPerPackage);
}
}
@@ -1340,7 +1433,7 @@ public final class NotificationAttentionHelper {
updateLightsLocked();
}
}
- if (mEnablePoliteNotificationsFeature) {
+ if (Flags.politeNotifications()) {
if (NOTIFICATION_COOLDOWN_ENABLED_URI.equals(uri)) {
mNotificationCooldownEnabled = Settings.System.getIntForUser(
mContext.getContentResolver(),
@@ -1365,6 +1458,7 @@ public final class NotificationAttentionHelper {
Settings.System.NOTIFICATION_COOLDOWN_ALL,
DEFAULT_NOTIFICATION_COOLDOWN_ALL, UserHandle.USER_CURRENT)
!= 0;
+ mStrategy.setApplyCooldownPerPackage(mNotificationCooldownApplyToAll);
}
if (Flags.vibrateWhileUnlocked()) {
if (NOTIFICATION_COOLDOWN_VIBRATE_UNLOCKED_URI.equals(uri)) {
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 220321d19dac..1b77b99e7d3e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -19,17 +19,21 @@ import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.GROUP_ALERT_ALL;
import static android.app.Notification.GROUP_ALERT_CHILDREN;
import static android.app.Notification.GROUP_ALERT_SUMMARY;
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.media.AudioAttributes.USAGE_NOTIFICATION;
import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -195,7 +199,8 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
// TODO (b/291907312): remove feature flag
mSetFlagsRule.enableFlags(Flags.FLAG_REFACTOR_ATTENTION_HELPER);
// Disable feature flags by default. Tests should enable as needed.
- mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS, Flags.FLAG_EXPIRE_BITMAPS);
+ mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS,
+ Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS, Flags.FLAG_VIBRATE_WHILE_UNLOCKED);
mService = spy(new NotificationManagerService(getContext(), mNotificationRecordLogger,
mNotificationInstanceIdSequence));
@@ -364,10 +369,20 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
}
private NotificationRecord getNotificationRecord(int id,
- boolean insistent, boolean once,
- boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
- boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
- boolean isLeanback, UserHandle userHandle) {
+ boolean insistent, boolean once,
+ boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
+ boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
+ boolean isLeanback, UserHandle userHandle) {
+ return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, defaultVibration,
+ defaultSound, defaultLights, groupKey, groupAlertBehavior, isLeanback, userHandle,
+ mPkg);
+ }
+
+ private NotificationRecord getNotificationRecord(int id,
+ boolean insistent, boolean once,
+ boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
+ boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
+ boolean isLeanback, UserHandle userHandle, String packageName) {
final Builder builder = new Builder(getContext())
.setContentTitle("foo")
@@ -427,8 +442,8 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
when(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK))
.thenReturn(isLeanback);
- StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
- mPid, n, userHandle, null, System.currentTimeMillis());
+ StatusBarNotification sbn = new StatusBarNotification(packageName, packageName, id, mTag,
+ mUid, mPid, n, userHandle, null, System.currentTimeMillis());
NotificationRecord r = new NotificationRecord(context, sbn, mChannel);
mService.addNotification(r);
return r;
@@ -1990,7 +2005,6 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
public void testBeepVolume_politeNotif() throws Exception {
mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
TestableFlagResolver flagResolver = new TestableFlagResolver();
- flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
initAttentionHelper(flagResolver);
@@ -2015,13 +2029,11 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
- // TODO b/270456865: Only one of the two strategies will be released.
- // The other one need to be removed
@Test
- public void testBeepVolume_politeNotif_Strategy2() throws Exception {
+ public void testBeepVolume_politeNotif_GlobalStrategy() throws Exception {
mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
TestableFlagResolver flagResolver = new TestableFlagResolver();
- flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule2");
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
initAttentionHelper(flagResolver);
@@ -2032,14 +2044,58 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
Mockito.reset(mRingtonePlayer);
- // update should beep at 0% volume
- r.isUpdate = true;
- mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ // Use different package for next notifications
+ NotificationRecord r2 = getNotificationRecord(mId, false /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */, true, true,
+ false, null, Notification.GROUP_ALERT_ALL, false, mUser, "anotherPkg");
+
+ // update should beep at 50% volume
+ mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.5f);
+
+ // Use different package for next notifications
+ NotificationRecord r3 = getNotificationRecord(mId, false /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */, true, true,
+ false, null, Notification.GROUP_ALERT_ALL, false, mUser, "yetAnotherPkg");
+
+ // 2nd update should beep at 0% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
verifyBeepVolume(0.0f);
+ verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testBeepVolume_politeNotif_GlobalStrategy_ChannelHasUserSound() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+ initAttentionHelper(flagResolver);
+
+ NotificationRecord r = getBeepyNotification();
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // Use package with user-set sounds for next notifications
+ mChannel = new NotificationChannel("test2", "test2", IMPORTANCE_DEFAULT);
+ mChannel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
+ NotificationRecord r2 = getNotificationRecord(mId, false /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */, true, true,
+ false, null, Notification.GROUP_ALERT_ALL, false, mUser, "anotherPkg");
+
+ // update should beep at 100% volume
+ mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+ verifyBeepVolume(1.0f);
+
// 2nd update should beep at 50% volume
Mockito.reset(mRingtonePlayer);
- mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
verifyBeepVolume(0.5f);
verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
@@ -2047,39 +2103,101 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
}
@Test
- public void testVibrationIntensity_politeNotif() throws Exception {
+ public void testBeepVolume_politeNotif_applyPerApp() throws Exception {
mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ mSetFlagsRule.disableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
TestableFlagResolver flagResolver = new TestableFlagResolver();
- flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
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);
- NotificationRecord r = getBuzzyBeepyNotification();
+ NotificationRecord r = getBeepyNotification();
// set up internal state
mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
- VibratorHelper vibratorHelper = mAttentionHelper.getVibratorHelper();
- Mockito.reset(vibratorHelper);
+ // Use different channel for next notifications
+ mChannel = new NotificationChannel("test2", "test2", IMPORTANCE_DEFAULT);
- // update should buzz at 50% intensity
- r.isUpdate = true;
- mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ // update should beep at 50% volume
+ NotificationRecord r2 = getBeepyNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.5f);
- verify(vibratorHelper, times(1)).scale(any(), eq(0.5f));
- Mockito.reset(vibratorHelper);
+ // 2nd update should beep at 0% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.0f);
- // 2nd update should buzz at 0% intensity
+ // Use different package for next notifications
+ NotificationRecord r3 = getNotificationRecord(mId, false /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */, true, true,
+ false, null, Notification.GROUP_ALERT_ALL, false, mUser, "anotherPkg");
+
+ // Update from new package should beep at 100% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
+ verifyBeepVolume(1.0f);
+
+ verify(mAccessibilityService, times(4)).sendAccessibilityEvent(any(), anyInt());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testBeepVolume_politeNotif_applyPerApp_ChannelHasUserSound() 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);
+
+ NotificationRecord r = getBeepyNotification();
+
+ // set up internal state
mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
- verify(vibratorHelper, times(1)).scale(any(), eq(0.0f));
+ Mockito.reset(mRingtonePlayer);
+
+ // Use different channel for next notifications
+ mChannel = new NotificationChannel("test2", "test2", IMPORTANCE_DEFAULT);
+ mChannel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
+
+ // update should beep at 100% volume
+ NotificationRecord r2 = getBeepyNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+ verifyBeepVolume(1.0f);
+
+ // 2nd update should beep at 50% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+ verifyBeepVolume(0.5f);
+
+ // Use different package for next notifications
+ mChannel = new NotificationChannel("test3", "test3", IMPORTANCE_DEFAULT);
+ NotificationRecord r3 = getNotificationRecord(mId, false /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */, true, true,
+ false, null, Notification.GROUP_ALERT_ALL, false, mUser, "anotherPkg");
+
+ // Update from new package should beep at 100% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
+ verifyBeepVolume(1.0f);
+
+ verify(mAccessibilityService, times(4)).sendAccessibilityEvent(any(), anyInt());
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@Test
- public void testVibrationIntensity_politeNotif_Strategy2() throws Exception {
+ public void testVibrationIntensity_politeNotif() throws Exception {
mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
TestableFlagResolver flagResolver = new TestableFlagResolver();
- flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule2");
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
initAttentionHelper(flagResolver);
@@ -2092,16 +2210,16 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
VibratorHelper vibratorHelper = mAttentionHelper.getVibratorHelper();
Mockito.reset(vibratorHelper);
- // update should buzz at 0% intensity
+ // update should buzz at 50% intensity
r.isUpdate = true;
mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
- verify(vibratorHelper, times(1)).scale(any(), eq(0.0f));
+ verify(vibratorHelper, times(1)).scale(any(), eq(0.5f));
Mockito.reset(vibratorHelper);
- // 2nd update should buzz at 50% intensity
+ // 2nd update should buzz at 0% intensity
mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
- verify(vibratorHelper, times(1)).scale(any(), eq(0.5f));
+ verify(vibratorHelper, times(1)).scale(any(), eq(0.0f));
}
@Test
@@ -2162,7 +2280,6 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
public void testBeepVolume_politeNotif_workProfile() throws Exception {
mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
TestableFlagResolver flagResolver = new TestableFlagResolver();
- flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
@@ -2203,7 +2320,6 @@ public class NotificationAttentionHelperTest extends UiServiceTestCase {
public void testBeepVolume_politeNotif_workProfile_disabled() throws Exception {
mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
TestableFlagResolver flagResolver = new TestableFlagResolver();
- flagResolver.setFlagOverride(NotificationFlags.NOTIF_COOLDOWN_RULE, "rule1");
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);