diff options
8 files changed, 179 insertions, 92 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 803abf3f5f0d..d9ee41a28d61 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8902,6 +8902,14 @@ public final class Settings { public static final String PRIV_APP_OOB_ENABLED = "priv_app_oob_enabled"; /** + * Comma separated list of privileged package names, which will be running out-of-box APK. + * Default: "ALL" + * + * @hide + */ + public static final String PRIV_APP_OOB_LIST = "priv_app_oob_list"; + + /** * The interval in milliseconds at which location requests will be throttled when they are * coming from the background. * diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index db221cd7d8b9..0d60f331ab02 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -355,6 +355,7 @@ public class SettingsBackupTest { Settings.Global.POWER_MANAGER_CONSTANTS, Settings.Global.PREFERRED_NETWORK_MODE, Settings.Global.PRIV_APP_OOB_ENABLED, + Settings.Global.PRIV_APP_OOB_LIST, Settings.Global.PROVISIONING_APN_ALARM_DELAY_IN_MS, Settings.Global.RADIO_BLUETOOTH, Settings.Global.RADIO_CELL, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9de876ec4408..24f544e2aa7a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -466,6 +466,7 @@ import com.android.server.firewall.IntentFirewall; import com.android.server.job.JobSchedulerInternal; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; +import com.android.server.pm.dex.DexManager; import com.android.server.utils.PriorityDump; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.PinnedStackWindowController; @@ -4282,7 +4283,7 @@ public class ActivityManagerService extends IActivityManager.Stub } if (app.info.isPrivilegedApp() && - SystemProperties.getBoolean("pm.dexopt.priv-apps-oob", false)) { + DexManager.isPackageSelectedToRunOob(app.pkgList.keySet())) { runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES; } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index ebab1a72a227..b0be4a9799bb 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -34,6 +34,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import com.android.server.pm.Installer.InstallerException; +import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.dex.DexoptUtils; import com.android.server.pm.dex.PackageDexUsage; @@ -495,10 +496,9 @@ public class PackageDexOptimizer { boolean isUsedByOtherApps) { int flags = info.flags; boolean vmSafeMode = (flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0; - // When pm.dexopt.priv-apps-oob is true, we only verify privileged apps. - if (info.isPrivilegedApp() && - SystemProperties.getBoolean("pm.dexopt.priv-apps-oob", false)) { - return "verify"; + // When a priv app is configured to run out of box, only verify it. + if (info.isPrivilegedApp() && DexManager.isPackageSelectedToRunOob(info.packageName)) { + return "verify"; } if (vmSafeMode) { return getSafeModeCompilerFilter(targetCompilerFilter); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c2769380dc58..327acf370c3e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -578,8 +578,6 @@ public class PackageManagerService extends IPackageManager.Stub private static final String PRODUCT_OVERLAY_DIR = "/product/overlay"; - private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob"; - /** Canonical intent used to identify what counts as a "web browser" app */ private static final Intent sBrowserIntent; static { @@ -2459,7 +2457,7 @@ public class PackageManagerService extends IPackageManager.Stub "*dexopt*"); DexManager.Listener dexManagerListener = DexLogger.getListener(this, installer, mInstallLock); - mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock, + mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock, dexManagerListener); mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock); mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper()); @@ -10428,11 +10426,7 @@ public class PackageManagerService extends IPackageManager.Stub Log.d(TAG, "Scanning package " + pkg.packageName); } - if (Build.IS_DEBUGGABLE && - pkg.isPrivileged() && - SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false)) { - PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg); - } + DexManager.maybeLogUnexpectedPackageDetails(pkg); // Initialize package source and resource directories final File scanFile = new File(pkg.codePath); @@ -21023,23 +21017,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_SYSTEM); co.onChange(true); - // This observer provides an one directional mapping from Global.PRIV_APP_OOB_ENABLED to - // pm.dexopt.priv-apps-oob property. This is only for experiment and should be removed once - // it is done. - ContentObserver privAppOobObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange) { - int oobEnabled = Global.getInt(resolver, Global.PRIV_APP_OOB_ENABLED, 0); - SystemProperties.set(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, - oobEnabled == 1 ? "true" : "false"); - } - }; - mContext.getContentResolver().registerContentObserver( - Global.getUriFor(Global.PRIV_APP_OOB_ENABLED), false, privAppOobObserver, - UserHandle.USER_SYSTEM); - // At boot, restore the value from the setting, which persists across reboot. - privAppOobObserver.onChange(true); - // Disable any carrier apps. We do this very early in boot to prevent the apps from being // disabled after already being started. CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this, @@ -21128,6 +21105,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); storage.registerListener(mStorageListener); mInstallerService.systemReady(); + mDexManager.systemReady(); mPackageDexOptimizer.systemReady(); StorageManagerInternal StorageManagerInternal = LocalServices.getService( diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 4b907f4047b1..1aea8f0b0543 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -26,7 +26,6 @@ import static com.android.server.pm.PackageManagerService.TAG; import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import com.android.internal.content.NativeLibraryHelper; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.server.EventLogTags; import com.android.server.pm.dex.DexManager; @@ -56,7 +55,6 @@ import android.util.ArraySet; import android.util.Log; import android.util.PackageUtils; import android.util.Slog; -import android.util.jar.StrictJarFile; import android.util.proto.ProtoOutputStream; import dalvik.system.VMRuntime; @@ -85,12 +83,10 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.function.Predicate; import java.util.zip.GZIPInputStream; -import java.util.zip.ZipEntry; /** * Class containing helper methods for the PackageManagerService. @@ -317,61 +313,6 @@ public class PackageManagerServiceUtils { return maxModifiedTime; } - /** - * Checks that the archive located at {@code fileName} has uncompressed dex file and so - * files that can be direclty mapped. - */ - public static void logApkHasUncompressedCode(String fileName) { - StrictJarFile jarFile = null; - try { - jarFile = new StrictJarFile(fileName, - false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/); - Iterator<ZipEntry> it = jarFile.iterator(); - while (it.hasNext()) { - ZipEntry entry = it.next(); - if (entry.getName().endsWith(".dex")) { - if (entry.getMethod() != ZipEntry.STORED) { - Slog.w(TAG, "APK " + fileName + " has compressed dex code " + - entry.getName()); - } else if ((entry.getDataOffset() & 0x3) != 0) { - Slog.w(TAG, "APK " + fileName + " has unaligned dex code " + - entry.getName()); - } - } else if (entry.getName().endsWith(".so")) { - if (entry.getMethod() != ZipEntry.STORED) { - Slog.w(TAG, "APK " + fileName + " has compressed native code " + - entry.getName()); - } else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) { - Slog.w(TAG, "APK " + fileName + " has unaligned native code " + - entry.getName()); - } - } - } - } catch (IOException ignore) { - Slog.wtf(TAG, "Error when parsing APK " + fileName); - } finally { - try { - if (jarFile != null) { - jarFile.close(); - } - } catch (IOException ignore) {} - } - return; - } - - /** - * Checks that the APKs in the given package have uncompressed dex file and so - * files that can be direclty mapped. - */ - public static void logPackageHasUncompressedCode(PackageParser.Package pkg) { - logApkHasUncompressedCode(pkg.baseCodePath); - if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { - for (int i = 0; i < pkg.splitCodePaths.length; i++) { - logApkHasUncompressedCode(pkg.splitCodePaths[i]); - } - } - } - private static File getSettingsProblemFile() { File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index 3e63fb42f0ef..392d4d839c45 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -16,17 +16,25 @@ package com.android.server.pm.dex; +import android.content.ContentResolver; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; +import android.content.pm.PackageParser; +import android.database.ContentObserver; +import android.os.Build; import android.os.FileUtils; import android.os.RemoteException; import android.os.storage.StorageManager; +import android.os.SystemProperties; import android.os.UserHandle; - +import android.provider.Settings.Global; import android.util.Slog; +import android.util.jar.StrictJarFile; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.PackageDexOptimizer; @@ -36,13 +44,16 @@ import com.android.server.pm.PackageManagerServiceCompilerMapping; import java.io.File; import java.io.IOException; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.zip.ZipEntry; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo; @@ -59,8 +70,14 @@ import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo; public class DexManager { private static final String TAG = "DexManager"; + private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob"; + private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST = + "pm.dexopt.priv-apps-oob-list"; + private static final boolean DEBUG = false; + private final Context mContext; + // Maps package name to code locations. // It caches the code locations for the installed packages. This allows for // faster lookups (no locks) when finding what package owns the dex file. @@ -106,8 +123,9 @@ public class DexManager { String dexPath, int storageFlags); } - public DexManager(IPackageManager pms, PackageDexOptimizer pdo, + public DexManager(Context context, IPackageManager pms, PackageDexOptimizer pdo, Installer installer, Object installLock, Listener listener) { + mContext = context; mPackageCodeLocationsCache = new HashMap<>(); mPackageDexUsage = new PackageDexUsage(); mPackageManager = pms; @@ -117,6 +135,10 @@ public class DexManager { mListener = listener; } + public void systemReady() { + registerSettingObserver(); + } + /** * Notify about dex files loads. * Note that this method is invoked when apps load dex files and it should @@ -641,6 +663,141 @@ public class DexManager { mPackageDexUsage.writeNow(); } + private void registerSettingObserver() { + final ContentResolver resolver = mContext.getContentResolver(); + + // This observer provides a one directional mapping from Global.PRIV_APP_OOB_ENABLED to + // pm.dexopt.priv-apps-oob property. This is only for experiment and should be removed once + // it is done. + ContentObserver privAppOobObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + int oobEnabled = Global.getInt(resolver, Global.PRIV_APP_OOB_ENABLED, 0); + SystemProperties.set(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, + oobEnabled == 1 ? "true" : "false"); + } + }; + resolver.registerContentObserver( + Global.getUriFor(Global.PRIV_APP_OOB_ENABLED), false, privAppOobObserver, + UserHandle.USER_SYSTEM); + // At boot, restore the value from the setting, which persists across reboot. + privAppOobObserver.onChange(true); + + ContentObserver privAppOobListObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + String oobList = Global.getString(resolver, Global.PRIV_APP_OOB_LIST); + if (oobList == null) { + oobList = "ALL"; + } + SystemProperties.set(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST, oobList); + } + }; + resolver.registerContentObserver( + Global.getUriFor(Global.PRIV_APP_OOB_LIST), false, privAppOobListObserver, + UserHandle.USER_SYSTEM); + // At boot, restore the value from the setting, which persists across reboot. + privAppOobListObserver.onChange(true); + } + + /** + * Returns whether the given package is in the list of privilaged apps that should run out of + * box. This only makes sense if PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB is true. Note that when + * the the OOB list is empty, all priv apps will run in OOB mode. + */ + public static boolean isPackageSelectedToRunOob(String packageName) { + return isPackageSelectedToRunOob(Arrays.asList(packageName)); + } + + /** + * Returns whether any of the given packages are in the list of privilaged apps that should run + * out of box. This only makes sense if PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB is true. Note that + * when the the OOB list is empty, all priv apps will run in OOB mode. + */ + public static boolean isPackageSelectedToRunOob(Collection<String> packageNamesInSameProcess) { + if (!SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false)) { + return false; + } + String oobListProperty = SystemProperties.get( + PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST, "ALL"); + if ("ALL".equals(oobListProperty)) { + return true; + } + for (String oobPkgName : oobListProperty.split(",")) { + if (packageNamesInSameProcess.contains(oobPkgName)) { + return true; + } + } + return false; + } + + /** + * Generates package related log if the package has code stored in unexpected way. + */ + public static void maybeLogUnexpectedPackageDetails(PackageParser.Package pkg) { + if (!Build.IS_DEBUGGABLE) { + return; + } + + if (pkg.isPrivileged() && isPackageSelectedToRunOob(pkg.packageName)) { + logIfPackageHasUncompressedCode(pkg); + } + } + + /** + * Generates log if the APKs in the given package have uncompressed dex file and so + * files that can be direclty mapped. + */ + private static void logIfPackageHasUncompressedCode(PackageParser.Package pkg) { + logIfApkHasUncompressedCode(pkg.baseCodePath); + if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { + for (int i = 0; i < pkg.splitCodePaths.length; i++) { + logIfApkHasUncompressedCode(pkg.splitCodePaths[i]); + } + } + } + + /** + * Generates log if the archive located at {@code fileName} has uncompressed dex file and so + * files that can be direclty mapped. + */ + private static void logIfApkHasUncompressedCode(String fileName) { + StrictJarFile jarFile = null; + try { + jarFile = new StrictJarFile(fileName, + false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/); + Iterator<ZipEntry> it = jarFile.iterator(); + while (it.hasNext()) { + ZipEntry entry = it.next(); + if (entry.getName().endsWith(".dex")) { + if (entry.getMethod() != ZipEntry.STORED) { + Slog.w(TAG, "APK " + fileName + " has compressed dex code " + + entry.getName()); + } else if ((entry.getDataOffset() & 0x3) != 0) { + Slog.w(TAG, "APK " + fileName + " has unaligned dex code " + + entry.getName()); + } + } else if (entry.getName().endsWith(".so")) { + if (entry.getMethod() != ZipEntry.STORED) { + Slog.w(TAG, "APK " + fileName + " has compressed native code " + + entry.getName()); + } else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) { + Slog.w(TAG, "APK " + fileName + " has unaligned native code " + + entry.getName()); + } + } + } + } catch (IOException ignore) { + Slog.wtf(TAG, "Error when parsing APK " + fileName); + } finally { + try { + if (jarFile != null) { + jarFile.close(); + } + } catch (IOException ignore) {} + } + } + public static class RegisterDexModuleResult { public RegisterDexModuleResult() { this(false, null); diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java index 36d0c8bec09e..147347d47444 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -111,7 +111,8 @@ public class DexManagerTests { DELEGATE_LAST_CLASS_LOADER_NAME); mDexManager = new DexManager( - mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock, mListener); + /*Context*/ null, mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock, + mListener); // Foo and Bar are available to user0. // Only Bar is available to user1; |