Merge "Support non-privileged APKs in APEX."
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 6d5fe53b..a92237b 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -55,6 +55,7 @@
private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
private static final String ENV_PRODUCT_ROOT = "PRODUCT_ROOT";
private static final String ENV_SYSTEM_EXT_ROOT = "SYSTEM_EXT_ROOT";
+ private static final String ENV_APEX_ROOT = "APEX_ROOT";
/** {@hide} */
public static final String DIR_ANDROID = "Android";
@@ -78,7 +79,9 @@
private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
private static final File DIR_PRODUCT_ROOT = getDirectory(ENV_PRODUCT_ROOT, "/product");
private static final File DIR_SYSTEM_EXT_ROOT = getDirectory(ENV_SYSTEM_EXT_ROOT,
- "/system_ext");
+ "/system_ext");
+ private static final File DIR_APEX_ROOT = getDirectory(ENV_APEX_ROOT,
+ "/apex");
@UnsupportedAppUsage
private static UserEnvironment sCurrentUser;
@@ -248,6 +251,16 @@
}
/**
+ * Return root directory of the apex mount point, where all the apex modules are made available
+ * to the rest of the system.
+ *
+ * @hide
+ */
+ public static @NonNull File getApexDirectory() {
+ return DIR_APEX_ROOT;
+ }
+
+ /**
* Return the system directory for a user. This is for use by system
* services to store files relating to the user. This directory will be
* automatically deleted when the user is removed.
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index fd44cbb..12e8069 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -32,6 +32,7 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
+import android.os.Environment;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.sysprop.ApexProperties;
@@ -47,6 +48,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -64,9 +66,9 @@
static final int MATCH_FACTORY_PACKAGE = 1 << 1;
/**
- * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerNoOp} depending
- * on whenever this device supports APEX, i.e. {@link ApexProperties#updatable()} evaluates to
- * {@code true}.
+ * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerFlattenedApex}
+ * depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()}
+ * evaluates to {@code true}.
*/
static ApexManager create(Context systemContext) {
if (ApexProperties.updatable().orElse(false)) {
@@ -77,10 +79,28 @@
throw new IllegalStateException("Required service apexservice not available");
}
} else {
- return new ApexManagerNoOp();
+ return new ApexManagerFlattenedApex();
}
}
+ /**
+ * Minimal information about APEX mount points and the original APEX package they refer to.
+ */
+ static class ActiveApexInfo {
+ public final File apexDirectory;
+ public final File preinstalledApexPath;
+
+ private ActiveApexInfo(File apexDirectory, File preinstalledApexPath) {
+ this.apexDirectory = apexDirectory;
+ this.preinstalledApexPath = preinstalledApexPath;
+ }
+ }
+
+ /**
+ * Returns {@link ActiveApexInfo} records relative to all active APEX packages.
+ */
+ abstract List<ActiveApexInfo> getActiveApexInfos();
+
abstract void systemReady();
/**
@@ -259,6 +279,22 @@
}
@Override
+ List<ActiveApexInfo> getActiveApexInfos() {
+ try {
+ return Arrays.stream(mApexService.getActivePackages())
+ .map(apexInfo -> new ActiveApexInfo(
+ new File(
+ Environment.getApexDirectory() + File.separator
+ + apexInfo.moduleName),
+ new File(apexInfo.modulePath))).collect(
+ Collectors.toList());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
+ }
+ return Collections.emptyList();
+ }
+
+ @Override
void systemReady() {
mContext.registerReceiver(new BroadcastReceiver() {
@Override
@@ -549,7 +585,40 @@
* An implementation of {@link ApexManager} that should be used in case device does not support
* updating APEX packages.
*/
- private static final class ApexManagerNoOp extends ApexManager {
+ private static final class ApexManagerFlattenedApex extends ApexManager {
+
+ @Override
+ List<ActiveApexInfo> getActiveApexInfos() {
+ // There is no apexd running in case of flattened apex
+ // We look up the /apex directory and identify the active APEX modules from there.
+ // As "preinstalled" path, we just report /system since in the case of flattened APEX
+ // the /apex directory is just a symlink to /system/apex.
+ List<ActiveApexInfo> result = new ArrayList<>();
+ File apexDir = Environment.getApexDirectory();
+ // In flattened configuration, init special-case the art directory and bind-mounts
+ // com.android.art.{release|debug} to com.android.art. At the time of writing, these
+ // directories are copied from the kArtApexDirNames variable in
+ // system/core/init/mount_namespace.cpp.
+ String[] skipDirs = {"com.android.art.release", "com.android.art.debug"};
+ if (apexDir.isDirectory()) {
+ File[] files = apexDir.listFiles();
+ // listFiles might be null if system server doesn't have permission to read
+ // a directory.
+ if (files != null) {
+ for (File file : files) {
+ if (file.isDirectory() && !file.getName().contains("@")) {
+ for (String skipDir : skipDirs) {
+ if (file.getName().equals(skipDir)) {
+ continue;
+ }
+ }
+ result.add(new ActiveApexInfo(file, Environment.getRootDirectory()));
+ }
+ }
+ }
+ }
+ return result;
+ }
@Override
void systemReady() {
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 1d3d24c..259200b 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -67,6 +67,15 @@
codeRoot = Environment.getSystemExtDirectory();
} else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) {
codeRoot = Environment.getOdmDirectory();
+ } else if (FileUtils.contains(Environment.getApexDirectory(), codePath)) {
+ String fullPath = codePath.getAbsolutePath();
+ String[] parts = fullPath.split(File.separator);
+ if (parts.length > 2) {
+ codeRoot = new File(parts[1] + File.separator + parts[2]);
+ } else {
+ Slog.w(PackageManagerService.TAG, "Can't canonicalize code path " + codePath);
+ codeRoot = Environment.getApexDirectory();
+ }
} else {
// Unrecognized code path; take its top real segment as the apk root:
// e.g. /something/app/blah.apk => /something
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b5fa80d1..74a85d5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -370,6 +370,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
/**
* Keep track of all those APKs everywhere.
@@ -762,6 +763,8 @@
new SystemPartition(Environment.getSystemExtDirectory(), SCAN_AS_SYSTEM_EXT,
true /* hasPriv */, true /* hasOverlays */)));
+ private final List<SystemPartition> mDirsToScanAsSystem;
+
/**
* Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
*
@@ -2552,6 +2555,16 @@
mApexManager = ApexManager.create(mContext);
mAppsFilter = mInjector.getAppsFilter();
+ mDirsToScanAsSystem = new ArrayList<>();
+ mDirsToScanAsSystem.addAll(SYSTEM_PARTITIONS);
+ mDirsToScanAsSystem.addAll(mApexManager.getActiveApexInfos().stream()
+ .map(ai -> resolveApexToSystemPartition(ai))
+ .filter(Objects::nonNull).collect(Collectors.toList()));
+ Slog.d(TAG,
+ "Directories scanned as system partitions: [" + mDirsToScanAsSystem.stream().map(
+ d -> (d.folder.getAbsolutePath() + ":" + d.scanFlag))
+ .collect(Collectors.joining(",")) + "]");
+
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
@@ -2684,8 +2697,8 @@
// reside in the right directory.
final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
- for (int i = SYSTEM_PARTITIONS.size() - 1; i >= 0; i--) {
- final SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
+ final SystemPartition partition = mDirsToScanAsSystem.get(i);
if (partition.overlayFolder == null) {
continue;
}
@@ -2699,8 +2712,8 @@
throw new IllegalStateException(
"Failed to load frameworks package; check log for warnings");
}
- for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
- final SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
+ final SystemPartition partition = mDirsToScanAsSystem.get(i);
if (partition.privAppFolder != null) {
scanDirTracedLI(partition.privAppFolder, systemParseFlags,
systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0);
@@ -2878,8 +2891,8 @@
@ParseFlags int reparseFlags = 0;
@ScanFlags int rescanFlags = 0;
- for (int i1 = 0, size = SYSTEM_PARTITIONS.size(); i1 < size; i1++) {
- SystemPartition partition = SYSTEM_PARTITIONS.get(i1);
+ for (int i1 = 0, size = mDirsToScanAsSystem.size(); i1 < size; i1++) {
+ SystemPartition partition = mDirsToScanAsSystem.get(i1);
if (partition.containsPrivApp(scanFile)) {
reparseFlags = systemParseFlags;
rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
@@ -17778,6 +17791,7 @@
}
static boolean locationIsPrivileged(String path) {
+ // TODO(dariofreni): include APEX partitions when they will support priv apps.
for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
SystemPartition partition = SYSTEM_PARTITIONS.get(i);
if (partition.containsPrivPath(path)) {
@@ -17787,6 +17801,18 @@
return false;
}
+ private static @Nullable SystemPartition resolveApexToSystemPartition(
+ ApexManager.ActiveApexInfo apexInfo) {
+ for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
+ SystemPartition sp = SYSTEM_PARTITIONS.get(i);
+ if (apexInfo.preinstalledApexPath.getAbsolutePath().startsWith(
+ sp.folder.getAbsolutePath())) {
+ return new SystemPartition(apexInfo.apexDirectory, sp.scanFlag,
+ false /* hasPriv */, false /* hasOverlays */);
+ }
+ }
+ return null;
+ }
/*
* Tries to delete system package.
@@ -17897,8 +17923,8 @@
| PackageParser.PARSE_MUST_BE_APK
| PackageParser.PARSE_IS_SYSTEM_DIR;
@ScanFlags int scanFlags = SCAN_AS_SYSTEM;
- for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
- SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
+ SystemPartition partition = mDirsToScanAsSystem.get(i);
if (partition.containsPath(codePathString)) {
scanFlags |= partition.scanFlag;
if (partition.containsPrivPath(codePathString)) {