Frameworks/base: Refactor package manager
Introduce a mapping between dexopt reasons and compiler filters. Use
reasons in package manager and other classes, where possible.
Change PackageDexOptimizer to accept a compilation filter. Adapt for
the split-out profile merging. Pass compilation filter to installd.
Bug: 27689078
Change-Id: I8c0ea6f10fbfdbd096adecc52abfd2466d048fdc
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index dabc652..e3fb161 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -454,8 +454,21 @@
*/
boolean performDexOptIfNeeded(String packageName, String instructionSet);
- boolean performDexOpt(String packageName, String instructionSet, boolean useProfiles,
- boolean extractOnly, boolean force);
+ /**
+ * Ask the package manager to perform a dex-opt for the given reason. The package
+ * manager will map the reason to a compiler filter according to the current system
+ * configuration.
+ */
+ boolean performDexOpt(String packageName, String instructionSet, boolean checkProfiles,
+ int compileReason, boolean force);
+ /**
+ * Ask the package manager to perform a dex-opt with the given compiler filter.
+ *
+ * Note: exposed only for the shell command to allow moving packages explicitly to a
+ * definite state.
+ */
+ boolean performDexOptMode(String packageName, String instructionSet, boolean checkProfiles,
+ String targetCompilerFilter, boolean force);
void forceDexOpt(String packageName);
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index ed4722d..2a9264d 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -21,7 +21,6 @@
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Slog;
-import android.text.TextUtils;
import com.android.internal.util.Preconditions;
@@ -140,14 +139,14 @@
}
public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
- int dexFlags, String volumeUuid, boolean useProfiles) throws InstallerException {
+ int dexFlags, String compilerFilter, String volumeUuid) throws InstallerException {
dexopt(apkPath, uid, "*", instructionSet, dexoptNeeded,
- null /*outputPath*/, dexFlags, volumeUuid, useProfiles);
+ null /*outputPath*/, dexFlags, compilerFilter, volumeUuid);
}
public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
- int dexoptNeeded, String outputPath, int dexFlags, String volumeUuid,
- boolean useProfiles) throws InstallerException {
+ int dexoptNeeded, String outputPath, int dexFlags, String compilerFilter,
+ String volumeUuid) throws InstallerException {
execute("dexopt",
apkPath,
uid,
@@ -156,8 +155,27 @@
dexoptNeeded,
outputPath,
dexFlags,
- volumeUuid,
- useProfiles ? '1' : '0');
+ compilerFilter,
+ volumeUuid);
+ }
+
+ public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
+ String rawReply = executeForResult("merge_profiles", uid, pkgName);
+ if (rawReply == null) {
+ throw new IllegalStateException("Unexpected null reply");
+ }
+ final String res[] = rawReply.split(" ");
+
+ if ((res == null) || (res.length != 2)) {
+ throw new InstallerException("Invalid size result: " + rawReply);
+ }
+
+ // Just as a sanity check. Anything != "true" will be interpreted as false by parseBoolean.
+ if (!res[1].equals("true") && !res[1].equals("false")) {
+ throw new InstallerException("Invalid boolean result: " + rawReply);
+ }
+
+ return Boolean.parseBoolean(res[1]);
}
private boolean connect() {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index b658f87..5980ab6 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -501,12 +501,14 @@
for (String classPathElement : classPathElements) {
// System server is fully AOTed and never profiled
// for profile guided compilation.
+ // TODO: Make this configurable between INTERPRET_ONLY, SPEED, SPACE and EVERYTHING?
final int dexoptNeeded = DexFile.getDexOptNeeded(
- classPathElement, instructionSet, DexFile.COMPILATION_TYPE_FULL);
+ classPathElement, instructionSet, "speed",
+ false /* newProfile */);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
installer.dexopt(classPathElement, Process.SYSTEM_UID, instructionSet,
- dexoptNeeded, 0 /*dexFlags*/, null /*volumeUuid*/,
- false /*useProfiles*/);
+ dexoptNeeded, 0 /*dexFlags*/, "speed",
+ null /*volumeUuid*/);
}
}
} catch (IOException | InstallerException e) {
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 0eacd13..d449ce5 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_BACKGROUND_DEXOPT;
+
import android.app.AlarmManager;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
@@ -51,8 +53,6 @@
final AtomicBoolean mIdleTime = new AtomicBoolean(false);
- private boolean useJitProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
-
public static void schedule(Context context) {
JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo job = new JobInfo.Builder(BACKGROUND_DEXOPT_JOB, sDexoptServiceName)
@@ -93,8 +93,8 @@
// skip previously failing package
continue;
}
- if (!pm.performDexOpt(pkg, /* instruction set */ null, useJitProfiles,
- /* extractOnly */ false, /* force */ false)) {
+ if (!pm.performDexOpt(pkg, /* instruction set */ null, /* checkProfiles */ true,
+ REASON_BACKGROUND_DEXOPT, /* force */ false)) {
// there was a problem running dexopt,
// remember this so we do not keep retrying.
sFailedPackageNames.add(pkg);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 206a143..a1f937ab 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.pm.PackageStats;
import android.os.Build;
-import android.os.storage.StorageManager;
import android.util.Slog;
import com.android.internal.os.InstallerConnection;
@@ -37,17 +36,17 @@
* frameworks/native/cmds/installd/installd.h
* **************************************************************************/
/** Application should be visible to everyone */
- public static final int DEXOPT_PUBLIC = 1 << 1;
+ public static final int DEXOPT_PUBLIC = 1 << 1;
/** Application wants to run in VM safe mode */
- public static final int DEXOPT_SAFEMODE = 1 << 2;
+ public static final int DEXOPT_SAFEMODE = 1 << 2;
/** Application wants to allow debugging of its code */
- public static final int DEXOPT_DEBUGGABLE = 1 << 3;
+ public static final int DEXOPT_DEBUGGABLE = 1 << 3;
/** The system boot has finished */
- public static final int DEXOPT_BOOTCOMPLETE = 1 << 4;
- /** Do not compile, only extract bytecode into an OAT file */
- public static final int DEXOPT_EXTRACTONLY = 1 << 5;
+ 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;
/** This is an OTA update dexopt */
- public static final int DEXOPT_OTA = 1 << 6;
+ public static final int DEXOPT_OTA = 1 << 6;
// NOTE: keep in sync with installd
public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
@@ -137,19 +136,23 @@
}
public void dexopt(String apkPath, int uid, String instructionSet, int dexoptNeeded,
- int dexFlags, String volumeUuid, boolean useProfiles) throws InstallerException {
+ int dexFlags, String compilerFilter, String volumeUuid) throws InstallerException {
assertValidInstructionSet(instructionSet);
mInstaller.dexopt(apkPath, uid, instructionSet, dexoptNeeded, dexFlags,
- volumeUuid, useProfiles);
+ compilerFilter, volumeUuid);
}
public void dexopt(String apkPath, int uid, String pkgName, String instructionSet,
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
- String volumeUuid, boolean useProfiles)
+ String compilerFilter, String volumeUuid)
throws InstallerException {
assertValidInstructionSet(instructionSet);
mInstaller.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded,
- outputPath, dexFlags, volumeUuid, useProfiles);
+ outputPath, dexFlags, compilerFilter, volumeUuid);
+ }
+
+ public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
+ return mInstaller.mergeProfiles(uid, pkgName);
}
public void idmap(String targetApkPath, String overlayApkPath, int uid)
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 67aeed1..03e838b 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -19,11 +19,12 @@
import static com.android.server.pm.Installer.DEXOPT_OTA;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_AB_OTA;
import android.content.Context;
import android.content.pm.IOtaDexopt;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.Package;
import android.os.Environment;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -130,6 +131,7 @@
// TODO: If apps are not installed in the internal /data partition, we should compare
// against that storage's free capacity.
File dataDir = Environment.getDataDirectory();
+ @SuppressWarnings("deprecation")
long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir);
if (lowThreshold == 0) {
throw new IllegalStateException("Invalid low memory threshold");
@@ -142,7 +144,7 @@
}
mPackageDexOptimizer.performDexOpt(nextPackage, null /* ISAs */, false /* useProfiles */,
- false /* extractOnly */);
+ getCompilerFilterForReason(REASON_AB_OTA));
}
private void moveAbArtifacts(Installer installer) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 561682c..5ceb65f 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -20,13 +20,10 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.Package;
import android.os.Environment;
import android.os.PowerManager;
import android.os.UserHandle;
import android.os.WorkSource;
-import android.os.storage.StorageManager;
-import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
@@ -34,18 +31,18 @@
import java.io.File;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.List;
import dalvik.system.DexFile;
import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
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_EXTRACTONLY;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getFullCompilerFilter;
/**
* Helper class for running dexopt command on packages.
@@ -59,8 +56,6 @@
static final int DEX_OPT_DEFERRED = 2;
static final int DEX_OPT_FAILED = -1;
- private static final boolean DEBUG_DEXOPT = PackageManagerService.DEBUG_DEXOPT;
-
private final Installer mInstaller;
private final Object mInstallLock;
@@ -94,8 +89,8 @@
* <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are
* synchronized on {@link #mInstallLock}.
*/
- int performDexOpt(PackageParser.Package pkg, String[] instructionSets, boolean useProfiles,
- boolean extractOnly) {
+ int performDexOpt(PackageParser.Package pkg, String[] instructionSets, boolean checkProfiles,
+ String targetCompilationFilter) {
synchronized (mInstallLock) {
final boolean useLock = mSystemReady;
if (useLock) {
@@ -103,7 +98,8 @@
mDexoptWakeLock.acquire();
}
try {
- return performDexOptLI(pkg, instructionSets, useProfiles, extractOnly);
+ return performDexOptLI(pkg, instructionSets, checkProfiles,
+ targetCompilationFilter);
} finally {
if (useLock) {
mDexoptWakeLock.release();
@@ -128,7 +124,7 @@
}
private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,
- boolean useProfiles, boolean extractOnly) {
+ boolean checkProfiles, String targetCompilerFilter) {
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
@@ -136,36 +132,51 @@
return DEX_OPT_SKIPPED;
}
+ final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
+ final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+
+ boolean isProfileGuidedFilter = DexFile.isProfileGuidedCompilerFilter(targetCompilerFilter);
+ // If any part of the app is used by other apps, we cannot use profile-guided
+ // compilation.
+ // TODO: This needs to be refactored to be also checked when the target mode is
+ // profile-guided.
+ if (isProfileGuidedFilter) {
+ for (String path : paths) {
+ if (isUsedByOtherApps(path)) {
+ checkProfiles = false;
+
+ // TODO: Should we only upgrade to the non-profile-guided version? That is,
+ // given verify-profile, should we move to interpret-only?
+ targetCompilerFilter = getFullCompilerFilter();
+ isProfileGuidedFilter = false;
+
+ break;
+ }
+ }
+ }
+
+ // If we're asked to take profile updates into account, check now.
+ boolean newProfile = false;
+ if (checkProfiles && isProfileGuidedFilter) {
+ // Merge profiles, see if we need to do anything.
+ try {
+ newProfile = mInstaller.mergeProfiles(sharedGid, pkg.packageName);
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to merge profiles", e);
+ }
+ }
+
final boolean vmSafeMode = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
boolean performedDexOpt = false;
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
for (String dexCodeInstructionSet : dexCodeInstructionSets) {
for (String path : paths) {
- if (useProfiles && isUsedByOtherApps(path)) {
- // We cannot use profile guided compilation if the apk was used by another app.
- useProfiles = false;
- }
int dexoptNeeded;
-
try {
- int compilationTypeMask = 0;
- if (extractOnly) {
- // For extract only, any type of compilation is good.
- compilationTypeMask = DexFile.COMPILATION_TYPE_FULL
- | DexFile.COMPILATION_TYPE_PROFILE_GUIDE
- | DexFile.COMPILATION_TYPE_EXTRACT_ONLY;
- } else {
- // Branch taken for profile guide and full compilation.
- // Profile guide compilation should only recompile a previous
- // profile compiled/extract only file and should not be attempted if the
- // apk is already fully compiled. So test against a full compilation type.
- compilationTypeMask = DexFile.COMPILATION_TYPE_FULL;
- }
dexoptNeeded = DexFile.getDexOptNeeded(path,
- dexCodeInstructionSet, compilationTypeMask);
+ dexCodeInstructionSet, targetCompilerFilter, newProfile);
} catch (IOException ioe) {
Slog.w(TAG, "IOException reading apk: " + path, ioe);
return DEX_OPT_FAILED;
@@ -194,20 +205,20 @@
Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
+ pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
+ " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
- + " extractOnly=" + extractOnly + " oatDir = " + oatDir);
- final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ + " target-filter=" + targetCompilerFilter + " oatDir = " + oatDir);
// Profile guide compiled oat files should not be public.
- final boolean isPublic = !pkg.isForwardLocked() && !useProfiles;
+ final boolean isPublic = !pkg.isForwardLocked() && !isProfileGuidedFilter;
+ final int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
final int dexFlags = adjustDexoptFlags(
( isPublic ? DEXOPT_PUBLIC : 0)
| (vmSafeMode ? DEXOPT_SAFEMODE : 0)
| (debuggable ? DEXOPT_DEBUGGABLE : 0)
- | (extractOnly ? DEXOPT_EXTRACTONLY : 0)
+ | profileFlag
| DEXOPT_BOOTCOMPLETE);
try {
mInstaller.dexopt(path, sharedGid, pkg.packageName, dexCodeInstructionSet,
- dexoptNeeded, oatDir, dexFlags, pkg.volumeUuid, useProfiles);
+ dexoptNeeded, oatDir, dexFlags, targetCompilerFilter, pkg.volumeUuid);
performedDexOpt = true;
} catch (InstallerException e) {
Slog.w(TAG, "Failed to dexopt", e);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9335116..b73d8f3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -92,6 +92,13 @@
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getFullCompilerFilter;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_BOOT;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_FORCED_DEXOPT;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_INSTALL;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_NON_SYSTEM_LIBRARY;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_SHARED_APK;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
@@ -1956,6 +1963,9 @@
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
+ // Self-check for initial settings.
+ PackageManagerServiceCompilerMapping.checkProperties();
+
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
@@ -2168,12 +2178,13 @@
// AOT compilation (if needed).
int dexoptNeeded = DexFile.getDexOptNeeded(
lib, dexCodeInstructionSet,
- DexFile.COMPILATION_TYPE_FULL);
+ getCompilerFilterForReason(REASON_SHARED_APK),
+ false /* newProfile */);
if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
mInstaller.dexopt(lib, Process.SYSTEM_UID, dexCodeInstructionSet,
dexoptNeeded, DEXOPT_PUBLIC /*dexFlags*/,
- StorageManager.UUID_PRIVATE_INTERNAL,
- false /*useProfiles*/);
+ getCompilerFilterForReason(REASON_SHARED_APK),
+ StorageManager.UUID_PRIVATE_INTERNAL);
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Library not found: " + lib);
@@ -6928,7 +6939,7 @@
// and would have to be patched (would be SELF_PATCHOAT, which is deprecated).
// Instead, force the extraction in this case.
performDexOpt(pkg.packageName, null /* instructionSet */,
- false /* useProfiles */, true /* extractOnly */, prunedCache);
+ false /* checkProfiles */, REASON_BOOT, prunedCache);
}
}
}
@@ -6947,29 +6958,37 @@
// TODO: this is not used nor needed. Delete it.
@Override
public boolean performDexOptIfNeeded(String packageName, String instructionSet) {
- return performDexOptTraced(packageName, instructionSet, false /* useProfiles */,
- false /* extractOnly */, false /* force */);
+ return performDexOptTraced(packageName, instructionSet, false /* checkProfiles */,
+ getFullCompilerFilter(), false /* force */);
}
@Override
- public boolean performDexOpt(String packageName, String instructionSet, boolean useProfiles,
- boolean extractOnly, boolean force) {
- return performDexOptTraced(packageName, instructionSet, useProfiles, extractOnly, force);
+ public boolean performDexOpt(String packageName, String instructionSet,
+ boolean checkProfiles, int compileReason, boolean force) {
+ return performDexOptTraced(packageName, instructionSet, checkProfiles,
+ getCompilerFilterForReason(compileReason), force);
+ }
+
+ @Override
+ public boolean performDexOptMode(String packageName, String instructionSet,
+ boolean checkProfiles, String targetCompilerFilter, boolean force) {
+ return performDexOptTraced(packageName, instructionSet, checkProfiles,
+ targetCompilerFilter, force);
}
private boolean performDexOptTraced(String packageName, String instructionSet,
- boolean useProfiles, boolean extractOnly, boolean force) {
+ boolean checkProfiles, String targetCompilerFilter, boolean force) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
try {
- return performDexOptInternal(packageName, instructionSet, useProfiles, extractOnly,
- force);
+ return performDexOptInternal(packageName, instructionSet, checkProfiles,
+ targetCompilerFilter, force);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private boolean performDexOptInternal(String packageName, String instructionSet,
- boolean useProfiles, boolean extractOnly, boolean force) {
+ boolean checkProfiles, String targetCompilerFilter, boolean force) {
PackageParser.Package p;
final String targetInstructionSet;
synchronized (mPackages) {
@@ -6987,7 +7006,7 @@
synchronized (mInstallLock) {
final String[] instructionSets = new String[] { targetInstructionSet };
int result = performDexOptInternalWithDependenciesLI(p, instructionSets,
- useProfiles, extractOnly, force);
+ checkProfiles, targetCompilerFilter, force);
return result == PackageDexOptimizer.DEX_OPT_PERFORMED;
}
} finally {
@@ -7008,7 +7027,8 @@
}
private int performDexOptInternalWithDependenciesLI(PackageParser.Package p,
- String instructionSets[], boolean useProfiles, boolean extractOnly, boolean force) {
+ String instructionSets[], boolean checkProfiles, String targetCompilerFilter,
+ boolean force) {
// Select the dex optimizer based on the force parameter.
// Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
// allocate an object here.
@@ -7022,13 +7042,13 @@
if (!deps.isEmpty()) {
for (PackageParser.Package depPackage : deps) {
// TODO: Analyze and investigate if we (should) profile libraries.
- // Currently this will do a full compilation of the library.
- pdo.performDexOpt(depPackage, instructionSets, false /* useProfiles */,
- false /* extractOnly */);
+ // Currently this will do a full compilation of the library by default.
+ pdo.performDexOpt(depPackage, instructionSets, false /* checkProfiles */,
+ getCompilerFilterForReason(REASON_NON_SYSTEM_LIBRARY));
}
}
- return pdo.performDexOpt(p, instructionSets, useProfiles, extractOnly);
+ return pdo.performDexOpt(p, instructionSets, checkProfiles, targetCompilerFilter);
}
Collection<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
@@ -7106,7 +7126,8 @@
// Whoever is calling forceDexOpt wants a fully compiled package.
// Don't use profiles since that may cause compilation to be skipped.
final int res = performDexOptInternalWithDependenciesLI(pkg, instructionSets,
- false /* useProfiles */, false /* extractOnly */, true /* force */);
+ false /* checkProfiles */, getCompilerFilterForReason(REASON_FORCED_DEXOPT),
+ true /* force */);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -13973,7 +13994,7 @@
// Do not run PackageDexOptimizer through the local performDexOpt
// method because `pkg` is not in `mPackages` yet.
int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instructionSets */,
- false /* useProfiles */, true /* extractOnly */);
+ false /* checkProfiles */, getCompilerFilterForReason(REASON_INSTALL));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
String msg = "Extracking package failed for " + pkgName;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
new file mode 100644
index 0000000..b53f8d1
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.os.SystemProperties;
+
+import dalvik.system.DexFile;
+
+/**
+ * Manage (retrieve) mappings from compilation reason to compilation filter.
+ */
+class PackageManagerServiceCompilerMapping {
+ // Compilation reasons.
+ public static final int REASON_BOOT = 0;
+ public static final int REASON_INSTALL = 1;
+ public static final int REASON_BACKGROUND_DEXOPT = 2;
+ public static final int REASON_AB_OTA = 3;
+ public static final int REASON_NON_SYSTEM_LIBRARY = 4;
+ public static final int REASON_SHARED_APK = 5;
+ public static final int REASON_FORCED_DEXOPT = 6;
+
+ private static final int REASON_LAST = REASON_FORCED_DEXOPT;
+
+ // Names for compilation reasons.
+ static final String REASON_STRINGS[] = {
+ "boot", "install", "bg-dexopt", "ab-ota", "nsys-library", "shared-apk", "forced-dexopt"
+ };
+
+ // Static block to ensure the strings array is of the right length.
+ static {
+ if (REASON_LAST + 1 != REASON_STRINGS.length) {
+ throw new IllegalStateException("REASON_STRINGS not correct");
+ }
+ }
+
+ private static String getSystemPropertyName(int reason) {
+ if (reason < 0 || reason >= REASON_STRINGS.length) {
+ throw new IllegalArgumentException("reason " + reason + " invalid");
+ }
+
+ return "pm.dexopt." + REASON_STRINGS[reason];
+ }
+
+ // Load the property for the given reason and check for validity. This will throw an
+ // exception in case the reason or value are invalid.
+ private static String getAndCheckValidity(int reason) {
+ String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
+ if (sysPropValue == null || sysPropValue.isEmpty() ||
+ !DexFile.isValidCompilerFilter(sysPropValue)) {
+ throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
+ + "(reason " + REASON_STRINGS[reason] + ")");
+ }
+
+ // Ensure that some reasons are not mapped to profile-guided filters.
+ switch (reason) {
+ case REASON_SHARED_APK:
+ case REASON_FORCED_DEXOPT:
+ if (DexFile.isProfileGuidedCompilerFilter(sysPropValue)) {
+ throw new IllegalStateException("\"" + sysPropValue + "\" is profile-guided, "
+ + "but not allowed for " + REASON_STRINGS[reason]);
+ }
+ break;
+ }
+
+ return sysPropValue;
+ }
+
+ // Check that the properties are set and valid.
+ // Note: this is done in a separate method so this class can be statically initialized.
+ static void checkProperties() {
+ // We're gonna check all properties and collect the exceptions, so we can give a general
+ // overview. Store the exceptions here.
+ RuntimeException toThrow = null;
+
+ for (int reason = 0; reason <= REASON_LAST; reason++) {
+ try {
+ // Check that the system property name is legal.
+ String sysPropName = getSystemPropertyName(reason);
+ if (sysPropName == null ||
+ sysPropName.isEmpty() ||
+ sysPropName.length() > SystemProperties.PROP_NAME_MAX) {
+ throw new IllegalStateException("Reason system property name \"" +
+ sysPropName +"\" for reason " + REASON_STRINGS[reason]);
+ }
+
+ // Check validity, ignore result.
+ getAndCheckValidity(reason);
+ } catch (Exception exc) {
+ if (toThrow == null) {
+ toThrow = new IllegalStateException("PMS compiler filter settings are bad.");
+ }
+ toThrow.addSuppressed(exc);
+ }
+ }
+
+ if (toThrow != null) {
+ throw toThrow;
+ }
+ }
+
+ public static String getCompilerFilterForReason(int reason) {
+ return getAndCheckValidity(reason);
+ }
+
+ /**
+ * Return the compiler filter for "full" compilation.
+ *
+ * We derive that from the traditional "dalvik.vm.dex2oat-filter" property and just make
+ * sure this isn't profile-guided. Returns "speed" in case of invalid (or missing) values.
+ */
+ public static String getFullCompilerFilter() {
+ String value = SystemProperties.get("dalvik.vm.dex2oat-filter");
+ if (value == null || value.isEmpty()) {
+ return "speed";
+ }
+
+ if (!DexFile.isValidCompilerFilter(value) ||
+ DexFile.isProfileGuidedCompilerFilter(value)) {
+ return "speed";
+ }
+
+ return value;
+ }
+
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 319fc37..7f626b2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -50,6 +50,8 @@
import android.util.PrintWriterPrinter;
import com.android.internal.util.SizedInputStream;
+import dalvik.system.DexFile;
+
import libcore.io.IoUtils;
import java.io.File;
@@ -249,11 +251,38 @@
private int runCompile() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
boolean useJitProfiles = false;
- boolean extractOnly = false;
boolean forceCompilation = false;
boolean allPackages = false;
boolean clearProfileData = false;
- String compilationMode = "default";
+ String compilerFilter = null;
+ String compilationReason = null;
+
+ if (peekNextArg() == null) {
+ // No arguments, show help.
+ pw.println("Usage: cmd package compile [-c] [-f] [--reset] [-m mode] " +
+ "[-r reason] [-a|pkg]");
+ pw.println();
+ pw.println(" -c Clear profile data");
+ pw.println(" -f Force compilation");
+ pw.println(" --reset Reset package");
+ pw.println(" -m mode Compilation mode, one of the dex2oat compiler filters");
+ pw.println(" verify-none");
+ pw.println(" verify-at-runtime");
+ pw.println(" verify-profile");
+ pw.println(" interpret-only");
+ pw.println(" space-profile");
+ pw.println(" space");
+ pw.println(" speed-profile");
+ pw.println(" speed");
+ pw.println(" everything");
+ pw.println(" -r reason Compiler reason, one of the package manager reasons");
+ for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
+ pw.println(" " +
+ PackageManagerServiceCompilerMapping.REASON_STRINGS[i]);
+ }
+ pw.println(" -a Apply to all packages");
+ return 1;
+ }
String opt;
while ((opt = getNextOption()) != null) {
@@ -268,12 +297,15 @@
forceCompilation = true;
break;
case "-m":
- compilationMode = getNextArgRequired();
+ compilerFilter = getNextArgRequired();
+ break;
+ case "-r":
+ compilationReason = getNextArgRequired();
break;
case "--reset":
forceCompilation = true;
clearProfileData = true;
- compilationMode = "extract";
+ compilerFilter = "reset";
break;
default:
pw.println("Error: Unknown option: " + opt);
@@ -281,27 +313,55 @@
}
}
- switch (compilationMode) {
- case "default":
- useJitProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
- extractOnly = false;
- break;
- case "full":
- useJitProfiles = false;
- extractOnly = false;
- break;
- case "profile":
- useJitProfiles = true;
- extractOnly = false;
- break;
- case "extract":
- useJitProfiles = false;
- extractOnly = true;
- break;
- default:
- pw.println("Error: Unknown compilation mode: " + compilationMode);
- return 1;
+ if (compilerFilter != null && compilationReason != null) {
+ pw.println("Cannot use compilation filter (\"-m\") and compilation reason (\"-r\") " +
+ "at the same time");
+ return 1;
}
+ if (compilerFilter == null && compilationReason == null) {
+ pw.println("Cannot run without any of compilation filter (\"-m\") and compilation " +
+ "reason (\"-r\") at the same time");
+ return 1;
+ }
+
+ String targetCompilerFilter;
+ if (compilerFilter != null) {
+ // Specially recognize default and reset. Otherwise, only accept valid modes.
+ if ("default".equals(compilerFilter)) {
+ // Use the default mode for background dexopt.
+ targetCompilerFilter =
+ PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
+ PackageManagerServiceCompilerMapping.REASON_BACKGROUND_DEXOPT);
+ } else if ("reset".equals(compilerFilter)) {
+ // Use the default mode for install.
+ targetCompilerFilter =
+ PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
+ PackageManagerServiceCompilerMapping.REASON_INSTALL);
+ } else {
+ if (!DexFile.isValidCompilerFilter(compilerFilter)) {
+ pw.println("Error: \"" + compilerFilter +
+ "\" is not a valid compilation filter.");
+ return 1;
+ }
+ targetCompilerFilter = compilerFilter;
+ }
+ } else {
+ int reason = -1;
+ for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
+ if (PackageManagerServiceCompilerMapping.REASON_STRINGS[i].equals(
+ compilationReason)) {
+ reason = i;
+ break;
+ }
+ }
+ if (reason == -1) {
+ pw.println("Error: Unknown compilation reason: " + compilationReason);
+ return 1;
+ }
+ targetCompilerFilter =
+ PackageManagerServiceCompilerMapping.getCompilerFilterForReason(reason);
+ }
+
List<String> packageNames = null;
if (allPackages) {
@@ -321,8 +381,8 @@
mInterface.clearApplicationProfileData(packageName);
}
- boolean result = mInterface.performDexOpt(packageName, null /* instructionSet */,
- useJitProfiles, extractOnly, forceCompilation);
+ boolean result = mInterface.performDexOptMode(packageName, null /* instructionSet */,
+ useJitProfiles, targetCompilerFilter, forceCompilation);
if (!result) {
failedPackages.add(packageName);
}