Merge "Optionally use ART Service for optimizing packages."

This commit is contained in:
Martin Stjernholm
2022-10-19 16:08:42 +00:00
committed by Android (Google) Code Review
2 changed files with 298 additions and 8 deletions

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}