Snooze NMS ManagedServices per user id, not globally

Listeners are bound and registered per user, so snoozing should be that way as well.

Test: atest ManagedServicesTest (and manually)
Fixes: 147075801
Fixes: 167152273
Change-Id: Ia61816518aa00f8a89f22e79ffdc048f3066541d
This commit is contained in:
Matías Hernández
2023-01-12 13:14:13 +01:00
parent 06da15b36e
commit 3482ed4c25
4 changed files with 119 additions and 20 deletions

View File

@@ -139,4 +139,9 @@ public class SparseSetArray<T> {
public T valueAt(int intIndex, int valueIndex) {
return mData.valueAt(intIndex).valueAt(valueIndex);
}
/** @return The set of values for key at position {@code intIndex}. */
public ArraySet<T> valuesAt(int intIndex) {
return mData.valueAt(intIndex);
}
}

View File

@@ -110,11 +110,20 @@ message ManagedServicesProto {
// All of this type/caption enabled for current profiles.
repeated android.content.ComponentNameProto enabled = 3;
repeated ManagedServiceInfoProto live_services = 4;
// Was: repeated ComponentNameProto, when snoozed services were not per-user-id.
reserved 5;
message SnoozedServices {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
optional int32 user_id = 1;
repeated android.content.ComponentNameProto snoozed = 2;
}
// Snoozed for current profiles.
repeated android.content.ComponentNameProto snoozed = 5;
repeated SnoozedServices snoozed = 6;
}
message RankingHelperProto {

View File

@@ -150,8 +150,9 @@ abstract public class ManagedServices {
= new ArraySet<>();
// Just the packages from mEnabledServicesForCurrentProfiles
private ArraySet<String> mEnabledServicesPackageNames = new ArraySet<>();
// List of enabled packages that have nevertheless asked not to be run
private ArraySet<ComponentName> mSnoozingForCurrentProfiles = new ArraySet<>();
// Per user id, list of enabled packages that have nevertheless asked not to be run
private final android.util.SparseSetArray<ComponentName> mSnoozing =
new android.util.SparseSetArray<>();
// List of approved packages or components (by user, then by primary/secondary) that are
// allowed to be bound as managed services. A package or component appearing in this list does
@@ -386,10 +387,15 @@ abstract public class ManagedServices {
}
}
pw.println(" Snoozed " + getCaption() + "s (" +
mSnoozingForCurrentProfiles.size() + "):");
for (ComponentName name : mSnoozingForCurrentProfiles) {
pw.println(" " + name.flattenToShortString());
synchronized (mSnoozing) {
pw.println(" Snoozed " + getCaption() + "s ("
+ mSnoozing.size() + "):");
for (int i = 0; i < mSnoozing.size(); i++) {
pw.println(" User: " + mSnoozing.keyAt(i));
for (ComponentName name : mSnoozing.valuesAt(i)) {
pw.println(" " + name.flattenToShortString());
}
}
}
}
@@ -431,8 +437,16 @@ abstract public class ManagedServices {
}
}
for (ComponentName name : mSnoozingForCurrentProfiles) {
name.dumpDebug(proto, ManagedServicesProto.SNOOZED);
synchronized (mSnoozing) {
for (int i = 0; i < mSnoozing.size(); i++) {
long token = proto.start(ManagedServicesProto.SNOOZED);
proto.write(ManagedServicesProto.SnoozedServices.USER_ID,
mSnoozing.keyAt(i));
for (ComponentName name : mSnoozing.valuesAt(i)) {
name.dumpDebug(proto, ManagedServicesProto.SnoozedServices.SNOOZED);
}
proto.end(token);
}
}
}
@@ -975,6 +989,9 @@ abstract public class ManagedServices {
synchronized (mApproved) {
mApproved.remove(user);
}
synchronized (mSnoozing) {
mSnoozing.remove(user);
}
rebindServices(true, user);
}
@@ -1066,15 +1083,17 @@ abstract public class ManagedServices {
}
protected void setComponentState(ComponentName component, int userId, boolean enabled) {
boolean previous = !mSnoozingForCurrentProfiles.contains(component);
if (previous == enabled) {
return;
}
synchronized (mSnoozing) {
boolean previous = !mSnoozing.contains(userId, component);
if (previous == enabled) {
return;
}
if (enabled) {
mSnoozingForCurrentProfiles.remove(component);
} else {
mSnoozingForCurrentProfiles.add(component);
if (enabled) {
mSnoozing.remove(userId, component);
} else {
mSnoozing.add(userId, component);
}
}
// State changed
@@ -1287,7 +1306,10 @@ abstract public class ManagedServices {
}
final Set<ComponentName> add = new HashSet<>(userComponents);
add.removeAll(mSnoozingForCurrentProfiles);
ArraySet<ComponentName> snoozed = mSnoozing.get(userId);
if (snoozed != null) {
add.removeAll(snoozed);
}
componentsToBind.put(userId, add);

View File

@@ -1581,6 +1581,55 @@ public class ManagedServicesTest extends UiServiceTestCase {
verify(context, never()).unbindService(any());
}
@Test
public void testSetComponentState_differentUsers() throws Exception {
Context context = mock(Context.class);
PackageManager pm = mock(PackageManager.class);
ApplicationInfo ai = new ApplicationInfo();
ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
when(context.getPackageName()).thenReturn(mContext.getPackageName());
when(context.getUserId()).thenReturn(mContext.getUserId());
when(context.getPackageManager()).thenReturn(pm);
when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
APPROVAL_BY_COMPONENT);
ComponentName cn = ComponentName.unflattenFromString("a/a");
addExpectedServices(service, Arrays.asList("a"), mZero.id);
addExpectedServices(service, Arrays.asList("a"), mTen.id);
when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
Object[] args = invocation.getArguments();
ServiceConnection sc = (ServiceConnection) args[1];
sc.onServiceConnected(cn, mock(IBinder.class));
return true;
});
service.addApprovedList("a/a", 0, true);
service.addApprovedList("a/a", 10, false);
service.registerService(cn, mZero.id);
assertTrue(service.isBound(cn, mZero.id));
service.onUserSwitched(mTen.id);
assertFalse(service.isBound(cn, mZero.id));
service.registerService(cn, mTen.id);
assertTrue(service.isBound(cn, mTen.id));
service.setComponentState(cn, mTen.id, false);
assertFalse(service.isBound(cn, mZero.id));
assertFalse(service.isBound(cn, mTen.id));
// Service should be rebound on user 0, since it was only disabled for user 10.
service.onUserSwitched(mZero.id);
assertTrue(service.isBound(cn, mZero.id));
assertFalse(service.isBound(cn, mTen.id));
// Service should stay unbound on going back to user 10.
service.onUserSwitched(mTen.id);
assertFalse(service.isBound(cn, mZero.id));
assertFalse(service.isBound(cn, mTen.id));
}
@Test
public void testOnPackagesChanged_nullValuesPassed_noNullPointers() {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
@@ -1847,7 +1896,7 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
private void addExpectedServices(final ManagedServices service, final List<String> packages,
int userId) {
int userId) throws Exception {
ManagedServices.Config config = service.getConfig();
when(mPm.queryIntentServicesAsUser(any(), anyInt(), eq(userId))).
thenAnswer(new Answer<List<ResolveInfo>>() {
@@ -1876,6 +1925,20 @@ public class ManagedServicesTest extends UiServiceTestCase {
return new ArrayList<>();
}
});
when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer(
(Answer<ServiceInfo>) invocation -> {
ComponentName invocationCn = invocation.getArgument(0);
if (invocationCn != null && packages.contains(invocationCn.getPackageName())) {
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.packageName = invocationCn.getPackageName();
serviceInfo.name = invocationCn.getClassName();
serviceInfo.permission = service.getConfig().bindPermission;
return serviceInfo;
}
return null;
}
);
}
private List<String> stringToList(String list) {