Merge "Optionally use ART Service for optimizing packages."
This commit is contained in:
committed by
Android (Google) Code Review
commit
cd4d015a09
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user