diff options
| author | 2019-10-09 15:43:38 +0100 | |
|---|---|---|
| committer | 2019-10-24 15:51:55 +0100 | |
| commit | c3e1bb723942a004c39cd1a6dc2893a4ac3172bd (patch) | |
| tree | eec7a09c8c511dca6d735c16448effd96df0325c | |
| parent | 6c10771e888c952c9da66d875074918ddae4aee8 (diff) | |
Support non-privileged APKs in APEX.
This change adds initial support for scanning APEX directories looking
for APK, and make them available to the system as normal system APKs.
For now privileged apps and resource overlay are not supported, and the
support will be added in a later CL.
Scanning features are inherited from the partition in which the
preinstalled version of a given APEX module is located.
Supports both flattened and un-flattened APEX configurations.
Bug: 138429615
Test: Compiled a test apex with the Snake app installed in it. Verified
app is correctly scanned and usable. Tried also installing the apex in
/vendor and /product.
Change-Id: I8fb91317da0296567403b2b62babbb71e4b48938
4 files changed, 131 insertions, 14 deletions
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 6d5fe53b3f3d..a92237b9f17f 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -55,6 +55,7 @@ public class Environment { 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 @@ public class Environment { 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 @@ public class Environment { } /** + * 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 fd44cbb46065..12e8069ecae5 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.PackageInfo; 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.io.PrintWriter; 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 @@ abstract class ApexManager { 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 @@ abstract class ApexManager { 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 @@ abstract class ApexManager { } @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 @@ abstract class ApexManager { * 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 1d3d24c27041..259200b92597 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 @@ final class PackageAbiHelperImpl implements PackageAbiHelper { 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 b5fa80d1d706..74a85d58c016 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.AtomicBoolean; 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 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ public class PackageManagerService extends IPackageManager.Stub // 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 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ public class PackageManagerService extends IPackageManager.Stub @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 @@ public class PackageManagerService extends IPackageManager.Stub } 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 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ public class PackageManagerService extends IPackageManager.Stub | 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)) { |