diff options
author | 2022-10-19 16:08:42 +0000 | |
---|---|---|
committer | 2022-10-19 16:08:42 +0000 | |
commit | cd4d015a09d4d887b8b76d1ed3549bb9e44a133d (patch) | |
tree | 66dfc6a9e3d7b0a5f9a55651fbadd0d433e8b45e | |
parent | a0d351ca0b2c79702e1c621e549b0f11a483846e (diff) | |
parent | 901d13ba1a6a785b3805816abad51c6a57be6a5e (diff) |
Merge "Optionally use ART Service for optimizing packages."
-rw-r--r-- | services/core/java/com/android/server/pm/DexOptHelper.java | 167 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/dex/DexoptOptions.java | 139 |
2 files changed, 298 insertions, 8 deletions
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index 3f04264714e4..c4f6836eba7b 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import static com.android.server.LocalManagerRegistry.ManagerNotFoundException; import static com.android.server.pm.ApexManager.ActiveApexInfo; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT; @@ -34,6 +35,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.REMOVE_IF_NULL_PK import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.AppGlobals; @@ -56,9 +58,16 @@ import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; +import com.android.server.LocalManagerRegistry; +import com.android.server.art.ArtManagerLocal; +import com.android.server.art.model.ArtFlags; +import com.android.server.art.model.OptimizeParams; +import com.android.server.art.model.OptimizeResult; +import com.android.server.pm.PackageDexOptimizer.DexOptResult; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageState; import com.android.server.pm.pkg.PackageStateInternal; import dalvik.system.DexFile; @@ -72,11 +81,15 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; -final class DexOptHelper { +/** + * Helper class for dex optimization operations in PackageManagerService. + */ +public final class DexOptHelper { private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000; private final PackageManagerService mPm; @@ -405,11 +418,12 @@ final class DexOptHelper { * {@link PackageDexOptimizer#DEX_OPT_CANCELLED} * {@link PackageDexOptimizer#DEX_OPT_FAILED} */ - @PackageDexOptimizer.DexOptResult + @DexOptResult /* package */ int performDexOptWithStatus(DexoptOptions options) { return performDexOptTraced(options); } + @DexOptResult private int performDexOptTraced(DexoptOptions options) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); try { @@ -421,7 +435,13 @@ final class DexOptHelper { // Run dexopt on a given package. Returns true if dexopt did not fail, i.e. // if the package can now be considered up to date for the given filter. + @DexOptResult private int performDexOptInternal(DexoptOptions options) { + Optional<Integer> artSrvRes = performDexOptWithArtService(options); + if (artSrvRes.isPresent()) { + return artSrvRes.get(); + } + AndroidPackage p; PackageSetting pkgSetting; synchronized (mPm.mLock) { @@ -446,8 +466,74 @@ final class DexOptHelper { } } - private int performDexOptInternalWithDependenciesLI(AndroidPackage p, - @NonNull PackageStateInternal pkgSetting, DexoptOptions options) { + /** + * Performs dexopt on the given package using ART Service. + * + * @return a {@link DexOptResult}, or empty if the request isn't supported so that it is + * necessary to fall back to the legacy code paths. + */ + private Optional<Integer> performDexOptWithArtService(DexoptOptions options) { + ArtManagerLocal artManager = getArtManagerLocal(); + if (artManager == null) { + return Optional.empty(); + } + + try (PackageManagerLocal.FilteredSnapshot snapshot = + getPackageManagerLocal().withFilteredSnapshot()) { + PackageState ops = snapshot.getPackageState(options.getPackageName()); + if (ops == null) { + return Optional.of(PackageDexOptimizer.DEX_OPT_FAILED); + } + AndroidPackage oap = ops.getAndroidPackage(); + if (oap == null) { + return Optional.of(PackageDexOptimizer.DEX_OPT_FAILED); + } + if (oap.isApex()) { + return Optional.of(PackageDexOptimizer.DEX_OPT_SKIPPED); + } + + // TODO(b/245301593): Delete the conditional when ART Service supports + // FLAG_SHOULD_INCLUDE_DEPENDENCIES and we can just set it unconditionally. + /*@OptimizeFlags*/ int extraFlags = ops.getUsesLibraries().isEmpty() + ? 0 + : ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES; + + OptimizeParams params = options.convertToOptimizeParams(extraFlags); + if (params == null) { + return Optional.empty(); + } + + // TODO(b/251903639): Either remove controlDexOptBlocking, or don't ignore it here. + OptimizeResult result; + try { + result = artManager.optimizePackage(snapshot, options.getPackageName(), params); + } catch (UnsupportedOperationException e) { + reportArtManagerFallback(options.getPackageName(), e.toString()); + return Optional.empty(); + } + + // TODO(b/251903639): Move this to ArtManagerLocal.addOptimizePackageDoneCallback when + // it is implemented. + for (OptimizeResult.PackageOptimizeResult pkgRes : result.getPackageOptimizeResults()) { + PackageState ps = snapshot.getPackageState(pkgRes.getPackageName()); + AndroidPackage ap = ps != null ? ps.getAndroidPackage() : null; + if (ap != null) { + CompilerStats.PackageStats stats = mPm.getOrCreateCompilerPackageStats(ap); + for (OptimizeResult.DexContainerFileOptimizeResult dexRes : + pkgRes.getDexContainerFileOptimizeResults()) { + stats.setCompileTime( + dexRes.getDexContainerFile(), dexRes.getDex2oatWallTimeMillis()); + } + } + } + + return Optional.of(convertToDexOptResult(result)); + } + } + + @DexOptResult + private int performDexOptInternalWithDependenciesLI( + AndroidPackage p, @NonNull PackageStateInternal pkgSetting, DexoptOptions options) { // System server gets a special path. if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) { return mPm.getDexManager().dexoptSystemServer(options); @@ -514,10 +600,20 @@ final class DexOptHelper { // Whoever is calling forceDexOpt wants a compiled package. // Don't use profiles since that may cause compilation to be skipped. - final int res = performDexOptInternalWithDependenciesLI(pkg, packageState, - new DexoptOptions(packageName, REASON_CMDLINE, - getDefaultCompilerFilter(), null /* splitName */, - DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE)); + DexoptOptions options = new DexoptOptions(packageName, REASON_CMDLINE, + getDefaultCompilerFilter(), null /* splitName */, + DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE); + + // performDexOptWithArtService ignores the snapshot and takes its own, so it can race with + // the package checks above, but at worst the effect is only a bit less friendly error + // below. + Optional<Integer> artSrvRes = performDexOptWithArtService(options); + int res; + if (artSrvRes.isPresent()) { + res = artSrvRes.get(); + } else { + res = performDexOptInternalWithDependenciesLI(pkg, packageState, options); + } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) { @@ -800,4 +896,59 @@ final class DexOptHelper { } return false; } + + private @NonNull PackageManagerLocal getPackageManagerLocal() { + try { + return LocalManagerRegistry.getManagerOrThrow(PackageManagerLocal.class); + } catch (ManagerNotFoundException e) { + throw new RuntimeException(e); + } + } + + /** + * Called whenever we need to fall back from ART Service to the legacy dexopt code. + */ + public static void reportArtManagerFallback(String packageName, String reason) { + // STOPSHIP(b/251903639): Minimize these calls to avoid platform getting shipped with code + // paths that will always bypass ART Service. + Slog.i(TAG, "Falling back to old PackageManager dexopt for " + packageName + ": " + reason); + } + + /** + * Returns {@link ArtManagerLocal} if one is found and should be used for package optimization. + */ + private @Nullable ArtManagerLocal getArtManagerLocal() { + if (!"true".equals(SystemProperties.get("dalvik.vm.useartservice", ""))) { + return null; + } + try { + return LocalManagerRegistry.getManagerOrThrow(ArtManagerLocal.class); + } catch (ManagerNotFoundException e) { + throw new RuntimeException(e); + } + } + + /** + * Converts an ART Service {@link OptimizeResult} to {@link DexOptResult}. + * + * For interfacing {@link ArtManagerLocal} with legacy dex optimization code in PackageManager. + */ + @DexOptResult + private static int convertToDexOptResult(OptimizeResult result) { + /*@OptimizeStatus*/ int status = result.getFinalStatus(); + switch (status) { + case OptimizeResult.OPTIMIZE_SKIPPED: + return PackageDexOptimizer.DEX_OPT_SKIPPED; + case OptimizeResult.OPTIMIZE_FAILED: + return PackageDexOptimizer.DEX_OPT_FAILED; + case OptimizeResult.OPTIMIZE_PERFORMED: + return PackageDexOptimizer.DEX_OPT_PERFORMED; + case OptimizeResult.OPTIMIZE_CANCELLED: + return PackageDexOptimizer.DEX_OPT_CANCELLED; + default: + throw new IllegalArgumentException("OptimizeResult for " + + result.getPackageOptimizeResults().get(0).getPackageName() + + " has unsupported status " + status); + } + } } diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java index 13f6ee77aace..f5557c417f1b 100644 --- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java +++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java @@ -18,6 +18,16 @@ package com.android.server.pm.dex; import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason; +import android.annotation.Nullable; + +import com.android.server.art.ReasonMapping; +import com.android.server.art.model.ArtFlags; +import com.android.server.art.model.OptimizeParams; +import com.android.server.pm.DexOptHelper; +import com.android.server.pm.PackageManagerService; + +import dalvik.system.DexFile; + /** * Options used for dexopt invocations. */ @@ -189,4 +199,133 @@ public final class DexoptOptions { mSplitName, mFlags); } + + /** + * Returns an {@link OptimizeParams} instance corresponding to this object, for use with + * {@link com.android.server.art.ArtManagerLocal}. + * + * @param extraFlags extra {@link ArtFlags#OptimizeFlags} to set in the returned + * {@code OptimizeParams} beyond those converted from this object + * @return null if the settings cannot be accurately represented, and hence the old + * PackageManager/installd code paths need to be used. + */ + public @Nullable OptimizeParams convertToOptimizeParams(/*@OptimizeFlags*/ int extraFlags) { + if (mSplitName != null) { + DexOptHelper.reportArtManagerFallback( + mPackageName, "Request to optimize only split " + mSplitName); + return null; + } + + /*@OptimizeFlags*/ int flags = extraFlags; + if ((mFlags & DEXOPT_CHECK_FOR_PROFILES_UPDATES) == 0 + && DexFile.isProfileGuidedCompilerFilter(mCompilerFilter)) { + // ART Service doesn't support bypassing this, so not setting this flag is not + // supported. + DexOptHelper.reportArtManagerFallback(mPackageName, + "DEXOPT_CHECK_FOR_PROFILES_UPDATES not set with profile compiler filter"); + return null; + } + if ((mFlags & DEXOPT_FORCE) != 0) { + flags |= ArtFlags.FLAG_FORCE; + } + if ((mFlags & DEXOPT_ONLY_SECONDARY_DEX) != 0) { + flags |= ArtFlags.FLAG_FOR_SECONDARY_DEX; + } else { + flags |= ArtFlags.FLAG_FOR_PRIMARY_DEX; + } + if ((mFlags & DEXOPT_DOWNGRADE) != 0) { + flags |= ArtFlags.FLAG_SHOULD_DOWNGRADE; + } + if ((mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) == 0) { + // ART Service cannot be instructed to ignore a DM file if present, so not setting this + // flag is not supported. + DexOptHelper.reportArtManagerFallback( + mPackageName, "DEXOPT_INSTALL_WITH_DEX_METADATA_FILE not set"); + return null; + } + + /*@PriorityClassApi*/ int priority; + // Replicates logic in RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags in installd. + if ((mFlags & DEXOPT_BOOT_COMPLETE) != 0) { + if ((mFlags & DEXOPT_FOR_RESTORE) != 0) { + priority = ArtFlags.PRIORITY_INTERACTIVE_FAST; + } else { + // TODO(b/251903639): Repurpose DEXOPT_IDLE_BACKGROUND_JOB to choose new + // dalvik.vm.background-dex2oat-* properties. + priority = ArtFlags.PRIORITY_INTERACTIVE; + } + } else { + priority = ArtFlags.PRIORITY_BOOT; + } + + // The following flags in mFlags are ignored: + // + // - DEXOPT_AS_SHARED_LIBRARY: It's implicit with ART Service since it always looks at + // <uses-library> rather than actual dependencies. + // + // We don't require it to be set either. It's safe when switching between old and new + // code paths since the only effect is that some packages may be unnecessarily compiled + // without user profiles. + // + // - DEXOPT_IDLE_BACKGROUND_JOB: Its only effect is to allow the debug variant dex2oatd to + // be used, but ART Service never uses that (cf. Artd::GetDex2Oat in artd.cc). + + String reason; + switch (mCompilationReason) { + case PackageManagerService.REASON_FIRST_BOOT: + reason = ReasonMapping.REASON_FIRST_BOOT; + break; + case PackageManagerService.REASON_BOOT_AFTER_OTA: + reason = ReasonMapping.REASON_BOOT_AFTER_OTA; + break; + case PackageManagerService.REASON_POST_BOOT: + // This reason will go away with the legacy dexopt code. + DexOptHelper.reportArtManagerFallback( + mPackageName, "Unsupported compilation reason REASON_POST_BOOT"); + return null; + case PackageManagerService.REASON_INSTALL: + reason = ReasonMapping.REASON_INSTALL; + break; + case PackageManagerService.REASON_INSTALL_FAST: + reason = ReasonMapping.REASON_INSTALL_FAST; + break; + case PackageManagerService.REASON_INSTALL_BULK: + reason = ReasonMapping.REASON_INSTALL_BULK; + break; + case PackageManagerService.REASON_INSTALL_BULK_SECONDARY: + reason = ReasonMapping.REASON_INSTALL_BULK_SECONDARY; + break; + case PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED: + reason = ReasonMapping.REASON_INSTALL_BULK_DOWNGRADED; + break; + case PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED: + reason = ReasonMapping.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED; + break; + case PackageManagerService.REASON_BACKGROUND_DEXOPT: + reason = ReasonMapping.REASON_BG_DEXOPT; + break; + case PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE: + reason = ReasonMapping.REASON_INACTIVE; + break; + case PackageManagerService.REASON_CMDLINE: + reason = ReasonMapping.REASON_CMDLINE; + break; + case PackageManagerService.REASON_SHARED: + case PackageManagerService.REASON_AB_OTA: + // REASON_SHARED shouldn't go into this code path - it's only used at lower levels + // in PackageDexOptimizer. + // TODO(b/251921228): OTA isn't supported, so REASON_AB_OTA shouldn't come this way + // either. + throw new UnsupportedOperationException( + "ART Service unsupported compilation reason " + mCompilationReason); + default: + throw new IllegalArgumentException( + "Invalid compilation reason " + mCompilationReason); + } + + return new OptimizeParams.Builder(reason, flags) + .setCompilerFilter(mCompilerFilter) + .setPriorityClass(priority) + .build(); + } } |