diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index c237222d91349..7a131e5680a81 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; @@ -245,6 +248,16 @@ public class Environment { return DIR_SYSTEM_EXT_ROOT; } + /** + * 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 diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index fd44cbb460652..12e8069ecae5f 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 getActiveApexInfos(); + abstract void systemReady(); /** @@ -258,6 +278,22 @@ abstract class ApexManager { return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } + @Override + List 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() { @@ -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 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 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 1d3d24c270412..259200b92597b 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 19b6ae88542b0..ef1fcdaa4089c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -375,6 +375,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.stream.Collectors; /** * Keep track of all those APKs everywhere. @@ -767,6 +768,8 @@ public class PackageManagerService extends IPackageManager.Stub new SystemPartition(Environment.getSystemExtDirectory(), SCAN_AS_SYSTEM_EXT, true /* hasPriv */, true /* hasOverlays */))); + private final List mDirsToScanAsSystem; + /** * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors. * @@ -2563,7 +2566,18 @@ public class PackageManagerService extends IPackageManager.Stub mProtectedPackages = new ProtectedPackages(mContext); - mApexManager = ApexManager.create(context); + mApexManager = ApexManager.create(mContext); + + 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 @@ -2700,8 +2714,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; } @@ -2717,8 +2731,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); @@ -2896,8 +2910,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 @@ -18375,6 +18389,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)) { @@ -18384,6 +18399,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. @@ -18490,8 +18517,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)) {