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

Commit 9393a2f5 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topic "cherrypicker-L40500030010147113:N80100030172488922" into aosp-main-future

* changes:
  Make PackageManager call into ART Service for incremental apps.
  Clean up DexOptHelper.
  Add an asynchronous dexopt method.
parents ff04f751 e3a1db43
Loading
Loading
Loading
Loading
+100 −80
Original line number Diff line number Diff line
@@ -96,6 +96,9 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

@@ -105,6 +108,11 @@ import java.util.function.Predicate;
public final class DexOptHelper {
    private static final long SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000;

    @NonNull
    private static final ThreadPoolExecutor sDexoptExecutor =
            new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */,
                    60 /* keepAliveTime */, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

    private static boolean sArtManagerLocalIsInitialized = false;

    private final PackageManagerService mPm;
@@ -113,6 +121,11 @@ public final class DexOptHelper {
    // used, to make it available to the onDexoptDone callback.
    private volatile long mBootDexoptStartTime;

    static {
        // Recycle the thread if it's not used for `keepAliveTime`.
        sDexoptExecutor.allowsCoreThreadTimeOut();
    }

    DexOptHelper(PackageManagerService pm) {
        mPm = pm;
    }
@@ -713,11 +726,9 @@ public final class DexOptHelper {
        }
    }

    /**
     * Returns DexoptOptions by the given InstallRequest.
     */
    static DexoptOptions getDexoptOptionsByInstallRequest(InstallRequest installRequest,
            DexManager dexManager) {
    /** Returns DexoptOptions by the given InstallRequest. */
    private static DexoptOptions getDexoptOptionsByInstallRequest(
            InstallRequest installRequest, DexManager dexManager) {
        final PackageSetting ps = installRequest.getScannedPackageSetting();
        final String packageName = ps.getPackageName();
        final boolean isBackupOrRestore =
@@ -735,64 +746,33 @@ public final class DexOptHelper {
        var options = new DexoptOptions(packageName, compilationReason, dexoptFlags);
        if (installRequest.getDexoptCompilerFilter() != null) {
            options = options.overrideCompilerFilter(installRequest.getDexoptCompilerFilter());
        } else if (pkg != null && pkg.isDebuggable()) {
        } else if (shouldSkipDexopt(installRequest)) {
            options = options.overrideCompilerFilter(DexoptParams.COMPILER_FILTER_NOOP);
        }
        return options;
    }

    /**
     * Perform dexopt if needed for the installation
     */
    static void performDexoptIfNeeded(InstallRequest installRequest, DexManager dexManager,
            Context context, PackageManagerTracedLock.RawLock installLock) {

        // Construct the DexoptOptions early to see if we should skip running dexopt.
        //
        // Do not run PackageDexOptimizer through the local performDexOpt
        // method because `pkg` may not be in `mPackages` yet.
        //
        // Also, don't fail application installs if the dexopt step fails.
        DexoptOptions dexoptOptions = getDexoptOptionsByInstallRequest(installRequest, dexManager);
        // Check whether we need to dexopt the app.
        //
        // NOTE: it is IMPORTANT to call dexopt:
        //   - after doRename which will sync the package data from AndroidPackage and
        //     its corresponding ApplicationInfo.
        //   - after installNewPackageLIF or replacePackageLIF which will update result with the
        //     uid of the application (pkg.applicationInfo.uid).
        //     This update happens in place!
        //
        // We only need to dexopt if the package meets ALL of the following conditions:
        //   1) it is not an instant app or if it is then dexopt is enabled via gservices.
        //   2) it is not debuggable.
        //   3) it is not on Incremental File System.
        //
        // Note that we do not dexopt instant apps by default. dexopt can take some time to
        // complete, so we skip this step during installation. Instead, we'll take extra time
        // the first time the instant app starts. It's preferred to do it this way to provide
        // continuous progress to the useur instead of mysteriously blocking somewhere in the
        // middle of running an instant app. The default behaviour can be overridden
        // via gservices.
        //
        // Furthermore, dexopt may be skipped, depending on the install scenario and current
        // state of the device.
        //
        // TODO(b/174695087): instantApp and onIncremental should be removed and their install
        //       path moved to SCENARIO_FAST.

        final boolean performDexopt = DexOptHelper.shouldPerformDexopt(installRequest,
                dexoptOptions, context);
        if (performDexopt) {
            // dexopt can take long, and ArtService doesn't require installd, so we release
            // the lock here and re-acquire the lock after dexopt is finished.
    /** Perform dexopt if needed for the installation */
    static void performDexoptIfNeeded(
            InstallRequest installRequest,
            DexManager dexManager,
            PackageManagerTracedLock.RawLock installLock) {
        if (!shouldCallArtService(installRequest)) {
            return;
        }

        // dexopt can take long, and ArtService doesn't require installd, so we release the lock
        // here and re-acquire the lock after dexopt is finished.
        if (installLock != null) {
            installLock.unlock();
        }
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
                DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService(
                        installRequest, dexoptOptions);
            DexoptOptions dexoptOptions =
                    getDexoptOptionsByInstallRequest(installRequest, dexManager);
            // Don't fail application installs if the dexopt step fails.
            DexoptResult dexOptResult =
                    DexOptHelper.dexoptPackageUsingArtService(installRequest, dexoptOptions);
            installRequest.onDexoptFinished(dexOptResult);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -801,6 +781,39 @@ public final class DexOptHelper {
            }
        }
    }

    /** Same as above, but runs asynchronously. */
    static CompletableFuture<Void> performDexoptIfNeededAsync(
            InstallRequest installRequest, DexManager dexManager) {
        if (!shouldCallArtService(installRequest)) {
            return CompletableFuture.completedFuture(null);
        }

        return CompletableFuture.runAsync(
                        () -> {
                            try {
                                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
                                DexoptOptions dexoptOptions =
                                        getDexoptOptionsByInstallRequest(
                                                installRequest, dexManager);
                                // Don't fail application installs if the dexopt step fails.
                                // TODO(b/393076925): Make this async in ART Service.
                                DexoptResult dexOptResult =
                                        DexOptHelper.dexoptPackageUsingArtService(
                                                installRequest, dexoptOptions);
                                installRequest.onDexoptFinished(dexOptResult);
                            } finally {
                                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                            }
                        },
                        sDexoptExecutor)
                .exceptionally(
                        (t) -> {
                            // This should never happen. A normal dexopt failure should result
                            // in a DexoptResult.DEXOPT_FAILED, not an exception.
                            Slog.wtf(TAG, "Dexopt encountered a fatal error", t);
                            return null;
                        });
    }

    /**
@@ -835,29 +848,36 @@ public final class DexOptHelper {
        }
    }

    private static boolean shouldSkipDexopt(InstallRequest installRequest) {
        PackageSetting ps = installRequest.getScannedPackageSetting();
        AndroidPackage pkg = ps.getPkg();
        boolean onIncremental = isIncrementalPath(ps.getPathString());
        return pkg == null || pkg.isDebuggable() || onIncremental;
    }

    /**
     * Returns whether to perform dexopt by the given InstallRequest.
     * Returns whether to call ART Service to perform dexopt for the given InstallRequest. Note that
     * ART Service may still skip dexopt, depending on the specified compiler filter, compilation
     * reason, and other conditions.
     */
    static boolean shouldPerformDexopt(InstallRequest installRequest, DexoptOptions dexoptOptions,
            Context context) {
    private static boolean shouldCallArtService(InstallRequest installRequest) {
        final boolean isApex = ((installRequest.getScanFlags() & SCAN_AS_APEX) != 0);
        // Historically, we did not dexopt instant apps,  and we have no plan to do so in the
        // future, so there is no need to call into ART Service.
        final boolean instantApp = ((installRequest.getScanFlags() & SCAN_AS_INSTANT_APP) != 0);
        final PackageSetting ps = installRequest.getScannedPackageSetting();
        final AndroidPackage pkg = ps.getPkg();
        final boolean onIncremental = isIncrementalPath(ps.getPathString());
        final boolean performDexOptForRollback = Flags.recoverabilityDetection()
                ? !(installRequest.isRollback()
                && installRequest.getInstallSource().mInitiatingPackageName.equals("android"))
                : true;

        // Don't skip the dexopt call if the compiler filter is "skip". Instead, call dexopt with
        // the "skip" filter so that ART Service gets notified and skips dexopt itself.
        return (!instantApp || Global.getInt(context.getContentResolver(),
                Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
                && pkg != null
                && (!onIncremental)
                && !isApex
                && performDexOptForRollback;
        final boolean performDexOptForRollback =
                !(installRequest.isRollback()
                        && installRequest
                                .getInstallSource()
                                .mInitiatingPackageName
                                .equals("android"));

        // THINK TWICE when you add a new condition here. You probably want to add a condition to
        // `shouldSkipDexopt` instead. In that way, ART Service will be called with the "skip"
        // compiler filter and it will have the chance to decide whether to skip dexopt.
        return !instantApp && pkg != null && !isApex && performDexOptForRollback;
    }

    private static class StagedApexObserver extends IStagedApexObserver.Stub {
+4 −3
Original line number Diff line number Diff line
@@ -1151,7 +1151,8 @@ final class InstallPackageHelper {
                return;
            }
            request.setKeepArtProfile(true);
            DexOptHelper.performDexoptIfNeeded(request, mDexManager, mContext, null);
            // TODO(b/388159696): Use performDexoptIfNeededAsync.
            DexOptHelper.performDexoptIfNeeded(request, mDexManager, null /* installLock */);
        }
    }

@@ -2789,8 +2790,8 @@ final class InstallPackageHelper {
                                    | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
                }

                DexOptHelper.performDexoptIfNeeded(installRequest, mDexManager, mContext,
                        mPm.mInstallLock.getRawLock());
                DexOptHelper.performDexoptIfNeeded(
                        installRequest, mDexManager, mPm.mInstallLock.getRawLock());
            }
        }
        PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(