Loading core/java/android/util/SparseSetArray.java +5 −0 Original line number Diff line number Diff line Loading @@ -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); } } core/proto/android/service/notification.proto +11 −2 Original line number Diff line number Diff line Loading @@ -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 { Loading services/core/java/com/android/server/notification/ManagedServices.java +39 −17 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -386,12 +387,17 @@ abstract public class ManagedServices { } } pw.println(" Snoozed " + getCaption() + "s (" + mSnoozingForCurrentProfiles.size() + "):"); for (ComponentName name : mSnoozingForCurrentProfiles) { 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()); } } } } public void dump(ProtoOutputStream proto, DumpFilter filter) { proto.write(ManagedServicesProto.CAPTION, getCaption()); Loading Loading @@ -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); } } } Loading Loading @@ -975,6 +989,9 @@ abstract public class ManagedServices { synchronized (mApproved) { mApproved.remove(user); } synchronized (mSnoozing) { mSnoozing.remove(user); } rebindServices(true, user); } Loading Loading @@ -1066,15 +1083,17 @@ abstract public class ManagedServices { } protected void setComponentState(ComponentName component, int userId, boolean enabled) { boolean previous = !mSnoozingForCurrentProfiles.contains(component); synchronized (mSnoozing) { boolean previous = !mSnoozing.contains(userId, component); if (previous == enabled) { return; } if (enabled) { mSnoozingForCurrentProfiles.remove(component); mSnoozing.remove(userId, component); } else { mSnoozingForCurrentProfiles.add(component); mSnoozing.add(userId, component); } } // State changed Loading Loading @@ -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); Loading services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +64 −1 Original line number Diff line number Diff line Loading @@ -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}) { Loading Loading @@ -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>>() { Loading Loading @@ -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) { Loading Loading
core/java/android/util/SparseSetArray.java +5 −0 Original line number Diff line number Diff line Loading @@ -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); } }
core/proto/android/service/notification.proto +11 −2 Original line number Diff line number Diff line Loading @@ -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 { Loading
services/core/java/com/android/server/notification/ManagedServices.java +39 −17 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -386,12 +387,17 @@ abstract public class ManagedServices { } } pw.println(" Snoozed " + getCaption() + "s (" + mSnoozingForCurrentProfiles.size() + "):"); for (ComponentName name : mSnoozingForCurrentProfiles) { 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()); } } } } public void dump(ProtoOutputStream proto, DumpFilter filter) { proto.write(ManagedServicesProto.CAPTION, getCaption()); Loading Loading @@ -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); } } } Loading Loading @@ -975,6 +989,9 @@ abstract public class ManagedServices { synchronized (mApproved) { mApproved.remove(user); } synchronized (mSnoozing) { mSnoozing.remove(user); } rebindServices(true, user); } Loading Loading @@ -1066,15 +1083,17 @@ abstract public class ManagedServices { } protected void setComponentState(ComponentName component, int userId, boolean enabled) { boolean previous = !mSnoozingForCurrentProfiles.contains(component); synchronized (mSnoozing) { boolean previous = !mSnoozing.contains(userId, component); if (previous == enabled) { return; } if (enabled) { mSnoozingForCurrentProfiles.remove(component); mSnoozing.remove(userId, component); } else { mSnoozingForCurrentProfiles.add(component); mSnoozing.add(userId, component); } } // State changed Loading Loading @@ -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); Loading
services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +64 −1 Original line number Diff line number Diff line Loading @@ -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}) { Loading Loading @@ -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>>() { Loading Loading @@ -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) { Loading