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

Commit 0ba9c3c5 authored by arangelov's avatar arangelov Committed by Antoan Angelov
Browse files

Don't remove mainline modules if they declare support in manifest

meta-data

Test: atest OverlayPackagesProviderTest
Fixes: 179653892
Change-Id: Idd324fc04542a9be055c1144e4473c6a716a2e6f
parent 10a6b054
Loading
Loading
Loading
Loading
+95 −1
Original line number Diff line number Diff line
@@ -19,17 +19,26 @@ package com.android.server.devicepolicy;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
import static android.app.admin.DevicePolicyManager.REQUIRED_APP_MANAGED_DEVICE;
import static android.app.admin.DevicePolicyManager.REQUIRED_APP_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.REQUIRED_APP_MANAGED_USER;
import static android.content.pm.PackageManager.GET_META_DATA;

import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.devicepolicy.DevicePolicyManagerService.dumpResources;

import static java.util.Objects.requireNonNull;

import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.util.ArraySet;
@@ -39,9 +48,13 @@ import android.view.inputmethod.InputMethodInfo;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.pm.ApexManager;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
@@ -51,6 +64,18 @@ import java.util.Set;
public class OverlayPackagesProvider {

    protected static final String TAG = "OverlayPackagesProvider";
    private static final Map<String, String> sActionToMetadataKeyMap = new HashMap<>();
    {
        sActionToMetadataKeyMap.put(ACTION_PROVISION_MANAGED_USER, REQUIRED_APP_MANAGED_USER);
        sActionToMetadataKeyMap.put(ACTION_PROVISION_MANAGED_PROFILE, REQUIRED_APP_MANAGED_PROFILE);
        sActionToMetadataKeyMap.put(ACTION_PROVISION_MANAGED_DEVICE, REQUIRED_APP_MANAGED_DEVICE);
    }
    private static final Set<String> sAllowedActions = new HashSet<>();
    {
        sAllowedActions.add(ACTION_PROVISION_MANAGED_USER);
        sAllowedActions.add(ACTION_PROVISION_MANAGED_PROFILE);
        sAllowedActions.add(ACTION_PROVISION_MANAGED_DEVICE);
    }

    private final PackageManager mPm;
    private final Context mContext;
@@ -64,6 +89,8 @@ public class OverlayPackagesProvider {
    interface Injector {
        @NonNull
        List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId);

        String getActiveApexPackageNameContainingPackage(String packageName);
    }

    private static final class DefaultInjector implements Injector {
@@ -72,6 +99,11 @@ public class OverlayPackagesProvider {
        public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
            return InputMethodManagerInternal.get().getInputMethodListAsUser(userId);
        }

        @Override
        public String getActiveApexPackageNameContainingPackage(String packageName) {
            return ApexManager.getInstance().getActiveApexPackageNameContainingPackage(packageName);
        }
    }

    @VisibleForTesting
@@ -83,7 +115,8 @@ public class OverlayPackagesProvider {

    /**
     * Computes non-required apps. All the system apps with a launcher that are not in
     * the required set of packages will be considered as non-required apps.
     * the required set of packages, and all mainline modules that are not declared as required
     * via metadata in their manifests, will be considered as non-required apps.
     *
     * Note: If an app is mistakenly listed as both required and disallowed, it will be treated as
     * disallowed.
@@ -99,15 +132,76 @@ public class OverlayPackagesProvider {
    @NonNull
    public Set<String> getNonRequiredApps(@NonNull ComponentName admin, int userId,
            @NonNull String provisioningAction) {
        requireNonNull(admin);
        checkArgument(sAllowedActions.contains(provisioningAction));
        final Set<String> nonRequiredApps = getLaunchableApps(userId);
        // Newly installed system apps are uninstalled when they are not required and are either
        // disallowed or have a launcher icon.
        nonRequiredApps.removeAll(getRequiredApps(provisioningAction, admin.getPackageName()));
        nonRequiredApps.removeAll(getSystemInputMethods(userId));
        nonRequiredApps.addAll(getDisallowedApps(provisioningAction));
        nonRequiredApps.removeAll(
                getRequiredAppsMainlineModules(nonRequiredApps, provisioningAction));
        return nonRequiredApps;
    }

    /**
     * Returns a subset of {@code packageNames} whose packages are mainline modules declared as
     * required apps via their app metadata.
     * @see DevicePolicyManager#REQUIRED_APP_MANAGED_USER
     * @see DevicePolicyManager#REQUIRED_APP_MANAGED_DEVICE
     * @see DevicePolicyManager#REQUIRED_APP_MANAGED_PROFILE
     */
    private Set<String> getRequiredAppsMainlineModules(
            Set<String> packageNames,
            String provisioningAction) {
        final Set<String> result = new HashSet<>();
        for (String packageName : packageNames) {
            if (!isMainlineModule(packageName)) {
                continue;
            }
            if (!isRequiredAppDeclaredInMetadata(packageName, provisioningAction)) {
                continue;
            }
            result.add(packageName);
        }
        return result;
    }

    private boolean isRequiredAppDeclaredInMetadata(String packageName, String provisioningAction) {
        PackageInfo packageInfo;
        try {
            packageInfo = mPm.getPackageInfo(packageName, GET_META_DATA);
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
        final String metadataKey = sActionToMetadataKeyMap.get(provisioningAction);
        return packageInfo.applicationInfo.metaData.getBoolean(metadataKey);
    }

    /**
     * Returns {@code true} if the provided package name is a mainline module.
     * <p>There are 2 types of mainline modules: a regular mainline module and apk-in-apex module.
     */
    private boolean isMainlineModule(String packageName) {
        return isRegularMainlineModule(packageName) || isApkInApexMainlineModule(packageName);
    }

    private boolean isRegularMainlineModule(String packageName) {
        try {
            mPm.getModuleInfo(packageName, /* flags= */ 0);
            return true;
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
    }

    private boolean isApkInApexMainlineModule(String packageName) {
        final String apexPackageName =
                mInjector.getActiveApexPackageNameContainingPackage(packageName);
        return apexPackageName != null;
    }

    private Set<String> getLaunchableApps(int userId) {
        final Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
        launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+123 −1
Original line number Diff line number Diff line
@@ -19,6 +19,9 @@ package com.android.server.devicepolicy;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
import static android.app.admin.DevicePolicyManager.REQUIRED_APP_MANAGED_DEVICE;
import static android.app.admin.DevicePolicyManager.REQUIRED_APP_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.REQUIRED_APP_MANAGED_USER;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -31,13 +34,17 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.os.Bundle;
import android.test.mock.MockPackageManager;
import android.view.inputmethod.InputMethodInfo;

import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;

@@ -51,8 +58,10 @@ import org.mockito.MockitoAnnotations;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
@@ -76,6 +85,8 @@ public class OverlayPackagesProviderTest {

    private FakePackageManager mPackageManager;
    private String[] mSystemAppsWithLauncher;
    private Set<String> mRegularMainlineModules = new HashSet<>();
    private Map<String, String> mMainlineModuleToDeclaredMetadataMap = new HashMap<>();
    private OverlayPackagesProvider mHelper;

    @Before
@@ -168,7 +179,7 @@ public class OverlayPackagesProviderTest {
        setSystemAppsWithLauncher("app.a", "app.b");
        setSystemInputMethods("app.a");

        verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_PROFILE, "app.a", "app.b");
        verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_PROFILE, "app.b");
    }

    @Test
@@ -257,6 +268,93 @@ public class OverlayPackagesProviderTest {
                R.array.vendor_disallowed_apps_managed_profile);
    }

    @Test
    public void testGetNonRequiredApps_mainlineModules_managedProfile_works() {
        setupApexModulesWithManagedProfile("package1");
        setupRegularModulesWithManagedProfile("package2");
        setSystemAppsWithLauncher("package1", "package2", "package3");

        verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_PROFILE, "package3");
    }

    @Test
    public void testGetNonRequiredApps_mainlineModules_managedDevice_works() {
        setupApexModulesWithManagedDevice("package1");
        setupRegularModulesWithManagedDevice("package2");
        setSystemAppsWithLauncher("package1", "package2", "package3");

        verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_DEVICE, "package3");
    }

    @Test
    public void testGetNonRequiredApps_mainlineModules_managedUser_works() {
        setupApexModulesWithManagedUser("package1");
        setupRegularModulesWithManagedUser("package2");
        setSystemAppsWithLauncher("package1", "package2", "package3");

        verifyAppsAreNonRequired(ACTION_PROVISION_MANAGED_USER, "package3");
    }

    @Test
    public void testGetNonRequiredApps_mainlineModules_noMetadata_works() {
        setupApexModulesWithNoMetadata("package1");
        setupRegularModulesWithNoMetadata("package2");
        setSystemAppsWithLauncher("package1", "package2", "package3");

        verifyAppsAreNonRequired(
                ACTION_PROVISION_MANAGED_PROFILE, "package1", "package2", "package3");
    }

    private void setupRegularModulesWithManagedUser(String... regularModules) {
        setupRegularModulesWithMetadata(regularModules, REQUIRED_APP_MANAGED_USER);
    }

    private void setupRegularModulesWithManagedDevice(String... regularModules) {
        setupRegularModulesWithMetadata(regularModules, REQUIRED_APP_MANAGED_DEVICE);
    }

    private void setupRegularModulesWithManagedProfile(String... regularModules) {
        setupRegularModulesWithMetadata(regularModules, REQUIRED_APP_MANAGED_PROFILE);
    }

    private void setupRegularModulesWithNoMetadata(String... regularModules) {
        mRegularMainlineModules.addAll(Arrays.asList(regularModules));
    }

    private void setupRegularModulesWithMetadata(String[] regularModules, String metadataKey) {
        for (String regularModule : regularModules) {
            mRegularMainlineModules.add(regularModule);
            mMainlineModuleToDeclaredMetadataMap.put(regularModule, metadataKey);
        }
    }

    private void setupApexModulesWithManagedUser(String... apexPackageNames) {
        setupApexModulesWithMetadata(apexPackageNames, REQUIRED_APP_MANAGED_USER);
    }

    private void setupApexModulesWithManagedDevice(String... apexPackageNames) {
        setupApexModulesWithMetadata(apexPackageNames, REQUIRED_APP_MANAGED_DEVICE);
    }

    private void setupApexModulesWithManagedProfile(String... apexPackageNames) {
        setupApexModulesWithMetadata(apexPackageNames, REQUIRED_APP_MANAGED_PROFILE);
    }

    private void setupApexModulesWithNoMetadata(String... apexPackageNames) {
        for (String apexPackageName : apexPackageNames) {
            when(mInjector.getActiveApexPackageNameContainingPackage(eq(apexPackageName)))
                    .thenReturn("apex");
        }
    }

    private void setupApexModulesWithMetadata(String[] apexPackageNames, String metadataKey) {
        for (String apexPackageName : apexPackageNames) {
            when(mInjector.getActiveApexPackageNameContainingPackage(eq(apexPackageName)))
                    .thenReturn("apex");
            mMainlineModuleToDeclaredMetadataMap.put(apexPackageName, metadataKey);
        }
    }

    private ArrayList<String> getStringArrayInRealResources(int id) {
        return new ArrayList<>(Arrays.asList(mRealResources.getStringArray(id)));
    }
@@ -383,5 +481,29 @@ public class OverlayPackagesProviderTest {
            }
            return result;
        }

        @NonNull
        @Override
        public PackageInfo getPackageInfo(String packageName, int flags) {
            final PackageInfo packageInfo = new PackageInfo();
            final ApplicationInfo applicationInfo = new ApplicationInfo();
            applicationInfo.metaData = new Bundle();
            if (mMainlineModuleToDeclaredMetadataMap.containsKey(packageName)) {
                applicationInfo.metaData.putBoolean(
                        mMainlineModuleToDeclaredMetadataMap.get(packageName), true);
            }
            packageInfo.applicationInfo = applicationInfo;
            return packageInfo;
        }

        @NonNull
        @Override
        public ModuleInfo getModuleInfo(@NonNull String packageName, int flags)
                throws NameNotFoundException {
            if (!mRegularMainlineModules.contains(packageName)) {
                throw new NameNotFoundException("package does not exist");
            }
            return new ModuleInfo().setName(packageName);
        }
    }
}