Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4fdf8271 authored by Calin Juravle's avatar Calin Juravle
Browse files

Enable dexopt for system server dex files

Add logic for dexopt-ing system server dex files. The files
are managed by the DexManager and need special dexopt
arguments.

As such, they have a dedicated dexopt path, that can be
initiated from DexManager. (The arguments are what we currently
use in ZygoteInit#performSystemServerDexOpt)

Test: adb shell cmd package dexopt android
      presubmits
Bug: 148774920
Change-Id: If0c0457f8045e8f475815136fb5ae2f340eb6661
parent be3b2304
Loading
Loading
Loading
Loading
+51 −1
Original line number Diff line number Diff line
@@ -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 : "";
+5 −0
Original line number Diff line number Diff line
@@ -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.
+80 −9
Original line number Diff line number Diff line
@@ -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()) {
@@ -485,6 +479,83 @@ public class DexManager {
        return success;
    }

    /**
     * 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