Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0c15cdfd authored by Valentin Iftime's avatar Valentin Iftime Committed by Iavor-Valentin Iftime
Browse files

Check for NLS bind permission when rebinding services

 Also, after updating packages with NLS components, check
 the approved services and remove from approved list if missing permissions.

Test: atest ManagedServicesTest
Bug: 321707289

Change-Id: I11901755ec430c6e3145def9d67e4e63cda00806
(cherry picked from commit 24b13a64)
Merged-In: I11901755ec430c6e3145def9d67e4e63cda00806
parent 340a1267
Loading
Loading
Loading
Loading
+85 −25
Original line number Diff line number Diff line
@@ -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
@@ -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);
@@ -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));
                        }
                    }
                }
@@ -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");
@@ -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) {
@@ -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");
@@ -1399,9 +1440,6 @@ abstract public class ManagedServices {
                Slog.v(TAG,
                        "enabling " + getCaption() + " for " + userId + ": " + component);
                registerService(info, userId);
                } catch (RemoteException e) {
                    e.rethrowFromSystemServer();
                }
            }
        }
    }
@@ -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.
     */
@@ -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 {
@@ -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;
+54 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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}) {