diff options
| -rw-r--r-- | services/core/java/com/android/server/notification/ManagedServices.java | 98 | ||||
| -rw-r--r-- | services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java | 79 |
2 files changed, 152 insertions, 25 deletions
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 25fefad8042f..83a1df89754c 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -918,6 +918,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); @@ -975,6 +992,7 @@ abstract public class ManagedServices { for (int uid : uidList) { if (isPackageAllowed(pkgName, UserHandle.getUserId(uid))) { anyServicesInvolved = true; + trimApprovedListsForInvalidServices(pkgName, UserHandle.getUserId(uid)); } } } @@ -1107,8 +1125,7 @@ abstract public class ManagedServices { synchronized (mMutex) { 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"); @@ -1246,6 +1263,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) { @@ -1432,28 +1476,20 @@ 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); - if (info == null) { - Slog.w(TAG, "Not binding " + getCaption() + " service " + component - + ": service not found"); - continue; - } - if (!mConfig.bindPermission.equals(info.permission)) { - Slog.w(TAG, "Not binding " + getCaption() + " service " + component - + ": it does not require the permission " + mConfig.bindPermission); - continue; - } - Slog.v(TAG, - "enabling " + getCaption() + " for " + userId + ": " + component); - registerService(info, userId); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); + ServiceInfo info = getServiceInfo(component, userId); + if (info == null) { + Slog.w(TAG, "Not binding " + getCaption() + " service " + component + + ": service not found"); + continue; } + if (!mConfig.bindPermission.equals(info.permission)) { + Slog.w(TAG, "Not binding " + getCaption() + " service " + component + + ": it does not require the permission " + mConfig.bindPermission); + continue; + } + Slog.v(TAG, + "enabling " + getCaption() + " for " + userId + ": " + component); + registerService(info, userId); } } } @@ -1478,8 +1514,7 @@ abstract public class ManagedServices { void reregisterService(final ComponentName cn, final int userId) { // If rebinding a package that died, ensure it still has permission // after the rebind delay - if (isPackageOrComponentAllowed(cn.getPackageName(), userId) - || isPackageOrComponentAllowed(cn.flattenToString(), userId)) { + if (isPackageOrComponentAllowedWithPermission(cn, userId)) { registerService(cn, userId); } } @@ -1718,6 +1753,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; 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 8b1384ed894f..38ab53be4601 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -33,8 +33,10 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; 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; @@ -52,8 +54,10 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.os.Build; +import android.os.Bundle; import android.os.IBinder; import android.os.IInterface; +import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -871,6 +875,7 @@ public class ManagedServicesTest extends UiServiceTestCase { return true; }); + mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>()); service.addApprovedList("a", 0, true); service.reregisterService(cn, 0); @@ -901,6 +906,7 @@ public class ManagedServicesTest extends UiServiceTestCase { return true; }); + mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>()); service.addApprovedList("a", 0, false); service.reregisterService(cn, 0); @@ -931,6 +937,7 @@ public class ManagedServicesTest extends UiServiceTestCase { return true; }); + mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>()); service.addApprovedList("a/a", 0, true); service.reregisterService(cn, 0); @@ -961,6 +968,7 @@ public class ManagedServicesTest extends UiServiceTestCase { return true; }); + mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>()); service.addApprovedList("a/a", 0, false); service.reregisterService(cn, 0); @@ -1118,6 +1126,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(), anyLong(), 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}) { ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, @@ -1820,6 +1880,25 @@ public class ManagedServicesTest extends UiServiceTestCase { assertTrue(profiles.isProfileUser(13)); } + private void mockServiceInfoWithMetaData(List<ComponentName> componentNames, + ManagedServices service, ArrayMap<ComponentName, Bundle> metaDatas) + throws RemoteException { + when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer( + (Answer<ServiceInfo>) invocation -> { + ComponentName invocationCn = invocation.getArgument(0); + if (invocationCn != null && componentNames.contains(invocationCn)) { + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.packageName = invocationCn.getPackageName(); + serviceInfo.name = invocationCn.getClassName(); + serviceInfo.permission = service.getConfig().bindPermission; + serviceInfo.metaData = metaDatas.get(invocationCn); + return serviceInfo; + } + return null; + } + ); + } + private void resetComponentsAndPackages() { ArrayMap<Integer, ArrayMap<Integer, String>> empty = new ArrayMap(1); ArrayMap<Integer, String> emptyPkgs = new ArrayMap(0); |