diff options
3 files changed, 136 insertions, 10 deletions
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index e7d0c41c0fea..65b7cf3eabd1 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -51,6 +51,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; +import android.os.storage.StorageManager; import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -79,7 +80,7 @@ import java.util.Map; * Helper class for running dexopt command on packages. */ public class PackageDexOptimizer { - private static final String TAG = "PackageManager.DexOptimizer"; + private static final String TAG = "PackageDexOptimizer"; static final String OAT_DIR_NAME = "oat"; // TODO b/19550105 Remove error codes and use exceptions public static final int DEX_OPT_SKIPPED = 0; @@ -307,6 +308,55 @@ public class PackageDexOptimizer { } } + /** + * Perform dexopt (if needed) on a system server code path). + */ + public int dexoptSystemServerPath( + String dexPath, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) { + int dexoptFlags = DEXOPT_PUBLIC + | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0) + | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0); + + int result = DEX_OPT_SKIPPED; + for (String isa : dexUseInfo.getLoaderIsas()) { + int dexoptNeeded = getDexoptNeeded( + dexPath, + isa, + options.getCompilerFilter(), + dexUseInfo.getClassLoaderContext(), + /* newProfile= */false, + /* downgrade= */ false); + + if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) { + continue; + } + try { + mInstaller.dexopt( + dexPath, + android.os.Process.SYSTEM_UID, + /* packageName= */ "android", + isa, + dexoptNeeded, + /* oatDir= */ null, + dexoptFlags, + options.getCompilerFilter(), + StorageManager.UUID_PRIVATE_INTERNAL, + dexUseInfo.getClassLoaderContext(), + /* seInfo= */ null, + /* downgrade= */ false , + /* targetSdk= */ 0, + /* profileName */ null, + /* dexMetadataPath */ null, + getReasonName(options.getCompilationReason())); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to dexopt", e); + return DEX_OPT_FAILED; + } + result = DEX_OPT_PERFORMED; + } + return result; + } + private String getAugmentedReasonName(int compilationReason, boolean useDexMetadata) { String annotation = useDexMetadata ? ArtManagerService.DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION : ""; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f693555d9761..9c41e6d5c6a3 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -9855,6 +9855,11 @@ public class PackageManagerService extends IPackageManager.Stub private int performDexOptInternalWithDependenciesLI(AndroidPackage p, @NonNull PackageSetting pkgSetting, DexoptOptions options) { + // System server gets a special path. + if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) { + return mDexManager.dexoptSystemServer(options); + } + // 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. diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index ebdf85691e58..eb7057dd2994 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -49,6 +49,8 @@ import dalvik.system.VMRuntime; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -437,15 +439,7 @@ public class DexManager { * because they don't need to be compiled).. */ public boolean dexoptSecondaryDex(DexoptOptions options) { - // 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 = options.isForce() - ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer) - : mPackageDexOptimizer; + PackageDexOptimizer pdo = getPackageDexOptimizer(options); String packageName = options.getPackageName(); PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName); if (useInfo.getDexUseInfoMap().isEmpty()) { @@ -486,6 +480,83 @@ public class DexManager { } /** + * Performs dexopt on system server dex files. + * + * <p>Verfifies that the package name is {@link PackageManagerService#PLATFORM_PACKAGE_NAME}. + * + * @return + * <p>PackageDexOptimizer.DEX_OPT_SKIPPED if dexopt was skipped because no system server + * files were recorded or if no dexopt was needed. + * <p>PackageDexOptimizer.DEX_OPT_FAILED if any dexopt operation failed. + * <p>PackageDexOptimizer.DEX_OPT_PERFORMED if all dexopt operations succeeded. + */ + public int dexoptSystemServer(DexoptOptions options) { + if (!PLATFORM_PACKAGE_NAME.equals(options.getPackageName())) { + Slog.wtf(TAG, "Non system server package used when trying to dexopt system server:" + + options.getPackageName()); + return PackageDexOptimizer.DEX_OPT_FAILED; + } + + PackageDexOptimizer pdo = getPackageDexOptimizer(options); + String packageName = options.getPackageName(); + PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName); + if (useInfo.getDexUseInfoMap().isEmpty()) { + if (DEBUG) { + Slog.d(TAG, "No dex files recorded for system server"); + } + // Nothing to compile, return true. + return PackageDexOptimizer.DEX_OPT_SKIPPED; + } + + boolean usageUpdated = false; + int result = PackageDexOptimizer.DEX_OPT_SKIPPED; + for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) { + String dexPath = entry.getKey(); + DexUseInfo dexUseInfo = entry.getValue(); + if (!Files.exists(Paths.get(dexPath))) { + if (DEBUG) { + Slog.w(TAG, "A dex file previously loaded by System Server does not exist " + + " anymore: " + dexPath); + } + usageUpdated = mPackageDexUsage.removeDexFile( + packageName, dexPath, dexUseInfo.getOwnerUserId()) || usageUpdated; + continue; + } + + int newResult = pdo.dexoptSystemServerPath(dexPath, dexUseInfo, options); + + // The end result is: + // - FAILED if any path failed, + // - PERFORMED if at least one path needed compilation, + // - SKIPPED when all paths are up to date + if ((result != PackageDexOptimizer.DEX_OPT_FAILED) + && (newResult != PackageDexOptimizer.DEX_OPT_SKIPPED)) { + result = newResult; + } + } + + if (usageUpdated) { + mPackageDexUsage.maybeWriteAsync(); + } + + return result; + } + + /** + * 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. + */ + private PackageDexOptimizer getPackageDexOptimizer(DexoptOptions options) { + return options.isForce() + ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer) + : mPackageDexOptimizer; + } + + /** * Reconcile the information we have about the secondary dex files belonging to * {@code packagName} and the actual dex files. For all dex files that were * deleted, update the internal records and delete any generated oat files. |