diff options
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); |