Compile secondary dex files in DexManager

am: 7218363a92

Change-Id: I53e7864b097cf9cff702e9185bb1c9091d97fe63
This commit is contained in:
Calin Juravle
2017-03-03 00:03:17 +00:00
committed by android-build-merger
8 changed files with 226 additions and 20 deletions

View File

@@ -482,6 +482,7 @@ interface IPackageManager {
*/
boolean performDexOpt(String packageName, boolean checkProfiles,
int compileReason, boolean force);
/**
* Ask the package manager to perform a dex-opt with the given compiler filter.
*
@@ -491,6 +492,16 @@ interface IPackageManager {
boolean performDexOptMode(String packageName, boolean checkProfiles,
String targetCompilerFilter, boolean force);
/**
* Ask the package manager to perform a dex-opt with the given compiler filter on the
* secondary dex files belonging to the given package.
*
* Note: exposed only for the shell command to allow moving packages explicitly to a
* definite state.
*/
boolean performDexOptSecondary(String packageName,
String targetCompilerFilter, boolean force);
/**
* Ask the package manager to dump profiles associated with a package.
*/

View File

@@ -50,6 +50,14 @@ public class Installer extends SystemService {
public static final int DEXOPT_BOOTCOMPLETE = 1 << 4;
/** Hint that the dexopt type is profile-guided. */
public static final int DEXOPT_PROFILE_GUIDED = 1 << 5;
/** The compilation is for a secondary dex file. */
public static final int DEXOPT_SECONDARY_DEX = 1 << 6;
/** Ignore the result of dexoptNeeded and force compilation. */
public static final int DEXOPT_FORCE = 1 << 7;
/** Indicates that the dex file passed to dexopt in on CE storage. */
public static final int DEXOPT_STORAGE_CE = 1 << 8;
/** Indicates that the dex file passed to dexopt in on DE storage. */
public static final int DEXOPT_STORAGE_DE = 1 << 9;
// NOTE: keep in sync with installd
public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;

View File

@@ -19,6 +19,7 @@ package com.android.server.pm;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageParser;
import android.os.Environment;
import android.os.PowerManager;
@@ -35,6 +36,7 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import dalvik.system.DexFile;
@@ -43,6 +45,10 @@ import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
import static com.android.server.pm.Installer.DEXOPT_SAFEMODE;
import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
import static com.android.server.pm.Installer.DEXOPT_FORCE;
import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -52,13 +58,13 @@ import static dalvik.system.DexFile.isProfileGuidedCompilerFilter;
/**
* Helper class for running dexopt command on packages.
*/
class PackageDexOptimizer {
public class PackageDexOptimizer {
private static final String TAG = "PackageManager.DexOptimizer";
static final String OAT_DIR_NAME = "oat";
// TODO b/19550105 Remove error codes and use exceptions
static final int DEX_OPT_SKIPPED = 0;
static final int DEX_OPT_PERFORMED = 1;
static final int DEX_OPT_FAILED = -1;
public static final int DEX_OPT_SKIPPED = 0;
public static final int DEX_OPT_PERFORMED = 1;
public static final int DEX_OPT_FAILED = -1;
private final Installer mInstaller;
private final Object mInstallLock;
@@ -100,6 +106,9 @@ class PackageDexOptimizer {
return DEX_OPT_SKIPPED;
}
synchronized (mInstallLock) {
// During boot the system doesn't need to instantiate and obtain a wake lock.
// PowerManager might not be ready, but that doesn't mean that we can't proceed with
// dexopt.
final boolean useLock = mSystemReady;
if (useLock) {
mDexoptWakeLock.setWorkSource(new WorkSource(pkg.applicationInfo.uid));
@@ -130,9 +139,11 @@ class PackageDexOptimizer {
final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
final String compilerFilter = getRealCompilerFilter(pkg, targetCompilerFilter);
final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
targetCompilerFilter, isUsedByOtherApps(pkg));
final boolean profileUpdated = checkForProfileUpdates &&
isProfileUpdated(pkg, sharedGid, compilerFilter);
// TODO(calin,jeffhao): shared library paths should be adjusted to include previous code
// paths (b/34169257).
final String sharedLibrariesPath = getSharedLibrariesPath(sharedLibraries);
@@ -200,6 +211,79 @@ class PackageDexOptimizer {
}
}
/**
* Performs dexopt on the secondary dex {@code path} belonging to the app {@code info}.
*
* @return
* DEX_OPT_FAILED if there was any exception during dexopt
* DEX_OPT_PERFORMED if dexopt was performed successfully on the given path.
* NOTE that DEX_OPT_PERFORMED for secondary dex files includes the case when the dex file
* didn't need an update. That's because at the moment we don't get more than success/failure
* from installd.
*
* TODO(calin): Consider adding return codes to installd dexopt invocation (rather than
* throwing exceptions). Or maybe make a separate call to installd to get DexOptNeeded, though
* that seems wasteful.
*/
public int dexOptSecondaryDexPath(ApplicationInfo info, String path, Set<String> isas,
String compilerFilter, boolean isUsedByOtherApps) {
synchronized (mInstallLock) {
// During boot the system doesn't need to instantiate and obtain a wake lock.
// PowerManager might not be ready, but that doesn't mean that we can't proceed with
// dexopt.
final boolean useLock = mSystemReady;
if (useLock) {
mDexoptWakeLock.setWorkSource(new WorkSource(info.uid));
mDexoptWakeLock.acquire();
}
try {
return dexOptSecondaryDexPathLI(info, path, isas, compilerFilter,
isUsedByOtherApps);
} finally {
if (useLock) {
mDexoptWakeLock.release();
}
}
}
}
@GuardedBy("mInstallLock")
private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path, Set<String> isas,
String compilerFilter, boolean isUsedByOtherApps) {
int dexoptFlags = getDexFlags(info, compilerFilter) | DEXOPT_SECONDARY_DEX;
// Check the app storage and add the appropriate flags.
if (info.dataDir.equals(info.deviceProtectedDataDir)) {
dexoptFlags |= DEXOPT_STORAGE_DE;
} else if (info.dataDir.equals(info.credentialProtectedDataDir)) {
dexoptFlags |= DEXOPT_STORAGE_CE;
} else {
Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
return DEX_OPT_FAILED;
}
compilerFilter = getRealCompilerFilter(info, compilerFilter, isUsedByOtherApps);
Log.d(TAG, "Running dexopt on: " + path
+ " pkg=" + info.packageName + " isa=" + isas
+ " dexoptFlags=" + printDexoptFlags(dexoptFlags)
+ " target-filter=" + compilerFilter);
try {
for (String isa : isas) {
// Reuse the same dexopt path as for the primary apks. We don't need all the
// arguments as some (dexopNeeded and oatDir) will be computed by installd because
// system server cannot read untrusted app content.
// TODO(calin): maybe add a separate call.
mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
/*oatDir*/ null, dexoptFlags,
compilerFilter, info.volumeUuid, /*sharedLibrariesPath*/ null);
}
return DEX_OPT_PERFORMED;
} catch (InstallerException e) {
Slog.w(TAG, "Failed to dexopt", e);
return DEX_OPT_FAILED;
}
}
/**
* Adjust the given dexopt-needed value. Can be overridden to influence the decision to
* optimize or not (and in what way).
@@ -246,8 +330,9 @@ class PackageDexOptimizer {
* The target filter will be updated if the package code is used by other apps
* or if it has the safe mode flag set.
*/
private String getRealCompilerFilter(PackageParser.Package pkg, String targetCompilerFilter) {
int flags = pkg.applicationInfo.flags;
private String getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter,
boolean isUsedByOtherApps) {
int flags = info.flags;
boolean vmSafeMode = (flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
if (vmSafeMode) {
// For the compilation, it doesn't really matter what we return here because installd
@@ -259,7 +344,7 @@ class PackageDexOptimizer {
return getNonProfileGuidedCompilerFilter(targetCompilerFilter);
}
if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps(pkg)) {
if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) {
// If the dex files is used by other apps, we cannot use profile-guided compilation.
return getNonProfileGuidedCompilerFilter(targetCompilerFilter);
}
@@ -272,12 +357,16 @@ class PackageDexOptimizer {
* filter.
*/
private int getDexFlags(PackageParser.Package pkg, String compilerFilter) {
int flags = pkg.applicationInfo.flags;
return getDexFlags(pkg.applicationInfo, compilerFilter);
}
private int getDexFlags(ApplicationInfo info, String compilerFilter) {
int flags = info.flags;
boolean vmSafeMode = (flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
// Profile guide compiled oat files should not be public.
boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
boolean isPublic = !pkg.isForwardLocked() && !isProfileGuidedFilter;
boolean isPublic = !info.isForwardLocked() && !isProfileGuidedFilter;
int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
int dexFlags =
(isPublic ? DEXOPT_PUBLIC : 0)
@@ -437,6 +526,19 @@ class PackageDexOptimizer {
if ((flags & DEXOPT_SAFEMODE) == DEXOPT_SAFEMODE) {
flagsList.add("safemode");
}
if ((flags & DEXOPT_SECONDARY_DEX) == DEXOPT_SECONDARY_DEX) {
flagsList.add("secondary");
}
if ((flags & DEXOPT_FORCE) == DEXOPT_FORCE) {
flagsList.add("force");
}
if ((flags & DEXOPT_STORAGE_CE) == DEXOPT_STORAGE_CE) {
flagsList.add("storage_ce");
}
if ((flags & DEXOPT_STORAGE_DE) == DEXOPT_STORAGE_DE) {
flagsList.add("storage_de");
}
return String.join(",", flagsList);
}
@@ -461,5 +563,12 @@ class PackageDexOptimizer {
// TODO: The return value is wrong when patchoat is needed.
return DexFile.DEX2OAT_FROM_SCRATCH;
}
@Override
protected int adjustDexoptFlags(int flags) {
// Add DEXOPT_FORCE flag to signal installd that it should force compilation
// and discard dexoptanalyzer result.
return flags | DEXOPT_FORCE;
}
}
}

View File

@@ -2133,7 +2133,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mInstaller = installer;
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
mDexManager = new DexManager();
mDexManager = new DexManager(this, mPackageDexOptimizer);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
@@ -7540,6 +7540,15 @@ public class PackageManagerService extends IPackageManager.Stub {
targetCompilerFilter, getOrCreateCompilerPackageStats(p));
}
// Performs dexopt on the used secondary dex files belonging to the given package.
// Returns true if all dex files were process successfully (which could mean either dexopt or
// skip). Returns false if any of the files caused errors.
@Override
public boolean performDexOptSecondary(String packageName, String compilerFilter,
boolean force) {
return mDexManager.dexoptSecondaryDex(packageName, compilerFilter, force);
}
Collection<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
if (p.usesLibraries != null || p.usesOptionalLibraries != null) {
ArrayList<PackageParser.Package> retValue = new ArrayList<>();

View File

@@ -23,7 +23,7 @@ import dalvik.system.DexFile;
/**
* Manage (retrieve) mappings from compilation reason to compilation filter.
*/
class PackageManagerServiceCompilerMapping {
public class PackageManagerServiceCompilerMapping {
// Names for compilation reasons.
static final String REASON_STRINGS[] = {
"first-boot", "boot", "install", "bg-dexopt", "ab-ota", "nsys-library", "shared-apk",

View File

@@ -288,6 +288,7 @@ class PackageManagerShellCommand extends ShellCommand {
String compilerFilter = null;
String compilationReason = null;
String checkProfilesRaw = null;
boolean secondaryDex = false;
String opt;
while ((opt = getNextOption()) != null) {
@@ -315,6 +316,9 @@ class PackageManagerShellCommand extends ShellCommand {
clearProfileData = true;
compilationReason = "install";
break;
case "--secondary-dex":
secondaryDex = true;
break;
default:
pw.println("Error: Unknown option: " + opt);
return 1;
@@ -387,8 +391,11 @@ class PackageManagerShellCommand extends ShellCommand {
mInterface.clearApplicationProfileData(packageName);
}
boolean result = mInterface.performDexOptMode(packageName,
checkProfiles, targetCompilerFilter, forceCompilation);
boolean result = secondaryDex
? mInterface.performDexOptSecondary(packageName,
targetCompilerFilter, forceCompilation)
: mInterface.performDexOptMode(packageName,
checkProfiles, targetCompilerFilter, forceCompilation);
if (!result) {
failedPackages.add(packageName);
}
@@ -1448,6 +1455,7 @@ class PackageManagerShellCommand extends ShellCommand {
}
pw.println(" --reset: restore package to its post-install state");
pw.println(" --check-prof (true | false): look at profiles when doing dexopt?");
pw.println(" --secondary-dex: copmile app secondary dex files");
pw.println(" list features");
pw.println(" Prints all features of the system.");
pw.println(" list instrumentation [-f] [TARGET-PACKAGE]");

View File

@@ -16,13 +16,16 @@
package com.android.server.pm.dex;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageParser;
import android.content.pm.ApplicationInfo;
import android.os.RemoteException;
import android.util.Slog;
import com.android.server.pm.PackageDexOptimizer;
import com.android.server.pm.PackageManagerServiceUtils;
import com.android.server.pm.PackageManagerServiceCompilerMapping;
import java.io.File;
import java.io.IOException;
@@ -32,6 +35,9 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
/**
* This class keeps track of how dex files are used.
* Every time it gets a notification about a dex file being loaded it tracks
@@ -54,15 +60,20 @@ public class DexManager {
// encode and save the dex usage data.
private final PackageDexUsage mPackageDexUsage;
private final IPackageManager mPackageManager;
private final PackageDexOptimizer mPackageDexOptimizer;
// Possible outcomes of a dex search.
private static int DEX_SEARCH_NOT_FOUND = 0; // dex file not found
private static int DEX_SEARCH_FOUND_PRIMARY = 1; // dex file is the primary/base apk
private static int DEX_SEARCH_FOUND_SPLIT = 2; // dex file is a split apk
private static int DEX_SEARCH_FOUND_SECONDARY = 3; // dex file is a secondary dex
public DexManager() {
public DexManager(IPackageManager pms, PackageDexOptimizer pdo) {
mPackageCodeLocationsCache = new HashMap<>();
mPackageDexUsage = new PackageDexUsage();
mPackageManager = pms;
mPackageDexOptimizer = pdo;
}
/**
@@ -199,10 +210,61 @@ public class DexManager {
* Get the package dex usage for the given package name.
* @return the package data or null if there is no data available for this package.
*/
public PackageDexUsage.PackageUseInfo getPackageUseInfo(String packageName) {
public PackageUseInfo getPackageUseInfo(String packageName) {
return mPackageDexUsage.getPackageUseInfo(packageName);
}
/**
* Perform dexopt on the package {@code packageName} secondary dex files.
* @return true if all secondary dex files were processed successfully (compiled or skipped
* because they don't need to be compiled)..
*/
public boolean dexoptSecondaryDex(String packageName, String compilerFilter, boolean force) {
// Select the dex optimizer based on the force parameter.
// Forced compilation is done through ForcedUpdatePackageDexOptimizer which will adjust
// the necessary dexopt flags to make sure that compilation is not skipped. This avoid
// passing the force flag through the multitude of layers.
// Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
// allocate an object here.
PackageDexOptimizer pdo = force
? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
: mPackageDexOptimizer;
PackageUseInfo useInfo = getPackageUseInfo(packageName);
if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "No secondary dex use for package:" + packageName);
}
// Nothing to compile, return true.
return true;
}
boolean success = true;
for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) {
String dexPath = entry.getKey();
DexUseInfo dexUseInfo = entry.getValue();
PackageInfo pkg = null;
try {
pkg = mPackageManager.getPackageInfo(packageName, /*flags*/0,
dexUseInfo.getOwnerUserId());
} catch (RemoteException e) {
throw new AssertionError(e);
}
// It may be that the package gets uninstalled while we try to compile its
// secondary dex files. If that's the case, just ignore.
// Note that we don't break the entire loop because the package might still be
// installed for other users.
if (pkg == null) {
Slog.d(TAG, "Could not find package when compiling secondary dex " + packageName
+ " for user " + dexUseInfo.getOwnerUserId());
// Skip over it, another user might still have the package.
continue;
}
int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath,
dexUseInfo.getLoaderIsas(), compilerFilter, dexUseInfo.isUsedByOtherApps());
success = success && (result != PackageDexOptimizer.DEX_OPT_FAILED);
}
return success;
}
/**
* Retrieves the package which owns the given dexPath.
*/

View File

@@ -73,8 +73,7 @@ public class DexManagerTests {
mInvalidIsa = new TestData("INVALID", "INVALID_ISA", mUser0);
mDoesNotExist = new TestData("DOES.NOT.EXIST", isa, mUser1);
mDexManager = new DexManager();
mDexManager = new DexManager(null, null);
// Foo and Bar are available to user0.
// Only Bar is available to user1;