Loading services/core/java/com/android/server/notification/ManagedServices.java +85 −25 Original line number Diff line number Diff line Loading @@ -155,7 +155,9 @@ abstract public class ManagedServices { // 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 // not mean that we are currently bound to said package/component. protected ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>(); @GuardedBy("mApproved") protected final ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>(); // List of packages or components (by user) that are configured to be enabled/disabled // explicitly by the user Loading Loading @@ -871,6 +873,23 @@ abstract public class ManagedServices { return false; } protected boolean isPackageOrComponentAllowedWithPermission(ComponentName component, int userId) { if (!(isPackageOrComponentAllowed(component.flattenToString(), userId) || isPackageOrComponentAllowed(component.getPackageName(), userId))) { return false; } return componentHasBindPermission(component, userId); } private boolean componentHasBindPermission(ComponentName component, int userId) { ServiceInfo info = getServiceInfo(component, userId); if (info == null) { return false; } return mConfig.bindPermission.equals(info.permission); } boolean isPackageOrComponentUserSet(String pkgOrComponent, int userId) { synchronized (mApproved) { ArraySet<String> services = mUserSetServices.get(userId); Loading Loading @@ -928,6 +947,7 @@ abstract public class ManagedServices { for (int uid : uidList) { if (isPackageAllowed(pkgName, UserHandle.getUserId(uid))) { anyServicesInvolved = true; trimApprovedListsForInvalidServices(pkgName, UserHandle.getUserId(uid)); } } } Loading Loading @@ -1057,8 +1077,7 @@ abstract public class ManagedServices { for (int i = 0; i < userIds.size(); i++) { final int userId = userIds.get(i); if (enabled) { if (isPackageOrComponentAllowed(component.flattenToString(), userId) || isPackageOrComponentAllowed(component.getPackageName(), userId)) { if (isPackageOrComponentAllowedWithPermission(component, userId)) { registerServiceLocked(component, userId); } else { Slog.d(TAG, component + " no longer has permission to be bound"); Loading Loading @@ -1197,6 +1216,33 @@ abstract public class ManagedServices { return removed; } private void trimApprovedListsForInvalidServices(String packageName, int userId) { synchronized (mApproved) { final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId); if (approvedByType == null) { return; } for (int i = 0; i < approvedByType.size(); i++) { final ArraySet<String> approved = approvedByType.valueAt(i); for (int j = approved.size() - 1; j >= 0; j--) { final String approvedPackageOrComponent = approved.valueAt(j); if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) { final ComponentName component = ComponentName.unflattenFromString( approvedPackageOrComponent); if (component != null && !componentHasBindPermission(component, userId)) { approved.removeAt(j); if (DEBUG) { Slog.v(TAG, "Removing " + approvedPackageOrComponent + " from approved list; no bind permission found " + mConfig.bindPermission); } } } } } } } protected String getPackageName(String packageOrComponent) { final ComponentName component = ComponentName.unflattenFromString(packageOrComponent); if (component != null) { Loading Loading @@ -1380,12 +1426,7 @@ abstract public class ManagedServices { final int userId = componentsToBind.keyAt(i); final Set<ComponentName> add = componentsToBind.get(userId); for (ComponentName component : add) { try { ServiceInfo info = mPm.getServiceInfo(component, PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); ServiceInfo info = getServiceInfo(component, userId); if (info == null) { Slog.w(TAG, "Not binding " + getCaption() + " service " + component + ": service not found"); Loading @@ -1399,9 +1440,6 @@ abstract public class ManagedServices { Slog.v(TAG, "enabling " + getCaption() + " for " + userId + ": " + component); registerService(info, userId); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } } } Loading @@ -1422,6 +1460,15 @@ abstract public class ManagedServices { } } @VisibleForTesting void reregisterService(final ComponentName cn, final int userId) { // If rebinding a package that died, ensure it still has permission // after the rebind delay if (isPackageOrComponentAllowedWithPermission(cn, userId)) { registerService(cn, userId); } } /** * Inject a system service into the management list. */ Loading Loading @@ -1523,7 +1570,7 @@ abstract public class ManagedServices { mHandler.postDelayed(new Runnable() { @Override public void run() { registerService(name, userid); reregisterService(name, userid); } }, ON_BINDING_DIED_REBIND_DELAY_MS); } else { Loading Loading @@ -1655,6 +1702,19 @@ abstract public class ManagedServices { } } private ServiceInfo getServiceInfo(ComponentName component, int userId) { try { return mPm.getServiceInfo(component, PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); } catch (RemoteException e) { e.rethrowFromSystemServer(); } return null; } public class ManagedServiceInfo implements IBinder.DeathRecipient { public IInterface service; public ComponentName component; Loading services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +54 −0 Original line number Diff line number Diff line Loading @@ -27,8 +27,10 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; Loading Loading @@ -955,6 +957,58 @@ public class ManagedServicesTest extends UiServiceTestCase { } } @Test public void testUpgradeAppNoPermissionNoRebind() throws Exception { Context context = spy(getContext()); doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any()); ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, APPROVAL_BY_COMPONENT); List<String> packages = new ArrayList<>(); packages.add("package"); addExpectedServices(service, packages, 0); final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1"); final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2"); // Both components are approved initially mExpectedPrimaryComponentNames.clear(); mExpectedPrimaryPackages.clear(); mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2"); mExpectedSecondaryComponentNames.clear(); mExpectedSecondaryPackages.clear(); loadXml(service); //Component package/C1 loses bind permission when(mIpm.getServiceInfo(any(), anyInt(), anyInt())).thenAnswer( (Answer<ServiceInfo>) invocation -> { ComponentName invocationCn = invocation.getArgument(0); if (invocationCn != null) { ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.packageName = invocationCn.getPackageName(); serviceInfo.name = invocationCn.getClassName(); if (invocationCn.equals(unapprovedComponent)) { serviceInfo.permission = "none"; } else { serviceInfo.permission = service.getConfig().bindPermission; } serviceInfo.metaData = null; return serviceInfo; } return null; } ); // Trigger package update service.onPackagesChanged(false, new String[]{"package"}, new int[]{0}); assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent)); assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent)); } @Test public void testSetPackageOrComponentEnabled() throws Exception { for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { Loading Loading
services/core/java/com/android/server/notification/ManagedServices.java +85 −25 Original line number Diff line number Diff line Loading @@ -155,7 +155,9 @@ abstract public class ManagedServices { // 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 // not mean that we are currently bound to said package/component. protected ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>(); @GuardedBy("mApproved") protected final ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved = new ArrayMap<>(); // List of packages or components (by user) that are configured to be enabled/disabled // explicitly by the user Loading Loading @@ -871,6 +873,23 @@ abstract public class ManagedServices { return false; } protected boolean isPackageOrComponentAllowedWithPermission(ComponentName component, int userId) { if (!(isPackageOrComponentAllowed(component.flattenToString(), userId) || isPackageOrComponentAllowed(component.getPackageName(), userId))) { return false; } return componentHasBindPermission(component, userId); } private boolean componentHasBindPermission(ComponentName component, int userId) { ServiceInfo info = getServiceInfo(component, userId); if (info == null) { return false; } return mConfig.bindPermission.equals(info.permission); } boolean isPackageOrComponentUserSet(String pkgOrComponent, int userId) { synchronized (mApproved) { ArraySet<String> services = mUserSetServices.get(userId); Loading Loading @@ -928,6 +947,7 @@ abstract public class ManagedServices { for (int uid : uidList) { if (isPackageAllowed(pkgName, UserHandle.getUserId(uid))) { anyServicesInvolved = true; trimApprovedListsForInvalidServices(pkgName, UserHandle.getUserId(uid)); } } } Loading Loading @@ -1057,8 +1077,7 @@ abstract public class ManagedServices { for (int i = 0; i < userIds.size(); i++) { final int userId = userIds.get(i); if (enabled) { if (isPackageOrComponentAllowed(component.flattenToString(), userId) || isPackageOrComponentAllowed(component.getPackageName(), userId)) { if (isPackageOrComponentAllowedWithPermission(component, userId)) { registerServiceLocked(component, userId); } else { Slog.d(TAG, component + " no longer has permission to be bound"); Loading Loading @@ -1197,6 +1216,33 @@ abstract public class ManagedServices { return removed; } private void trimApprovedListsForInvalidServices(String packageName, int userId) { synchronized (mApproved) { final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId); if (approvedByType == null) { return; } for (int i = 0; i < approvedByType.size(); i++) { final ArraySet<String> approved = approvedByType.valueAt(i); for (int j = approved.size() - 1; j >= 0; j--) { final String approvedPackageOrComponent = approved.valueAt(j); if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) { final ComponentName component = ComponentName.unflattenFromString( approvedPackageOrComponent); if (component != null && !componentHasBindPermission(component, userId)) { approved.removeAt(j); if (DEBUG) { Slog.v(TAG, "Removing " + approvedPackageOrComponent + " from approved list; no bind permission found " + mConfig.bindPermission); } } } } } } } protected String getPackageName(String packageOrComponent) { final ComponentName component = ComponentName.unflattenFromString(packageOrComponent); if (component != null) { Loading Loading @@ -1380,12 +1426,7 @@ abstract public class ManagedServices { final int userId = componentsToBind.keyAt(i); final Set<ComponentName> add = componentsToBind.get(userId); for (ComponentName component : add) { try { ServiceInfo info = mPm.getServiceInfo(component, PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); ServiceInfo info = getServiceInfo(component, userId); if (info == null) { Slog.w(TAG, "Not binding " + getCaption() + " service " + component + ": service not found"); Loading @@ -1399,9 +1440,6 @@ abstract public class ManagedServices { Slog.v(TAG, "enabling " + getCaption() + " for " + userId + ": " + component); registerService(info, userId); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } } } Loading @@ -1422,6 +1460,15 @@ abstract public class ManagedServices { } } @VisibleForTesting void reregisterService(final ComponentName cn, final int userId) { // If rebinding a package that died, ensure it still has permission // after the rebind delay if (isPackageOrComponentAllowedWithPermission(cn, userId)) { registerService(cn, userId); } } /** * Inject a system service into the management list. */ Loading Loading @@ -1523,7 +1570,7 @@ abstract public class ManagedServices { mHandler.postDelayed(new Runnable() { @Override public void run() { registerService(name, userid); reregisterService(name, userid); } }, ON_BINDING_DIED_REBIND_DELAY_MS); } else { Loading Loading @@ -1655,6 +1702,19 @@ abstract public class ManagedServices { } } private ServiceInfo getServiceInfo(ComponentName component, int userId) { try { return mPm.getServiceInfo(component, PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); } catch (RemoteException e) { e.rethrowFromSystemServer(); } return null; } public class ManagedServiceInfo implements IBinder.DeathRecipient { public IInterface service; public ComponentName component; Loading
services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +54 −0 Original line number Diff line number Diff line Loading @@ -27,8 +27,10 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; Loading Loading @@ -955,6 +957,58 @@ public class ManagedServicesTest extends UiServiceTestCase { } } @Test public void testUpgradeAppNoPermissionNoRebind() throws Exception { Context context = spy(getContext()); doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any()); ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, APPROVAL_BY_COMPONENT); List<String> packages = new ArrayList<>(); packages.add("package"); addExpectedServices(service, packages, 0); final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1"); final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2"); // Both components are approved initially mExpectedPrimaryComponentNames.clear(); mExpectedPrimaryPackages.clear(); mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2"); mExpectedSecondaryComponentNames.clear(); mExpectedSecondaryPackages.clear(); loadXml(service); //Component package/C1 loses bind permission when(mIpm.getServiceInfo(any(), anyInt(), anyInt())).thenAnswer( (Answer<ServiceInfo>) invocation -> { ComponentName invocationCn = invocation.getArgument(0); if (invocationCn != null) { ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.packageName = invocationCn.getPackageName(); serviceInfo.name = invocationCn.getClassName(); if (invocationCn.equals(unapprovedComponent)) { serviceInfo.permission = "none"; } else { serviceInfo.permission = service.getConfig().bindPermission; } serviceInfo.metaData = null; return serviceInfo; } return null; } ); // Trigger package update service.onPackagesChanged(false, new String[]{"package"}, new int[]{0}); assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent)); assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent)); } @Test public void testSetPackageOrComponentEnabled() throws Exception { for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) { Loading