diff options
2 files changed, 218 insertions, 2 deletions
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java index 280f12f6a60e..685cf0580a48 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java @@ -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); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java index 24e226a64917..a8f24ce5e11d 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java @@ -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); + } } } |