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
This commit is contained in:
Matías Hernández
2023-03-02 19:06:56 +01:00
parent 6cab17b429
commit ac171dec4b
4 changed files with 94 additions and 76 deletions

View File

@@ -892,6 +892,7 @@ abstract public class ManagedServices {
return allowedComponents;
}
@NonNull
protected List<String> getAllowedPackages(int userId) {
final List<String> allowedPackages = new ArrayList<>();
synchronized (mApproved) {
@@ -1181,25 +1182,6 @@ abstract public class ManagedServices {
return installed;
}
protected Set<String> getAllowedPackages() {
final Set<String> allowedPackages = new ArraySet<>();
synchronized (mApproved) {
for (int k = 0; k < mApproved.size(); k++) {
ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.valueAt(k);
for (int i = 0; i < allowedByType.size(); i++) {
final ArraySet<String> 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<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);

View File

@@ -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<String> 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));
}
}
}

View File

@@ -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<String> allowedPackagesForUser0 = new ArrayList<>();
allowedPackagesForUser0.add("this.is.a.package.name");
allowedPackagesForUser0.add("another.package");
allowedPackagesForUser0.add("secondary");
List<String> actual = service.getAllowedPackages(0);
assertEquals(3, actual.size());
for (String pkg : allowedPackagesForUser0) {
assertTrue(actual.contains(pkg));
}
List<String> 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<String> 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<String> 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}) {

View File

@@ -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<List<String>>() {
@Override
public List<String> 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<Intent>() {
@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(),