summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java96
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java124
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);
+ }
}
}