From ecccf009ea07d8f1c4caa3524e18eb289f163d7a Mon Sep 17 00:00:00 2001 From: Patrick Baumann Date: Wed, 18 Sep 2019 14:24:54 -0700 Subject: [PATCH 1/4] Refactors initial directory scan to be dryer This change collapses a bunch of manual calls to scanDir with a map and a loop. This makes things a little more readable and easier to update going forward. Bug: NA Test: boots without issue. Merged-In: I81fb7a9474894d0bceecb6b0ef7a64d047a40c14 Change-Id: I81fb7a9474894d0bceecb6b0ef7a64d047a40c14 --- .../server/pm/PackageManagerService.java | 492 +++++------------- .../server/pm/PackageManagerServiceTest.java | 15 +- 2 files changed, 131 insertions(+), 376 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8d31b34b568be..19b6ae88542b0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -91,6 +91,7 @@ import static android.os.storage.StorageManager.FLAG_STORAGE_CE; import static android.os.storage.StorageManager.FLAG_STORAGE_DE; import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL; +import static com.android.internal.annotations.VisibleForTesting.Visibility; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT; import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; @@ -588,16 +589,6 @@ public class PackageManagerService extends IPackageManager.Stub private static final String PACKAGE_SCHEME = "package"; - private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay"; - - private static final String PRODUCT_OVERLAY_DIR = "/product/overlay"; - - private static final String SYSTEM_EXT_OVERLAY_DIR = "/system_ext/overlay"; - - private static final String ODM_OVERLAY_DIR = "/odm/overlay"; - - private static final String OEM_OVERLAY_DIR = "/oem/overlay"; - /** Canonical intent used to identify what counts as a "web browser" app */ private static final Intent sBrowserIntent; static { @@ -757,7 +748,29 @@ public class PackageManagerService extends IPackageManager.Stub private final Injector mInjector; /** - * Unit tests will instantiate and / or extend to mock dependencies / behaviors. + * The list of all system partitions that may contain packages in ascending order of + * specificity (the more generic, the earlier in the list a partition appears). + */ + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final List SYSTEM_PARTITIONS = Collections.unmodifiableList( + Arrays.asList( + new SystemPartition(Environment.getRootDirectory(), 0 /* scanFlag */, + true /* hasPriv */, false /* hasOverlays */), + new SystemPartition(Environment.getVendorDirectory(), SCAN_AS_VENDOR, + true /* hasPriv */, true /* hasOverlays */), + new SystemPartition(Environment.getOdmDirectory(), SCAN_AS_ODM, + true /* hasPriv */, true /* hasOverlays */), + new SystemPartition(Environment.getOemDirectory(), SCAN_AS_OEM, + false /* hasPriv */, true /* hasOverlays */), + new SystemPartition(Environment.getProductDirectory(), SCAN_AS_PRODUCT, + true /* hasPriv */, true /* hasOverlays */), + new SystemPartition(Environment.getSystemExtDirectory(), SCAN_AS_SYSTEM_EXT, + true /* hasPriv */, true /* hasOverlays */))); + + /** + * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors. + * + * NOTE: All getters should return the same instance for every call. */ @VisibleForTesting static class Injector { @@ -2410,9 +2423,54 @@ public class PackageManagerService extends IPackageManager.Stub } } + @VisibleForTesting(visibility = Visibility.PRIVATE) + static class SystemPartition { + public final File folder; + public final int scanFlag; + public final File appFolder; + @Nullable + public final File privAppFolder; + @Nullable + public final File overlayFolder; + + private SystemPartition(File folder, int scanFlag, boolean hasPrivApps, + boolean hasOverlays) { + this.folder = folder; + this.scanFlag = scanFlag; + this.appFolder = toCanonical(new File(folder, "app")); + this.privAppFolder = hasPrivApps ? toCanonical(new File(folder, "priv-app")) : null; + this.overlayFolder = hasOverlays ? toCanonical(new File(folder, "overlay")) : null; + } + + public boolean containsPrivApp(File scanFile) { + return FileUtils.contains(privAppFolder, scanFile); + } + + public boolean containsApp(File scanFile) { + return FileUtils.contains(appFolder, scanFile); + } + + public boolean containsPath(String path) { + return path.startsWith(folder.getPath() + "/"); + } + + public boolean containsPrivPath(String path) { + return privAppFolder != null && path.startsWith(privAppFolder.getPath() + "/"); + } + + private static File toCanonical(File dir) { + try { + return dir.getCanonicalFile(); + } catch (IOException e) { + // failed to look up canonical path, continue with original one + return dir; + } + } + } + public PackageManagerService(Context context, Installer installer, - boolean factoryTest, boolean onlyCore) { - LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES); + boolean factoryTest, boolean onlyCore) { + LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "create package manager"); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis()); @@ -2640,215 +2698,35 @@ public class PackageManagerService extends IPackageManager.Stub // any apps.) // For security and version matching reason, only consider overlay packages if they // reside in the right directory. - scanDirTracedLI(new File(VENDOR_OVERLAY_DIR), - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_VENDOR, - 0); - scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR), - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT, - 0); - scanDirTracedLI(new File(SYSTEM_EXT_OVERLAY_DIR), - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_SYSTEM_EXT, - 0); - scanDirTracedLI(new File(ODM_OVERLAY_DIR), - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_ODM, - 0); - scanDirTracedLI(new File(OEM_OVERLAY_DIR), - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_OEM, - 0); + 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); + if (partition.overlayFolder == null) { + continue; + } + scanDirTracedLI(partition.overlayFolder, systemParseFlags, + systemScanFlags | partition.scanFlag, 0); + } mParallelPackageParserCallback.findStaticOverlayPackages(); - // Find base frameworks (resource packages without code). - scanDirTracedLI(frameworkDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_NO_DEX - | SCAN_AS_SYSTEM - | SCAN_AS_PRIVILEGED, - 0); + scanDirTracedLI(frameworkDir, systemParseFlags, + systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0); if (!mPackages.containsKey("android")) { throw new IllegalStateException( "Failed to load frameworks package; check log for warnings"); } - - // Collect privileged system packages. - final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app"); - scanDirTracedLI(privilegedAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_PRIVILEGED, - 0); - - // Collect ordinary system packages. - final File systemAppDir = new File(Environment.getRootDirectory(), "app"); - scanDirTracedLI(systemAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM, - 0); - - // Collect privileged vendor packages. - File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app"); - try { - privilegedVendorAppDir = privilegedVendorAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one + for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) { + final SystemPartition partition = SYSTEM_PARTITIONS.get(i); + if (partition.privAppFolder != null) { + scanDirTracedLI(partition.privAppFolder, systemParseFlags, + systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0); + } + scanDirTracedLI(partition.appFolder, systemParseFlags, + systemScanFlags | partition.scanFlag, 0); } - scanDirTracedLI(privilegedVendorAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_VENDOR - | SCAN_AS_PRIVILEGED, - 0); - // Collect ordinary vendor packages. - File vendorAppDir = new File(Environment.getVendorDirectory(), "app"); - try { - vendorAppDir = vendorAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one - } - scanDirTracedLI(vendorAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_VENDOR, - 0); - - // Collect privileged odm packages. /odm is another vendor partition - // other than /vendor. - File privilegedOdmAppDir = new File(Environment.getOdmDirectory(), - "priv-app"); - try { - privilegedOdmAppDir = privilegedOdmAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one - } - scanDirTracedLI(privilegedOdmAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_VENDOR - | SCAN_AS_PRIVILEGED, - 0); - - // Collect ordinary odm packages. /odm is another vendor partition - // other than /vendor. - File odmAppDir = new File(Environment.getOdmDirectory(), "app"); - try { - odmAppDir = odmAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one - } - scanDirTracedLI(odmAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_VENDOR, - 0); - - // Collect all OEM packages. - final File oemAppDir = new File(Environment.getOemDirectory(), "app"); - scanDirTracedLI(oemAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_OEM, - 0); - - // Collected privileged /product packages. - File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app"); - try { - privilegedProductAppDir = privilegedProductAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one - } - scanDirTracedLI(privilegedProductAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT - | SCAN_AS_PRIVILEGED, - 0); - - // Collect ordinary /product packages. - File productAppDir = new File(Environment.getProductDirectory(), "app"); - try { - productAppDir = productAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one - } - scanDirTracedLI(productAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT, - 0); - - // Collected privileged /system_ext packages. - File privilegedSystemExtAppDir = - new File(Environment.getSystemExtDirectory(), "priv-app"); - try { - privilegedSystemExtAppDir = - privilegedSystemExtAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one - } - scanDirTracedLI(privilegedSystemExtAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_SYSTEM_EXT - | SCAN_AS_PRIVILEGED, - 0); - - // Collect ordinary /system_ext packages. - File systemExtAppDir = new File(Environment.getSystemExtDirectory(), "app"); - try { - systemExtAppDir = systemExtAppDir.getCanonicalFile(); - } catch (IOException e) { - // failed to look up canonical path, continue with original one - } - scanDirTracedLI(systemExtAppDir, - mDefParseFlags - | PackageParser.PARSE_IS_SYSTEM_DIR, - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_SYSTEM_EXT, - 0); // Prune any system packages that no longer exist. final List possiblyDeletedUpdatedSystemApps = new ArrayList<>(); @@ -3016,89 +2894,26 @@ public class PackageManagerService extends IPackageManager.Stub logCriticalInfo(Log.WARN, "Expected better " + packageName + " but never showed up; reverting to system"); - final @ParseFlags int reparseFlags; - final @ScanFlags int rescanFlags; - if (FileUtils.contains(privilegedAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_PRIVILEGED; - } else if (FileUtils.contains(systemAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM; - } else if (FileUtils.contains(privilegedVendorAppDir, scanFile) - || FileUtils.contains(privilegedOdmAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_VENDOR - | SCAN_AS_PRIVILEGED; - } else if (FileUtils.contains(vendorAppDir, scanFile) - || FileUtils.contains(odmAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_VENDOR; - } else if (FileUtils.contains(oemAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_OEM; - } else if (FileUtils.contains(privilegedProductAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT - | SCAN_AS_PRIVILEGED; - } else if (FileUtils.contains(productAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT; - } else if (FileUtils.contains(privilegedSystemExtAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_SYSTEM_EXT - | SCAN_AS_PRIVILEGED; - } else if (FileUtils.contains(systemExtAppDir, scanFile)) { - reparseFlags = - mDefParseFlags | - PackageParser.PARSE_IS_SYSTEM_DIR; - rescanFlags = - scanFlags - | SCAN_AS_SYSTEM - | SCAN_AS_SYSTEM_EXT; - } else { + @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); + if (partition.containsPrivApp(scanFile)) { + reparseFlags = systemParseFlags; + rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED + | partition.scanFlag; + break; + } + if (partition.containsApp(scanFile)) { + reparseFlags = systemParseFlags; + rescanFlags = systemScanFlags | partition.scanFlag; + break; + } + } + if (rescanFlags == 0) { Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile); continue; } - mSettings.enableSystemPackageLPw(packageName); try { @@ -18560,70 +18375,15 @@ public class PackageManagerService extends IPackageManager.Stub } static boolean locationIsPrivileged(String path) { - try { - final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app"); - final File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app"); - final File privilegedOdmAppDir = new File(Environment.getOdmDirectory(), "priv-app"); - final File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app"); - final File privilegedSystemExtAppDir = - new File(Environment.getSystemExtDirectory(), "priv-app"); - return path.startsWith(privilegedAppDir.getCanonicalPath() + "/") - || path.startsWith(privilegedVendorAppDir.getCanonicalPath() + "/") - || path.startsWith(privilegedOdmAppDir.getCanonicalPath() + "/") - || path.startsWith(privilegedProductAppDir.getCanonicalPath() + "/") - || path.startsWith(privilegedSystemExtAppDir.getCanonicalPath() + "/"); - } catch (IOException e) { - Slog.e(TAG, "Unable to access code path " + path); + for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) { + SystemPartition partition = SYSTEM_PARTITIONS.get(i); + if (partition.containsPrivPath(path)) { + return true; + } } return false; } - static boolean locationIsOem(String path) { - try { - return path.startsWith(Environment.getOemDirectory().getCanonicalPath() + "/"); - } catch (IOException e) { - Slog.e(TAG, "Unable to access code path " + path); - } - return false; - } - - static boolean locationIsVendor(String path) { - try { - return path.startsWith(Environment.getVendorDirectory().getCanonicalPath() + "/") - || path.startsWith(Environment.getOdmDirectory().getCanonicalPath() + "/"); - } catch (IOException e) { - Slog.e(TAG, "Unable to access code path " + path); - } - return false; - } - - static boolean locationIsProduct(String path) { - try { - return path.startsWith(Environment.getProductDirectory().getCanonicalPath() + "/"); - } catch (IOException e) { - Slog.e(TAG, "Unable to access code path " + path); - } - return false; - } - - static boolean locationIsSystemExt(String path) { - try { - return path.startsWith( - Environment.getSystemExtDirectory().getCanonicalPath() + "/"); - } catch (IOException e) { - Slog.e(TAG, "Unable to access code path " + path); - } - return false; - } - - static boolean locationIsOdm(String path) { - try { - return path.startsWith(Environment.getOdmDirectory().getCanonicalPath() + "/"); - } catch (IOException e) { - Slog.e(TAG, "Unable to access code path " + path); - } - return false; - } /* * Tries to delete system package. @@ -18730,23 +18490,15 @@ public class PackageManagerService extends IPackageManager.Stub | PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM_DIR; @ScanFlags int scanFlags = SCAN_AS_SYSTEM; - if (locationIsPrivileged(codePathString)) { - scanFlags |= SCAN_AS_PRIVILEGED; - } - if (locationIsOem(codePathString)) { - scanFlags |= SCAN_AS_OEM; - } - if (locationIsVendor(codePathString)) { - scanFlags |= SCAN_AS_VENDOR; - } - if (locationIsProduct(codePathString)) { - scanFlags |= SCAN_AS_PRODUCT; - } - if (locationIsSystemExt(codePathString)) { - scanFlags |= SCAN_AS_SYSTEM_EXT; - } - if (locationIsOdm(codePathString)) { - scanFlags |= SCAN_AS_ODM; + for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) { + SystemPartition partition = SYSTEM_PARTITIONS.get(i); + if (partition.containsPath(codePathString)) { + scanFlags |= partition.scanFlag; + if (partition.containsPrivPath(codePathString)) { + scanFlags |= SCAN_AS_PRIVILEGED; + } + break; + } } final File codePath = new File(codePathString); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index fc7cfec9dc875..0a310d1936756 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -118,17 +118,20 @@ public class PackageManagerServiceTest { String[] partitions = { "system", "vendor", "odm", "oem", "product", "system_ext" }; String[] appdir = { "app", "priv-app" }; for (int i = 0; i < partitions.length; i++) { + final PackageManagerService.SystemPartition systemPartition = + PackageManagerService.SYSTEM_PARTITIONS.get(i); for (int j = 0; j < appdir.length; j++) { String canonical = new File("/" + partitions[i]).getCanonicalPath(); String path = String.format("%s/%s/A.apk", canonical, appdir[j]); - Assert.assertEquals(j == 1 && i != 3, - PackageManagerService.locationIsPrivileged(path)); + Assert.assertEquals(j == 1 && i != 3, systemPartition.containsPrivPath(path)); - Assert.assertEquals(i == 1 || i == 2, PackageManagerService.locationIsVendor(path)); - Assert.assertEquals(i == 3, PackageManagerService.locationIsOem(path)); - Assert.assertEquals(i == 4, PackageManagerService.locationIsProduct(path)); - Assert.assertEquals(i == 5, PackageManagerService.locationIsSystemExt(path)); + final int scanFlag = systemPartition.scanFlag; + Assert.assertEquals(i == 1, scanFlag == PackageManagerService.SCAN_AS_VENDOR); + Assert.assertEquals(i == 2, scanFlag == PackageManagerService.SCAN_AS_ODM); + Assert.assertEquals(i == 3, scanFlag == PackageManagerService.SCAN_AS_OEM); + Assert.assertEquals(i == 4, scanFlag == PackageManagerService.SCAN_AS_PRODUCT); + Assert.assertEquals(i == 5, scanFlag == PackageManagerService.SCAN_AS_SYSTEM_EXT); } } } From 7d038cd0e96eca070f5610356125a0b408d5819e Mon Sep 17 00:00:00 2001 From: Dario Freni Date: Wed, 9 Oct 2019 15:43:38 +0100 Subject: [PATCH 2/4] 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. Exempt-From-Owner-Approval: Approved in https://googleplex-android-review.git.corp.google.com/c/platform/frameworks/base/+/9533445 Merged-In: I8fb91317da0296567403b2b62babbb71e4b48938 Change-Id: I8fb91317da0296567403b2b62babbb71e4b48938 --- core/java/android/os/Environment.java | 15 +++- .../com/android/server/pm/ApexManager.java | 79 +++++++++++++++++-- .../server/pm/PackageAbiHelperImpl.java | 9 +++ .../server/pm/PackageManagerService.java | 45 ++++++++--- 4 files changed, 133 insertions(+), 15 deletions(-) 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)) { From 787471af481e875c59a7720f51174c893f85c97f Mon Sep 17 00:00:00 2001 From: Dario Freni Date: Tue, 22 Oct 2019 14:14:00 +0100 Subject: [PATCH 3/4] Support privileged apps installed in APEX. If an APEX contains a priv-app/ directory, and the APEX package itself is located in a partition for which privileged apps are allowed, then the APKs under said directory will be included in the initial scan and made available to the system. Bug: 138429615 Test: Move PermissionController to com.android.permission APEX. Run CtsPermissionTestCases and verify that the 16 failures out of 264 tests, which seem unrelated to PermissionController, happen also on an unpatched build on head. Exempt-From-Owner-Approval: Approved in https://googleplex-android-review.git.corp.google.com/c/platform/frameworks/base/+/9593749 Merged-In: If809db5d0a061c1a55aeb7f830c183fa822adca5 Change-Id: If809db5d0a061c1a55aeb7f830c183fa822adca5 --- .../server/pm/PackageManagerService.java | 47 ++++++++++--------- .../java/com/android/server/pm/Settings.java | 2 +- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ef1fcdaa4089c..7e05ce72850ae 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -756,17 +756,17 @@ public class PackageManagerService extends IPackageManager.Stub static final List SYSTEM_PARTITIONS = Collections.unmodifiableList( Arrays.asList( new SystemPartition(Environment.getRootDirectory(), 0 /* scanFlag */, - true /* hasPriv */, false /* hasOverlays */), + false /* hasOverlays */), new SystemPartition(Environment.getVendorDirectory(), SCAN_AS_VENDOR, - true /* hasPriv */, true /* hasOverlays */), + true /* hasOverlays */), new SystemPartition(Environment.getOdmDirectory(), SCAN_AS_ODM, - true /* hasPriv */, true /* hasOverlays */), + true /* hasOverlays */), new SystemPartition(Environment.getOemDirectory(), SCAN_AS_OEM, - false /* hasPriv */, true /* hasOverlays */), + true /* hasOverlays */), new SystemPartition(Environment.getProductDirectory(), SCAN_AS_PRODUCT, - true /* hasPriv */, true /* hasOverlays */), + true /* hasOverlays */), new SystemPartition(Environment.getSystemExtDirectory(), SCAN_AS_SYSTEM_EXT, - true /* hasPriv */, true /* hasOverlays */))); + true /* hasOverlays */))); private final List mDirsToScanAsSystem; @@ -2436,12 +2436,28 @@ public class PackageManagerService extends IPackageManager.Stub @Nullable public final File overlayFolder; - private SystemPartition(File folder, int scanFlag, boolean hasPrivApps, - boolean hasOverlays) { + + private static boolean shouldScanPrivApps(@ScanFlags int scanFlags) { + if ((scanFlags & SCAN_AS_OEM) != 0) { + return false; + } + if (scanFlags == 0) { // /system partition + return true; + } + if ((scanFlags + & (SCAN_AS_VENDOR | SCAN_AS_ODM | SCAN_AS_PRODUCT | SCAN_AS_SYSTEM_EXT)) != 0) { + return true; + } + return false; + } + + private SystemPartition(File folder, int scanFlag, boolean hasOverlays) { this.folder = folder; this.scanFlag = scanFlag; this.appFolder = toCanonical(new File(folder, "app")); - this.privAppFolder = hasPrivApps ? toCanonical(new File(folder, "priv-app")) : null; + this.privAppFolder = shouldScanPrivApps(scanFlag) + ? toCanonical(new File(folder, "priv-app")) + : null; this.overlayFolder = hasOverlays ? toCanonical(new File(folder, "overlay")) : null; } @@ -18388,17 +18404,6 @@ 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)) { - return true; - } - } - return false; - } - private static @Nullable SystemPartition resolveApexToSystemPartition( ApexManager.ActiveApexInfo apexInfo) { for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) { @@ -18406,7 +18411,7 @@ public class PackageManagerService extends IPackageManager.Stub if (apexInfo.preinstalledApexPath.getAbsolutePath().startsWith( sp.folder.getAbsolutePath())) { return new SystemPartition(apexInfo.apexDirectory, sp.scanFlag, - false /* hasPriv */, false /* hasOverlays */); + false /* hasOverlays */); } } return null; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 4ecfbfee6e654..d1e4537b9235a 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3506,7 +3506,7 @@ public final class Settings { int pkgFlags = 0; int pkgPrivateFlags = 0; pkgFlags |= ApplicationInfo.FLAG_SYSTEM; - if (PackageManagerService.locationIsPrivileged(codePathStr)) { + if (codePathStr.contains("/priv-app/")) { pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; } PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr), From 1e5261ce6f913c471cd099f476eb4411ce122ddb Mon Sep 17 00:00:00 2001 From: Patrick Baumann Date: Wed, 6 Nov 2019 10:36:39 -0800 Subject: [PATCH 4/4] DO NOT MERGE: Fixes NPE when preparing app data during init When deleting an unused static shared library on Q, the user manager was fetched via mContext.getSystemService. At this time during boot, the service wasn't registered and so null was returned. This has already been addressed in R with a move to injecting dependencies in the PackageManagerService constructor. Bug: 142083996 Bug: 141413692 Test: manual; remove static dependency on eng Q build and reboot Exempt-From-Owner-Approval: Already approved in https://googleplex-android-review.git.corp.google.com/c/platform/frameworks/base/+/9684883/ Change-Id: I8ae4e331d09b4734c54cdc6887b273705dce88b1 Merged-In: I8ae4e331d09b4734c54cdc6887b273705dce88b1 --- .../java/com/android/server/pm/PackageManagerService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7e05ce72850ae..6d32f7aed8d7f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -22344,9 +22344,9 @@ public class PackageManagerService extends IPackageManager.Stub mSettings.writeKernelMappingLPr(ps); } - final UserManager um = mContext.getSystemService(UserManager.class); + final UserManagerService um = sUserManager; UserManagerInternal umInternal = getUserManagerInternal(); - for (UserInfo user : um.getUsers()) { + for (UserInfo user : um.getUsers(false /* excludeDying */)) { final int flags; if (umInternal.isUserUnlockingOrUnlocked(user.id)) { flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;