summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Matías Hernández <matiashe@google.com> 2025-01-22 18:44:41 +0100
committer Matías Hernández <matiashe@google.com> 2025-01-23 15:57:06 +0100
commitd1869f47d26a27b1fd4983c6a8fdbda4af84ab46 (patch)
tree030d7bbefc42f2ef01a5077063fd0dfc5f77b86e
parent354e328272b336ae6e026dcc2c61d37444ac35c7 (diff)
Throttle broadcasts of ACTION_EFFECTS_SUPPRESSOR_CHANGED
1) Don't broadcast at all if the set of suppressed effects has changed but not the set of suppressors themselves (e.g. if an NLS changes the hints it provides). 2) Instead of sending it out immediately, wait for 250 ms after the last change (and restart this wait if it changes again). Bug: 371776935 Test: atest NotificationManagerServiceTest Flag: com.android.server.notification.nm_binder_perf_throttle_effects_suppressor_broadcast Change-Id: I48645b08dd80b49729396c85db37525d16949d37
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java49
-rw-r--r--services/core/java/com/android/server/notification/ZenLog.java5
-rw-r--r--services/core/java/com/android/server/notification/flags.aconfig10
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java79
4 files changed, 130 insertions, 13 deletions
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 588e87924b7d..abef3ed44f87 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -632,6 +632,8 @@ public class NotificationManagerService extends SystemService {
// Minium number of sparse groups for a package before autogrouping them
private static final int AUTOGROUP_SPARSE_GROUPS_AT_COUNT = 3;
+ private static final Duration ZEN_BROADCAST_DELAY = Duration.ofMillis(250);
+
private IActivityManager mAm;
private ActivityTaskManagerInternal mAtm;
private ActivityManager mActivityManager;
@@ -3178,6 +3180,24 @@ public class NotificationManagerService extends SystemService {
sendRegisteredOnlyBroadcast(new Intent(action));
}
+ /**
+ * Schedules a broadcast to be sent to runtime receivers and DND-policy-access packages. The
+ * broadcast will be sent after {@link #ZEN_BROADCAST_DELAY}, unless a new broadcast is
+ * scheduled in the interim, in which case the previous one is dropped and the waiting period
+ * is <em>restarted</em>.
+ *
+ * <p>Note that this uses <em>equality of the {@link Intent#getAction}</em> as the criteria for
+ * deduplicating pending broadcasts, ignoring the extras and anything else. This is intentional
+ * so that e.g. rapidly changing some value A -> B -> C will only produce a broadcast for C
+ * (instead of every time because the extras are different).
+ */
+ private void sendZenBroadcastWithDelay(Intent intent) {
+ String token = "zen_broadcast:" + intent.getAction();
+ mHandler.removeCallbacksAndEqualMessages(token);
+ mHandler.postDelayed(() -> sendRegisteredOnlyBroadcast(intent), token,
+ ZEN_BROADCAST_DELAY.toMillis());
+ }
+
private void sendRegisteredOnlyBroadcast(Intent baseIntent) {
int[] userIds = mUmInternal.getProfileIds(mAmi.getCurrentUserId(), true);
if (Flags.nmBinderPerfReduceZenBroadcasts()) {
@@ -3371,14 +3391,25 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mNotificationLock")
private void updateEffectsSuppressorLocked() {
+ final long oldSuppressedEffects = mZenModeHelper.getSuppressedEffects();
final long updatedSuppressedEffects = calculateSuppressedEffects();
- if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
+ if (updatedSuppressedEffects == oldSuppressedEffects) return;
+
final List<ComponentName> suppressors = getSuppressors();
ZenLog.traceEffectsSuppressorChanged(
- mEffectsSuppressors, suppressors, updatedSuppressedEffects);
- mEffectsSuppressors = suppressors;
+ mEffectsSuppressors, suppressors, oldSuppressedEffects, updatedSuppressedEffects);
mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
- sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
+
+ if (Flags.nmBinderPerfThrottleEffectsSuppressorBroadcast()) {
+ if (!suppressors.equals(mEffectsSuppressors)) {
+ mEffectsSuppressors = suppressors;
+ sendZenBroadcastWithDelay(
+ new Intent(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED));
+ }
+ } else {
+ mEffectsSuppressors = suppressors;
+ sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
+ }
}
private void exitIdle() {
@@ -3500,12 +3531,18 @@ public class NotificationManagerService extends SystemService {
}
private ArrayList<ComponentName> getSuppressors() {
- ArrayList<ComponentName> names = new ArrayList<ComponentName>();
+ ArrayList<ComponentName> names = new ArrayList<>();
for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
ArraySet<ComponentName> serviceInfoList = mListenersDisablingEffects.valueAt(i);
for (ComponentName info : serviceInfoList) {
- names.add(info);
+ if (Flags.nmBinderPerfThrottleEffectsSuppressorBroadcast()) {
+ if (!names.contains(info)) {
+ names.add(info);
+ }
+ } else {
+ names.add(info);
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index 7e853d9d2d0b..49f93b8b7c16 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -140,8 +140,9 @@ public class ZenLog {
}
public static void traceEffectsSuppressorChanged(List<ComponentName> oldSuppressors,
- List<ComponentName> newSuppressors, long suppressedEffects) {
- append(TYPE_SUPPRESSOR_CHANGED, "suppressed effects:" + suppressedEffects + ","
+ List<ComponentName> newSuppressors, long oldSuppressedEffects, long suppressedEffects) {
+ append(TYPE_SUPPRESSOR_CHANGED, "suppressed effects:"
+ + oldSuppressedEffects + "->" + suppressedEffects + ","
+ componentListToString(oldSuppressors) + "->"
+ componentListToString(newSuppressors));
}
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 822ff48c831c..048f2b6b0cbc 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -182,6 +182,16 @@ flag {
}
flag {
+ name: "nm_binder_perf_throttle_effects_suppressor_broadcast"
+ namespace: "systemui"
+ description: "Delay sending the ACTION_EFFECTS_SUPPRESSOR_CHANGED broadcast if it changes too often"
+ bug: "371776935"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "fix_calling_uid_from_cps"
namespace: "systemui"
description: "Correctly checks zen rule ownership when a CPS notifies with a Condition"
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 43302264964e..872c029c4c5f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -51,6 +51,7 @@ import static android.app.NotificationChannel.RECS_ID;
import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
+import static android.app.NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED;
import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
@@ -123,7 +124,9 @@ import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICAT
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_LOCKDOWN;
@@ -163,6 +166,7 @@ import static junit.framework.Assert.fail;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyLong;
@@ -361,7 +365,6 @@ import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
-import org.mockito.ArgumentMatchers;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -369,6 +372,9 @@ import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -384,9 +390,6 @@ import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@RunWithLooper
@@ -11404,8 +11407,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mContext).sendBroadcastAsUser(eqIntent(expected), eq(UserHandle.of(mUserId)));
}
+ private static Intent isIntentWithAction(String wantedAction) {
+ return argThat(
+ intent -> intent != null && wantedAction.equals(intent.getAction())
+ );
+ }
+
private static Intent eqIntent(Intent wanted) {
- return ArgumentMatchers.argThat(
+ return argThat(
new ArgumentMatcher<Intent>() {
@Override
public boolean matches(Intent argument) {
@@ -17506,6 +17515,66 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags({Flags.FLAG_NM_BINDER_PERF_THROTTLE_EFFECTS_SUPPRESSOR_BROADCAST,
+ Flags.FLAG_NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS})
+ public void requestHintsFromListener_changingEffectsButNotSuppressor_noBroadcast()
+ throws Exception {
+ // Note that NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS is not strictly necessary; however each
+ // path will do slightly different calls so we force one of them to simplify the test.
+ when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+ INotificationListener token = mock(INotificationListener.class);
+ mService.isSystemUid = true;
+
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_CALL_EFFECTS);
+ mTestableLooper.moveTimeForward(500); // more than ZEN_BROADCAST_DELAY
+ waitForIdle();
+
+ verify(mContext, times(1)).sendBroadcastMultiplePermissions(
+ isIntentWithAction(ACTION_EFFECTS_SUPPRESSOR_CHANGED), any(), any(), any());
+
+ // Same suppressor suppresses something else.
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+ mTestableLooper.moveTimeForward(500); // more than ZEN_BROADCAST_DELAY
+ waitForIdle();
+
+ // Still 1 total calls (the previous one).
+ verify(mContext, times(1)).sendBroadcastMultiplePermissions(
+ isIntentWithAction(ACTION_EFFECTS_SUPPRESSOR_CHANGED), any(), any(), any());
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_NM_BINDER_PERF_THROTTLE_EFFECTS_SUPPRESSOR_BROADCAST,
+ Flags.FLAG_NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS})
+ public void requestHintsFromListener_changingSuppressor_throttlesBroadcast() throws Exception {
+ // Note that NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS is not strictly necessary; however each
+ // path will do slightly different calls so we force one of them to simplify the test.
+ when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+ INotificationListener token = mock(INotificationListener.class);
+ mService.isSystemUid = true;
+
+ // Several updates in quick succession.
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_CALL_EFFECTS);
+ mBinderService.clearRequestedListenerHints(token);
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+ mBinderService.clearRequestedListenerHints(token);
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_CALL_EFFECTS);
+ mBinderService.clearRequestedListenerHints(token);
+ mBinderService.requestHintsFromListener(token, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+
+ // No broadcasts yet!
+ verify(mContext, never()).sendBroadcastMultiplePermissions(any(), any(), any(), any());
+
+ mTestableLooper.moveTimeForward(500); // more than ZEN_BROADCAST_DELAY
+ waitForIdle();
+
+ // Only one broadcast after idle time.
+ verify(mContext, times(1)).sendBroadcastMultiplePermissions(
+ isIntentWithAction(ACTION_EFFECTS_SUPPRESSOR_CHANGED), any(), any(), any());
+ }
+
+ @Test
@EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
public void testApplyAdjustment_keyType_validType() throws Exception {
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);