From bdd30d86ef98456161069d11481b2ccd25a11b4e Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Sun, 20 Mar 2016 11:32:11 -0700 Subject: 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 --- core/java/android/content/pm/IPackageManager.aidl | 17 ++- .../android/internal/os/InstallerConnection.java | 32 +++-- core/java/com/android/internal/os/ZygoteInit.java | 8 +- .../android/server/pm/BackgroundDexOptService.java | 8 +- .../core/java/com/android/server/pm/Installer.java | 27 ++-- .../com/android/server/pm/OtaDexoptService.java | 6 +- .../com/android/server/pm/PackageDexOptimizer.java | 83 ++++++------ .../android/server/pm/PackageManagerService.java | 63 ++++++---- .../pm/PackageManagerServiceCompilerMapping.java | 139 +++++++++++++++++++++ .../server/pm/PackageManagerShellCommand.java | 110 ++++++++++++---- 10 files changed, 381 insertions(+), 112 deletions(-) create mode 100644 services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index dabc6524b20f..e3fb161cf0ab 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -454,8 +454,21 @@ interface IPackageManager { */ 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 ed4722d985af..2a9264dccadf 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.net.LocalSocketAddress; 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 class InstallerConnection { } 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 @@ public class InstallerConnection { 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 b658f87a9689..5980ab69d7a4 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -501,12 +501,14 @@ public class ZygoteInit { 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 0eacd13e1a80..d449ce55bfba 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 @@ public class BackgroundDexOptService extends JobService { 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 @@ public class BackgroundDexOptService extends JobService { // 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 206a1438bd61..a1f937aba519 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.annotation.Nullable; 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 @@ public final class Installer extends SystemService { * 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 final class Installer extends SystemService { } 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 67aeed116df5..03e838b7e148 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 @@ package com.android.server.pm; 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 @@ public class OtaDexoptService extends IOtaDexopt.Stub { // 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 @@ public class OtaDexoptService extends IOtaDexopt.Stub { } 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 561682c5c478..5ceb65fb81fa 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.annotation.Nullable; 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 com.android.internal.os.InstallerConnection.InstallerException; 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 @@ class PackageDexOptimizer { 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 @@ class PackageDexOptimizer { *

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 @@ class PackageDexOptimizer { mDexoptWakeLock.acquire(); } try { - return performDexOptLI(pkg, instructionSets, useProfiles, extractOnly); + return performDexOptLI(pkg, instructionSets, checkProfiles, + targetCompilationFilter); } finally { if (useLock) { mDexoptWakeLock.release(); @@ -128,7 +124,7 @@ class PackageDexOptimizer { } 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 @@ class PackageDexOptimizer { return DEX_OPT_SKIPPED; } + final List 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 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 @@ class PackageDexOptimizer { 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 9335116d4451..b73d8f3b2705 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.getDexCodeInstructionSet; 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 class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 checkProfiles, int compileReason, boolean force) { + return performDexOptTraced(packageName, instructionSet, checkProfiles, + getCompilerFilterForReason(compileReason), force); } @Override - public boolean performDexOpt(String packageName, String instructionSet, boolean useProfiles, - boolean extractOnly, boolean force) { - return performDexOptTraced(packageName, instructionSet, useProfiles, extractOnly, force); + 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { } 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 findSharedNonSystemLibraries(PackageParser.Package p) { @@ -7106,7 +7126,8 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 000000000000..b53f8d143d96 --- /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 319fc3741828..7f626b2f0303 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.text.TextUtils; 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 @@ class PackageManagerShellCommand extends ShellCommand { 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,28 +313,56 @@ class PackageManagerShellCommand extends ShellCommand { } } - 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); + 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 packageNames = null; if (allPackages) { packageNames = mInterface.getAllPackages(); @@ -321,8 +381,8 @@ class PackageManagerShellCommand extends ShellCommand { 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); } -- cgit v1.2.3-59-g8ed1b