From ac171dec4b1c833d1f7d9d348e7b92c1d42aacbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Thu, 2 Mar 2023 19:06:56 +0100 Subject: [PATCH] Send DND related broadcasts to the correct destinations Previously, broadcasts such as ACTION_INTERRUPTION_FILTER_CHANGED were sent to every ConditionProvider package for all started users, even if the package had DND Access only for some subset of them. Now, they are sent only to the profiles of the current user, and only to the packages that have such access. Fixes: 270338618 Test: New unit tests + manual verification Change-Id: I4909a76a1f160eb5fb0be734a170982ffd2c0a69 --- .../server/notification/ManagedServices.java | 20 +---- .../NotificationManagerService.java | 20 ++--- .../notification/ManagedServicesTest.java | 54 ++----------- .../NotificationManagerServiceTest.java | 76 ++++++++++++++++++- 4 files changed, 94 insertions(+), 76 deletions(-) diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 53e841d50b33f..73440b7f2eec4 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -892,6 +892,7 @@ abstract public class ManagedServices { return allowedComponents; } + @NonNull protected List getAllowedPackages(int userId) { final List allowedPackages = new ArrayList<>(); synchronized (mApproved) { @@ -1181,25 +1182,6 @@ abstract public class ManagedServices { return installed; } - protected Set getAllowedPackages() { - final Set allowedPackages = new ArraySet<>(); - synchronized (mApproved) { - for (int k = 0; k < mApproved.size(); k++) { - ArrayMap> allowedByType = mApproved.valueAt(k); - for (int i = 0; i < allowedByType.size(); i++) { - final ArraySet allowed = allowedByType.valueAt(i); - for (int j = 0; j < allowed.size(); j++) { - String pkgName = getPackageName(allowed.valueAt(j)); - if (!TextUtils.isEmpty(pkgName)) { - allowedPackages.add(pkgName); - } - } - } - } - } - return allowedPackages; - } - private void trimApprovedListsAccordingToInstalledServices(int userId) { synchronized (mApproved) { final ArrayMap> approvedByType = mApproved.get(userId); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 0d394570ab8e8..53b03d58beaee 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2709,16 +2709,18 @@ public class NotificationManagerService extends SystemService { } private void sendRegisteredOnlyBroadcast(String action) { - Intent intent = new Intent(action); - getContext().sendBroadcastAsUser(intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), - UserHandle.ALL, null); + int[] userIds = mUmInternal.getProfileIds(mAmi.getCurrentUserId(), true); + Intent intent = new Intent(action).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + for (int userId : userIds) { + getContext().sendBroadcastAsUser(intent, UserHandle.of(userId), null); + } // explicitly send the broadcast to all DND packages, even if they aren't currently running - intent.setFlags(0); - final Set dndApprovedPackages = mConditionProviders.getAllowedPackages(); - for (String pkg : dndApprovedPackages) { - intent.setPackage(pkg); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - getContext().sendBroadcastAsUser(intent, UserHandle.ALL); + for (int userId : userIds) { + for (String pkg : mConditionProviders.getAllowedPackages(userId)) { + Intent pkgIntent = new Intent(action).setPackage(pkg).setFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + getContext().sendBroadcastAsUser(pkgIntent, UserHandle.of(userId)); + } } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index 6f37e601abce7..ce076217f37b4 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -24,6 +24,8 @@ import static android.service.notification.NotificationListenerService.META_DATA import static com.android.server.notification.ManagedServices.APPROVAL_BY_COMPONENT; import static com.android.server.notification.ManagedServices.APPROVAL_BY_PACKAGE; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; @@ -1201,28 +1203,11 @@ public class ManagedServicesTest extends UiServiceTestCase { mIpm, approvalLevel); loadXml(service); - List allowedPackagesForUser0 = new ArrayList<>(); - allowedPackagesForUser0.add("this.is.a.package.name"); - allowedPackagesForUser0.add("another.package"); - allowedPackagesForUser0.add("secondary"); - - List actual = service.getAllowedPackages(0); - assertEquals(3, actual.size()); - for (String pkg : allowedPackagesForUser0) { - assertTrue(actual.contains(pkg)); - } - - List allowedPackagesForUser10 = new ArrayList<>(); - allowedPackagesForUser10.add("this.is.another.package"); - allowedPackagesForUser10.add("package"); - allowedPackagesForUser10.add("this.is.another.package"); - allowedPackagesForUser10.add("component"); - - actual = service.getAllowedPackages(10); - assertEquals(4, actual.size()); - for (String pkg : allowedPackagesForUser10) { - assertTrue(actual.contains(pkg)); - } + assertThat(service.getAllowedPackages(0)).containsExactly("this.is.a.package.name", + "another.package", "secondary"); + assertThat(service.getAllowedPackages(10)).containsExactly("this.is.another.package", + "package", "this.is.another.package", "component"); + assertThat(service.getAllowedPackages(999)).isEmpty(); } } @@ -1262,31 +1247,6 @@ public class ManagedServicesTest extends UiServiceTestCase { assertEquals(0, service.getAllowedComponents(10).size()); } - @Test - public void testGetAllowedPackages() throws Exception { - ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, - mIpm, APPROVAL_BY_COMPONENT); - loadXml(service); - service.mApprovalLevel = APPROVAL_BY_PACKAGE; - loadXml(service); - - List allowedPackages = new ArrayList<>(); - allowedPackages.add("this.is.a.package.name"); - allowedPackages.add("another.package"); - allowedPackages.add("secondary"); - allowedPackages.add("this.is.another.package"); - allowedPackages.add("package"); - allowedPackages.add("component"); - allowedPackages.add("bananas!"); - allowedPackages.add("non.user.set.package"); - - Set actual = service.getAllowedPackages(); - assertEquals(allowedPackages.size(), actual.size()); - for (String pkg : allowedPackages) { - assertTrue(actual.contains(pkg)); - } - } - @Test public void testOnUserRemoved() throws Exception { for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { 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 f08d0f5f71a49..354420f46c2f7 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -29,6 +29,7 @@ import static android.app.Notification.FLAG_FOREGROUND_SERVICE; import static android.app.Notification.FLAG_NO_CLEAR; import static android.app.Notification.FLAG_ONGOING_EVENT; import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE; +import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED; import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; @@ -50,7 +51,6 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; - import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_MUTABLE; import static android.app.PendingIntent.FLAG_ONE_SHOT; @@ -237,6 +237,7 @@ import com.android.server.utils.quota.MultiRateLimiter; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; +import com.google.android.collect.Lists; import com.google.common.collect.ImmutableList; import org.junit.After; @@ -245,10 +246,13 @@ import org.junit.Before; import org.junit.Test; 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; import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.io.BufferedInputStream; @@ -440,6 +444,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager); mContext.addMockSystemService(NotificationManager.class, mMockNm); + doNothing().when(mContext).sendBroadcastAsUser(any(), any()); doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any()); setDpmAppOppsExemptFromDismissal(false); @@ -7827,6 +7832,75 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { eq("another.package"), eq(rule), anyString()); } + @Test + public void onZenModeChanged_sendsBroadcasts() throws Exception { + when(mAmi.getCurrentUserId()).thenReturn(100); + when(mUmInternal.getProfileIds(eq(100), anyBoolean())).thenReturn(new int[]{100, 101, 102}); + when(mConditionProviders.getAllowedPackages(anyInt())).then(new Answer>() { + @Override + public List answer(InvocationOnMock invocation) { + int userId = invocation.getArgument(0); + switch (userId) { + case 100: + return Lists.newArrayList("a", "b", "c"); + case 101: + return Lists.newArrayList(); + case 102: + return Lists.newArrayList("b"); + default: + throw new IllegalArgumentException( + "Why would you ask for packages of userId " + userId + "?"); + } + } + }); + + mService.getBinderService().setZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS, null, + "testing!"); + waitForIdle(); + + InOrder inOrder = inOrder(mContext); + // Verify broadcasts for registered receivers + inOrder.verify(mContext).sendBroadcastAsUser(eqIntent( + new Intent(ACTION_INTERRUPTION_FILTER_CHANGED).setFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY)), eq(UserHandle.of(100)), eq(null)); + inOrder.verify(mContext).sendBroadcastAsUser(eqIntent( + new Intent(ACTION_INTERRUPTION_FILTER_CHANGED).setFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY)), eq(UserHandle.of(101)), eq(null)); + inOrder.verify(mContext).sendBroadcastAsUser(eqIntent( + new Intent(ACTION_INTERRUPTION_FILTER_CHANGED).setFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY)), eq(UserHandle.of(102)), eq(null)); + + // Verify broadcast for packages that manage DND. + inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(new Intent( + ACTION_INTERRUPTION_FILTER_CHANGED).setPackage("a").setFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)), eq(UserHandle.of(100))); + inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(new Intent( + ACTION_INTERRUPTION_FILTER_CHANGED).setPackage("b").setFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)), eq(UserHandle.of(100))); + inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(new Intent( + ACTION_INTERRUPTION_FILTER_CHANGED).setPackage("c").setFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)), eq(UserHandle.of(100))); + inOrder.verify(mContext).sendBroadcastAsUser(eqIntent(new Intent( + ACTION_INTERRUPTION_FILTER_CHANGED).setPackage("b").setFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)), eq(UserHandle.of(102))); + } + + private static Intent eqIntent(Intent wanted) { + return ArgumentMatchers.argThat( + new ArgumentMatcher() { + @Override + public boolean matches(Intent argument) { + return wanted.filterEquals(argument) + && wanted.getFlags() == argument.getFlags(); + } + + @Override + public String toString() { + return wanted.toString(); + } + }); + } + @Test public void testAreNotificationsEnabledForPackage() throws Exception { mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),