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

Commit cd4d015a authored by Martin Stjernholm's avatar Martin Stjernholm Committed by Android (Google) Code Review
Browse files

Merge "Optionally use ART Service for optimizing packages."

parents a0d351ca 901d13ba
Loading
Loading
Loading
Loading
+159 −8
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.pm;

import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;

import static com.android.server.LocalManagerRegistry.ManagerNotFoundException;
import static com.android.server.pm.ApexManager.ActiveApexInfo;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
@@ -34,6 +35,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.REMOVE_IF_NULL_PK

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.AppGlobals;
@@ -56,9 +58,16 @@ import android.util.Slog;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.server.LocalManagerRegistry;
import com.android.server.art.ArtManagerLocal;
import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.OptimizeParams;
import com.android.server.art.model.OptimizeResult;
import com.android.server.pm.PackageDexOptimizer.DexOptResult;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;

import dalvik.system.DexFile;
@@ -72,11 +81,15 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

final class DexOptHelper {
/**
 * Helper class for dex optimization operations in PackageManagerService.
 */
public final class DexOptHelper {
    private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;

    private final PackageManagerService mPm;
@@ -405,11 +418,12 @@ final class DexOptHelper {
     * {@link PackageDexOptimizer#DEX_OPT_CANCELLED}
     * {@link PackageDexOptimizer#DEX_OPT_FAILED}
     */
    @PackageDexOptimizer.DexOptResult
    @DexOptResult
    /* package */ int performDexOptWithStatus(DexoptOptions options) {
        return performDexOptTraced(options);
    }

    @DexOptResult
    private int performDexOptTraced(DexoptOptions options) {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
        try {
@@ -421,7 +435,13 @@ final class DexOptHelper {

    // Run dexopt on a given package. Returns true if dexopt did not fail, i.e.
    // if the package can now be considered up to date for the given filter.
    @DexOptResult
    private int performDexOptInternal(DexoptOptions options) {
        Optional<Integer> artSrvRes = performDexOptWithArtService(options);
        if (artSrvRes.isPresent()) {
            return artSrvRes.get();
        }

        AndroidPackage p;
        PackageSetting pkgSetting;
        synchronized (mPm.mLock) {
@@ -446,8 +466,74 @@ final class DexOptHelper {
        }
    }

    private int performDexOptInternalWithDependenciesLI(AndroidPackage p,
            @NonNull PackageStateInternal pkgSetting, DexoptOptions options) {
    /**
     * Performs dexopt on the given package using ART Service.
     *
     * @return a {@link DexOptResult}, or empty if the request isn't supported so that it is
     *     necessary to fall back to the legacy code paths.
     */
    private Optional<Integer> performDexOptWithArtService(DexoptOptions options) {
        ArtManagerLocal artManager = getArtManagerLocal();
        if (artManager == null) {
            return Optional.empty();
        }

        try (PackageManagerLocal.FilteredSnapshot snapshot =
                        getPackageManagerLocal().withFilteredSnapshot()) {
            PackageState ops = snapshot.getPackageState(options.getPackageName());
            if (ops == null) {
                return Optional.of(PackageDexOptimizer.DEX_OPT_FAILED);
            }
            AndroidPackage oap = ops.getAndroidPackage();
            if (oap == null) {
                return Optional.of(PackageDexOptimizer.DEX_OPT_FAILED);
            }
            if (oap.isApex()) {
                return Optional.of(PackageDexOptimizer.DEX_OPT_SKIPPED);
            }

            // TODO(b/245301593): Delete the conditional when ART Service supports
            // FLAG_SHOULD_INCLUDE_DEPENDENCIES and we can just set it unconditionally.
            /*@OptimizeFlags*/ int extraFlags = ops.getUsesLibraries().isEmpty()
                    ? 0
                    : ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES;

            OptimizeParams params = options.convertToOptimizeParams(extraFlags);
            if (params == null) {
                return Optional.empty();
            }

            // TODO(b/251903639): Either remove controlDexOptBlocking, or don't ignore it here.
            OptimizeResult result;
            try {
                result = artManager.optimizePackage(snapshot, options.getPackageName(), params);
            } catch (UnsupportedOperationException e) {
                reportArtManagerFallback(options.getPackageName(), e.toString());
                return Optional.empty();
            }

            // TODO(b/251903639): Move this to ArtManagerLocal.addOptimizePackageDoneCallback when
            // it is implemented.
            for (OptimizeResult.PackageOptimizeResult pkgRes : result.getPackageOptimizeResults()) {
                PackageState ps = snapshot.getPackageState(pkgRes.getPackageName());
                AndroidPackage ap = ps != null ? ps.getAndroidPackage() : null;
                if (ap != null) {
                    CompilerStats.PackageStats stats = mPm.getOrCreateCompilerPackageStats(ap);
                    for (OptimizeResult.DexContainerFileOptimizeResult dexRes :
                            pkgRes.getDexContainerFileOptimizeResults()) {
                        stats.setCompileTime(
                                dexRes.getDexContainerFile(), dexRes.getDex2oatWallTimeMillis());
                    }
                }
            }

            return Optional.of(convertToDexOptResult(result));
        }
    }

    @DexOptResult
    private int performDexOptInternalWithDependenciesLI(
            AndroidPackage p, @NonNull PackageStateInternal pkgSetting, DexoptOptions options) {
        // System server gets a special path.
        if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) {
            return mPm.getDexManager().dexoptSystemServer(options);
@@ -514,10 +600,20 @@ final class DexOptHelper {

        // Whoever is calling forceDexOpt wants a compiled package.
        // Don't use profiles since that may cause compilation to be skipped.
        final int res = performDexOptInternalWithDependenciesLI(pkg, packageState,
                new DexoptOptions(packageName, REASON_CMDLINE,
        DexoptOptions options = new DexoptOptions(packageName, REASON_CMDLINE,
                getDefaultCompilerFilter(), null /* splitName */,
                        DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE));
                DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE);

        // performDexOptWithArtService ignores the snapshot and takes its own, so it can race with
        // the package checks above, but at worst the effect is only a bit less friendly error
        // below.
        Optional<Integer> artSrvRes = performDexOptWithArtService(options);
        int res;
        if (artSrvRes.isPresent()) {
            res = artSrvRes.get();
        } else {
            res = performDexOptInternalWithDependenciesLI(pkg, packageState, options);
        }

        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -800,4 +896,59 @@ final class DexOptHelper {
        }
        return false;
    }

    private @NonNull PackageManagerLocal getPackageManagerLocal() {
        try {
            return LocalManagerRegistry.getManagerOrThrow(PackageManagerLocal.class);
        } catch (ManagerNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Called whenever we need to fall back from ART Service to the legacy dexopt code.
     */
    public static void reportArtManagerFallback(String packageName, String reason) {
        // STOPSHIP(b/251903639): Minimize these calls to avoid platform getting shipped with code
        // paths that will always bypass ART Service.
        Slog.i(TAG, "Falling back to old PackageManager dexopt for " + packageName + ": " + reason);
    }

    /**
     * Returns {@link ArtManagerLocal} if one is found and should be used for package optimization.
     */
    private @Nullable ArtManagerLocal getArtManagerLocal() {
        if (!"true".equals(SystemProperties.get("dalvik.vm.useartservice", ""))) {
            return null;
        }
        try {
            return LocalManagerRegistry.getManagerOrThrow(ArtManagerLocal.class);
        } catch (ManagerNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Converts an ART Service {@link OptimizeResult} to {@link DexOptResult}.
     *
     * For interfacing {@link ArtManagerLocal} with legacy dex optimization code in PackageManager.
     */
    @DexOptResult
    private static int convertToDexOptResult(OptimizeResult result) {
        /*@OptimizeStatus*/ int status = result.getFinalStatus();
        switch (status) {
            case OptimizeResult.OPTIMIZE_SKIPPED:
                return PackageDexOptimizer.DEX_OPT_SKIPPED;
            case OptimizeResult.OPTIMIZE_FAILED:
                return PackageDexOptimizer.DEX_OPT_FAILED;
            case OptimizeResult.OPTIMIZE_PERFORMED:
                return PackageDexOptimizer.DEX_OPT_PERFORMED;
            case OptimizeResult.OPTIMIZE_CANCELLED:
                return PackageDexOptimizer.DEX_OPT_CANCELLED;
            default:
                throw new IllegalArgumentException("OptimizeResult for "
                        + result.getPackageOptimizeResults().get(0).getPackageName()
                        + " has unsupported status " + status);
        }
    }
}
+139 −0
Original line number Diff line number Diff line
@@ -18,6 +18,16 @@ package com.android.server.pm.dex;

import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;

import android.annotation.Nullable;

import com.android.server.art.ReasonMapping;
import com.android.server.art.model.ArtFlags;
import com.android.server.art.model.OptimizeParams;
import com.android.server.pm.DexOptHelper;
import com.android.server.pm.PackageManagerService;

import dalvik.system.DexFile;

/**
 * Options used for dexopt invocations.
 */
@@ -189,4 +199,133 @@ public final class DexoptOptions {
                mSplitName,
                mFlags);
    }

    /**
     * Returns an {@link OptimizeParams} instance corresponding to this object, for use with
     * {@link com.android.server.art.ArtManagerLocal}.
     *
     * @param extraFlags extra {@link ArtFlags#OptimizeFlags} to set in the returned
     *     {@code OptimizeParams} beyond those converted from this object
     * @return null if the settings cannot be accurately represented, and hence the old
     *     PackageManager/installd code paths need to be used.
     */
    public @Nullable OptimizeParams convertToOptimizeParams(/*@OptimizeFlags*/ int extraFlags) {
        if (mSplitName != null) {
            DexOptHelper.reportArtManagerFallback(
                    mPackageName, "Request to optimize only split " + mSplitName);
            return null;
        }

        /*@OptimizeFlags*/ int flags = extraFlags;
        if ((mFlags & DEXOPT_CHECK_FOR_PROFILES_UPDATES) == 0
                && DexFile.isProfileGuidedCompilerFilter(mCompilerFilter)) {
            // ART Service doesn't support bypassing this, so not setting this flag is not
            // supported.
            DexOptHelper.reportArtManagerFallback(mPackageName,
                    "DEXOPT_CHECK_FOR_PROFILES_UPDATES not set with profile compiler filter");
            return null;
        }
        if ((mFlags & DEXOPT_FORCE) != 0) {
            flags |= ArtFlags.FLAG_FORCE;
        }
        if ((mFlags & DEXOPT_ONLY_SECONDARY_DEX) != 0) {
            flags |= ArtFlags.FLAG_FOR_SECONDARY_DEX;
        } else {
            flags |= ArtFlags.FLAG_FOR_PRIMARY_DEX;
        }
        if ((mFlags & DEXOPT_DOWNGRADE) != 0) {
            flags |= ArtFlags.FLAG_SHOULD_DOWNGRADE;
        }
        if ((mFlags & DEXOPT_INSTALL_WITH_DEX_METADATA_FILE) == 0) {
            // ART Service cannot be instructed to ignore a DM file if present, so not setting this
            // flag is not supported.
            DexOptHelper.reportArtManagerFallback(
                    mPackageName, "DEXOPT_INSTALL_WITH_DEX_METADATA_FILE not set");
            return null;
        }

        /*@PriorityClassApi*/ int priority;
        // Replicates logic in RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags in installd.
        if ((mFlags & DEXOPT_BOOT_COMPLETE) != 0) {
            if ((mFlags & DEXOPT_FOR_RESTORE) != 0) {
                priority = ArtFlags.PRIORITY_INTERACTIVE_FAST;
            } else {
                // TODO(b/251903639): Repurpose DEXOPT_IDLE_BACKGROUND_JOB to choose new
                // dalvik.vm.background-dex2oat-* properties.
                priority = ArtFlags.PRIORITY_INTERACTIVE;
            }
        } else {
            priority = ArtFlags.PRIORITY_BOOT;
        }

        // The following flags in mFlags are ignored:
        //
        // -  DEXOPT_AS_SHARED_LIBRARY: It's implicit with ART Service since it always looks at
        //    <uses-library> rather than actual dependencies.
        //
        //    We don't require it to be set either. It's safe when switching between old and new
        //    code paths since the only effect is that some packages may be unnecessarily compiled
        //    without user profiles.
        //
        // -  DEXOPT_IDLE_BACKGROUND_JOB: Its only effect is to allow the debug variant dex2oatd to
        //    be used, but ART Service never uses that (cf. Artd::GetDex2Oat in artd.cc).

        String reason;
        switch (mCompilationReason) {
            case PackageManagerService.REASON_FIRST_BOOT:
                reason = ReasonMapping.REASON_FIRST_BOOT;
                break;
            case PackageManagerService.REASON_BOOT_AFTER_OTA:
                reason = ReasonMapping.REASON_BOOT_AFTER_OTA;
                break;
            case PackageManagerService.REASON_POST_BOOT:
                // This reason will go away with the legacy dexopt code.
                DexOptHelper.reportArtManagerFallback(
                        mPackageName, "Unsupported compilation reason REASON_POST_BOOT");
                return null;
            case PackageManagerService.REASON_INSTALL:
                reason = ReasonMapping.REASON_INSTALL;
                break;
            case PackageManagerService.REASON_INSTALL_FAST:
                reason = ReasonMapping.REASON_INSTALL_FAST;
                break;
            case PackageManagerService.REASON_INSTALL_BULK:
                reason = ReasonMapping.REASON_INSTALL_BULK;
                break;
            case PackageManagerService.REASON_INSTALL_BULK_SECONDARY:
                reason = ReasonMapping.REASON_INSTALL_BULK_SECONDARY;
                break;
            case PackageManagerService.REASON_INSTALL_BULK_DOWNGRADED:
                reason = ReasonMapping.REASON_INSTALL_BULK_DOWNGRADED;
                break;
            case PackageManagerService.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED:
                reason = ReasonMapping.REASON_INSTALL_BULK_SECONDARY_DOWNGRADED;
                break;
            case PackageManagerService.REASON_BACKGROUND_DEXOPT:
                reason = ReasonMapping.REASON_BG_DEXOPT;
                break;
            case PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE:
                reason = ReasonMapping.REASON_INACTIVE;
                break;
            case PackageManagerService.REASON_CMDLINE:
                reason = ReasonMapping.REASON_CMDLINE;
                break;
            case PackageManagerService.REASON_SHARED:
            case PackageManagerService.REASON_AB_OTA:
                // REASON_SHARED shouldn't go into this code path - it's only used at lower levels
                // in PackageDexOptimizer.
                // TODO(b/251921228): OTA isn't supported, so REASON_AB_OTA shouldn't come this way
                // either.
                throw new UnsupportedOperationException(
                        "ART Service unsupported compilation reason " + mCompilationReason);
            default:
                throw new IllegalArgumentException(
                        "Invalid compilation reason " + mCompilationReason);
        }

        return new OptimizeParams.Builder(reason, flags)
                .setCompilerFilter(mCompilerFilter)
                .setPriorityClass(priority)
                .build();
    }
}