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

Commit 06ae4cf5 authored by Martin Stjernholm's avatar Martin Stjernholm
Browse files

Add a checked exception to legacy dexopt code paths.

This exception helps us identify the code in PackageManager that will
eventually be replaced by ART Service. It's propagated to the point
where the code paths need to fork to call ArtManagerLocal instead.

This CL only adds TODO comments at those locations, unless supporting
ART Service is trivial.

This also fixes some cases where Exception is caught. It's an
antipattern because it matches both checked and unchecked exceptions,
so it hides unintended catching of checked exceptions. Instead catch
RuntimeException, along with a specific list of checked exceptions, if
any.

Test: Boot without dalvik.vm.useartservice=true
Test: m FrameworksMockingServicesTests
Bug: 251903639
Change-Id: I53758b8e4b135287e34b412ad1257e5f5fa58273
parent be9794ed
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -247,6 +247,19 @@ public class FunctionalUtils {
        }
    }

    /**
     * A {@link Supplier} that allows the caller to specify a custom checked {@link Exception} that
     * can be thrown by the implementer. This is usually used when proxying/wrapping calls between
     * different classes.
     *
     * @param <Output> Method return type
     * @param <ExceptionType> Checked exception type
     */
    @FunctionalInterface
    public interface ThrowingCheckedSupplier<Output, ExceptionType extends Exception> {
        Output get() throws ExceptionType;
    }

    /**
     * A {@link Consumer} that allows the caller to specify a custom checked {@link Exception} that
     * can be thrown by the implementer. This is usually used when proxying/wrapping calls between
+44 −28
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.pm.DexOptHelper.useArtService;
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;

@@ -45,6 +46,7 @@ import android.util.TimingsTraceLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.pkg.AndroidPackage;
@@ -242,6 +244,7 @@ public class AppDataHelper {
                }
            }

            if (!useArtService()) { // ART Service handles this on demand instead.
                // Prepare the application profiles only for upgrades and
                // first boot (so that we don't repeat the same operation at
                // each boot).
@@ -267,8 +270,13 @@ public class AppDataHelper {
                // profiles.
                if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()
                        || (userId != UserHandle.USER_SYSTEM)) {
                    try {
                        mArtManagerService.prepareAppProfiles(pkg, userId,
                                /* updateReferenceProfileContent= */ false);
                    } catch (LegacyDexoptDisabledException e2) {
                        throw new RuntimeException(e2);
                    }
                }
            }

            if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
@@ -578,7 +586,12 @@ public class AppDataHelper {
            Slog.wtf(TAG, "Package was null!", new Throwable());
            return;
        }
        // TODO(b/251903639): Call into ART Service.
        try {
            mArtManagerService.clearAppProfiles(pkg);
        } catch (LegacyDexoptDisabledException e) {
            throw new RuntimeException(e);
        }
    }

    public void destroyAppDataLIF(AndroidPackage pkg, int userId, int flags) {
@@ -615,8 +628,11 @@ public class AppDataHelper {
    }

    private void destroyAppProfilesLeafLIF(AndroidPackage pkg) {
        // TODO(b/251903639): Call into ART Service.
        try {
            mInstaller.destroyAppProfiles(pkg.getPackageName());
        } catch (LegacyDexoptDisabledException e) {
            throw new RuntimeException(e);
        } catch (Installer.InstallerException e) {
            Slog.w(TAG, String.valueOf(e));
        }
+85 −56
Original line number Diff line number Diff line
@@ -55,9 +55,11 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.FunctionalUtils.ThrowingCheckedSupplier;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.PinnerService;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.PackageDexOptimizer.DexOptResult;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
@@ -71,7 +73,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * Controls background dex optimization run as idle job or command line.
@@ -202,7 +203,8 @@ public final class BackgroundDexOptService {
    }

    /** Start scheduling job after boot completion */
    public void systemReady() {
    public void systemReady() throws LegacyDexoptDisabledException {
        Installer.checkLegacyDexoptDisabled();
        if (mInjector.isBackgroundDexOptDisabled()) {
            return;
        }
@@ -272,7 +274,8 @@ public final class BackgroundDexOptService {
     * @return true if dex optimization is complete. false if the task is cancelled or if there was
     *         an error.
     */
    public boolean runBackgroundDexoptJob(@Nullable List<String> packageNames) {
    public boolean runBackgroundDexoptJob(@Nullable List<String> packageNames)
            throws LegacyDexoptDisabledException {
        enforceRootOrShell();
        long identity = Binder.clearCallingIdentity();
        try {
@@ -301,7 +304,8 @@ public final class BackgroundDexOptService {
     *
     * <p>This is only for shell command and only root or shell user can use this.
     */
    public void cancelBackgroundDexoptJob() {
    public void cancelBackgroundDexoptJob() throws LegacyDexoptDisabledException {
        Installer.checkLegacyDexoptDisabled();
        enforceRootOrShell();
        Binder.withCleanCallingIdentity(() -> cancelDexOptAndWaitForCompletion());
    }
@@ -315,7 +319,8 @@ public final class BackgroundDexOptService {
     *
     * @param disable True if JobScheduler invocations should be disabled, false otherwise.
     */
    public void setDisableJobSchedulerJobs(boolean disable) {
    public void setDisableJobSchedulerJobs(boolean disable) throws LegacyDexoptDisabledException {
        Installer.checkLegacyDexoptDisabled();
        enforceRootOrShell();
        synchronized (mLock) {
            mDisableJobSchedulerJobs = disable;
@@ -323,14 +328,18 @@ public final class BackgroundDexOptService {
    }

    /** Adds listener for package update */
    public void addPackagesUpdatedListener(PackagesUpdatedListener listener) {
    public void addPackagesUpdatedListener(PackagesUpdatedListener listener)
            throws LegacyDexoptDisabledException {
        Installer.checkLegacyDexoptDisabled();
        synchronized (mLock) {
            mPackagesUpdatedListeners.add(listener);
        }
    }

    /** Removes package update listener */
    public void removePackagesUpdatedListener(PackagesUpdatedListener listener) {
    public void removePackagesUpdatedListener(PackagesUpdatedListener listener)
            throws LegacyDexoptDisabledException {
        Installer.checkLegacyDexoptDisabled();
        synchronized (mLock) {
            mPackagesUpdatedListeners.remove(listener);
        }
@@ -340,7 +349,8 @@ public final class BackgroundDexOptService {
     * Notifies package change and removes the package from the failed package list so that
     * the package can run dexopt again.
     */
    public void notifyPackageChanged(String packageName) {
    public void notifyPackageChanged(String packageName) throws LegacyDexoptDisabledException {
        Installer.checkLegacyDexoptDisabled();
        // The idle maintenance job skips packages which previously failed to
        // compile. The given package has changed and may successfully compile
        // now. Remove it from the list of known failing packages.
@@ -387,6 +397,7 @@ public final class BackgroundDexOptService {
                // Post boot job not finished yet. Run post boot job first.
                return false;
            }
            try {
                resetStatesForNewDexOptRunLocked(mInjector.createAndStartThread(
                        "BackgroundDexOptService_" + (isPostBootUpdateJob ? "PostBoot" : "Idle"),
                        () -> {
@@ -397,6 +408,8 @@ public final class BackgroundDexOptService {
                            try {
                                completed = runIdleOptimization(
                                        pm, pkgs, params.getJobId() == JOB_POST_BOOT_UPDATE);
                            } catch (LegacyDexoptDisabledException e) {
                                Slog.wtf(TAG, e);
                            } finally { // Those cleanup should be done always.
                                tr.traceEnd();
                                Slog.i(TAG,
@@ -418,6 +431,9 @@ public final class BackgroundDexOptService {
                                markDexOptCompleted();
                            }
                        }));
            } catch (LegacyDexoptDisabledException e) {
                Slog.wtf(TAG, e);
            }
        }
        return true;
    }
@@ -425,11 +441,16 @@ public final class BackgroundDexOptService {
    /** For BackgroundDexOptJobService to dispatch onStopJob event */
    /* package */ boolean onStopJob(BackgroundDexOptJobService job, JobParameters params) {
        Slog.i(TAG, "onStopJob:" + params.getJobId());
        // This cannot block as it is in main thread, thus dispatch to a newly created thread and
        // cancel it from there.
        // As this event does not happen often, creating a new thread is justified rather than
        // having one thread kept permanently.
        mInjector.createAndStartThread("DexOptCancel", this::cancelDexOptAndWaitForCompletion);
        // This cannot block as it is in main thread, thus dispatch to a newly created thread
        // and cancel it from there. As this event does not happen often, creating a new thread
        // is justified rather than having one thread kept permanently.
        mInjector.createAndStartThread("DexOptCancel", () -> {
            try {
                cancelDexOptAndWaitForCompletion();
            } catch (LegacyDexoptDisabledException e) {
                Slog.wtf(TAG, e);
            }
        });
        // Always reschedule for cancellation.
        return true;
    }
@@ -438,7 +459,7 @@ public final class BackgroundDexOptService {
     * Cancels pending dexopt and wait for completion of the cancellation. This can block the caller
     * until cancellation is done.
     */
    private void cancelDexOptAndWaitForCompletion() {
    private void cancelDexOptAndWaitForCompletion() throws LegacyDexoptDisabledException {
        synchronized (mLock) {
            if (mDexOptThread == null) {
                return;
@@ -496,7 +517,8 @@ public final class BackgroundDexOptService {
    }

    @GuardedBy("mLock")
    private void resetStatesForNewDexOptRunLocked(Thread thread) {
    private void resetStatesForNewDexOptRunLocked(Thread thread)
            throws LegacyDexoptDisabledException {
        mDexOptThread = thread;
        mLastCancelledPackages.clear();
        controlDexOptBlockingLocked(false);
@@ -510,7 +532,7 @@ public final class BackgroundDexOptService {
    }

    @GuardedBy("mLock")
    private void controlDexOptBlockingLocked(boolean block) {
    private void controlDexOptBlockingLocked(boolean block) throws LegacyDexoptDisabledException {
        PackageManagerService pm = mInjector.getPackageManagerService();
        mDexOptHelper.controlDexOptBlocking(block);
    }
@@ -564,8 +586,8 @@ public final class BackgroundDexOptService {
     * Returns whether we've successfully run the job. Note that it will return true even if some
     * packages may have failed compiling.
     */
    private boolean runIdleOptimization(
            PackageManagerService pm, List<String> pkgs, boolean isPostBootUpdate) {
    private boolean runIdleOptimization(PackageManagerService pm, List<String> pkgs,
            boolean isPostBootUpdate) throws LegacyDexoptDisabledException {
        synchronized (mLock) {
            mLastExecutionStatus = STATUS_UNSPECIFIED;
            mLastExecutionStartUptimeMs = SystemClock.uptimeMillis();
@@ -631,7 +653,8 @@ public final class BackgroundDexOptService {

    @Status
    private int idleOptimizePackages(PackageManagerService pm, List<String> pkgs,
            long lowStorageThreshold, boolean isPostBootUpdate) {
            long lowStorageThreshold, boolean isPostBootUpdate)
            throws LegacyDexoptDisabledException {
        ArraySet<String> updatedPackages = new ArraySet<>();

        try {
@@ -707,7 +730,8 @@ public final class BackgroundDexOptService {

    @Status
    private int optimizePackages(List<String> pkgs, long lowStorageThreshold,
            ArraySet<String> updatedPackages, boolean isPostBootUpdate) {
            ArraySet<String> updatedPackages, boolean isPostBootUpdate)
            throws LegacyDexoptDisabledException {
        boolean supportSecondaryDex = mInjector.supportSecondaryDex();

        // Keep the error if there is any error from any package.
@@ -760,7 +784,8 @@ public final class BackgroundDexOptService {
     */
    @DexOptResult
    private int downgradePackage(@NonNull Computer snapshot, PackageManagerService pm, String pkg,
            boolean isForPrimaryDex, boolean isPostBootUpdate) {
            boolean isForPrimaryDex, boolean isPostBootUpdate)
            throws LegacyDexoptDisabledException {
        if (DEBUG) {
            Slog.d(TAG, "Downgrading " + pkg);
        }
@@ -808,7 +833,7 @@ public final class BackgroundDexOptService {
    }

    @Status
    private int reconcileSecondaryDexFiles() {
    private int reconcileSecondaryDexFiles() throws LegacyDexoptDisabledException {
        // TODO(calin): should we denylist packages for which we fail to reconcile?
        for (String p : mInjector.getDexManager().getAllPackagesWithSecondaryDexFiles()) {
            if (isCancelling()) {
@@ -829,7 +854,8 @@ public final class BackgroundDexOptService {
     * @return PackageDexOptimizer#DEX_OPT_*
     */
    @DexOptResult
    private int optimizePackage(String pkg, boolean isForPrimaryDex, boolean isPostBootUpdate) {
    private int optimizePackage(String pkg, boolean isForPrimaryDex, boolean isPostBootUpdate)
            throws LegacyDexoptDisabledException {
        int reason = isPostBootUpdate ? PackageManagerService.REASON_POST_BOOT
                                      : PackageManagerService.REASON_BACKGROUND_DEXOPT;
        String filter = getCompilerFilterForReason(reason);
@@ -857,7 +883,8 @@ public final class BackgroundDexOptService {
    }

    @DexOptResult
    private int performDexOptPrimary(String pkg, int reason, String filter, int dexoptFlags) {
    private int performDexOptPrimary(String pkg, int reason, String filter, int dexoptFlags)
            throws LegacyDexoptDisabledException {
        DexoptOptions dexoptOptions =
                new DexoptOptions(pkg, reason, filter, /*splitName=*/null, dexoptFlags);
        return trackPerformDexOpt(pkg, /*isForPrimaryDex=*/true,
@@ -865,7 +892,8 @@ public final class BackgroundDexOptService {
    }

    @DexOptResult
    private int performDexOptSecondary(String pkg, int reason, String filter, int dexoptFlags) {
    private int performDexOptSecondary(String pkg, int reason, String filter, int dexoptFlags)
            throws LegacyDexoptDisabledException {
        DexoptOptions dexoptOptions = new DexoptOptions(pkg, reason, filter, /*splitName=*/null,
                dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX);
        return trackPerformDexOpt(pkg, /*isForPrimaryDex=*/false,
@@ -885,8 +913,9 @@ public final class BackgroundDexOptService {
     *  {@link PackageDexOptimizer#DEX_OPT_FAILED}
     */
    @DexOptResult
    private int trackPerformDexOpt(
            String pkg, boolean isForPrimaryDex, Supplier<Integer> performDexOptWrapper) {
    private int trackPerformDexOpt(String pkg, boolean isForPrimaryDex,
            ThrowingCheckedSupplier<Integer, LegacyDexoptDisabledException> performDexOptWrapper)
            throws LegacyDexoptDisabledException {
        ArraySet<String> failedPackageNames;
        synchronized (mLock) {
            failedPackageNames =
+8 −2
Original line number Diff line number Diff line
@@ -124,6 +124,7 @@ import com.android.internal.util.CollectionUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.parsing.PackageInfoUtils;
@@ -3009,8 +3010,13 @@ public class ComputerEngine implements Computer {
                    ipw.println("[" + pkgName + "]");
                    ipw.increaseIndent();

                    // TODO(b/251903639): Call into ART Service.
                    try {
                        mPackageDexOptimizer.dumpDexoptState(ipw, pkg, pkgSetting,
                                mDexManager.getPackageUseInfoOrDefault(pkgName));
                    } catch (LegacyDexoptDisabledException e) {
                        throw new RuntimeException(e);
                    }
                    ipw.decreaseIndent();
                }
                ipw.println("BgDexopt state:");
+38 −15
Original line number Diff line number Diff line
@@ -66,6 +66,8 @@ import com.android.server.art.DexUseManagerLocal;
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.Installer.InstallerException;
import com.android.server.pm.Installer.LegacyDexoptDisabledException;
import com.android.server.pm.PackageDexOptimizer.DexOptResult;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
@@ -125,8 +127,9 @@ public final class DexOptHelper {
     * and {@code numberOfPackagesFailed}.
     */
    public int[] performDexOptUpgrade(List<PackageStateInternal> packageStates, boolean showDialog,
            final int compilationReason, boolean bootComplete) {

            final int compilationReason, boolean bootComplete)
            throws LegacyDexoptDisabledException {
        Installer.checkLegacyDexoptDisabled();
        int numberOfPackagesVisited = 0;
        int numberOfPackagesOptimized = 0;
        int numberOfPackagesSkipped = 0;
@@ -158,7 +161,7 @@ public final class DexOptHelper {
                            // even if things are already compiled.
                            // useProfileForDexopt = true;
                        }
                    } catch (Exception e) {
                    } catch (InstallerException | RuntimeException e) {
                        Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ",
                                e);
                    }
@@ -193,7 +196,7 @@ public final class DexOptHelper {
                                } else {
                                    useProfileForDexopt = true;
                                }
                            } catch (Exception e) {
                            } catch (InstallerException | RuntimeException e) {
                                Log.e(TAG, "Failed to copy profile "
                                        + profileFile.getAbsolutePath() + " ", e);
                            }
@@ -284,7 +287,8 @@ public final class DexOptHelper {
     * Checks if system UI package (typically "com.android.systemui") needs to be re-compiled, and
     * compiles it if needed.
     */
    private void checkAndDexOptSystemUi() {
    private void checkAndDexOptSystemUi() throws LegacyDexoptDisabledException {
        Installer.checkLegacyDexoptDisabled();
        Computer snapshot = mPm.snapshotComputer();
        String sysUiPackageName =
                mPm.mContext.getString(com.android.internal.R.string.config_systemUi);
@@ -320,7 +324,7 @@ public final class DexOptHelper {
                            Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath());
                        }
                    }
                } catch (Exception e) {
                } catch (InstallerException | RuntimeException e) {
                    Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath(), e);
                }
            }
@@ -341,7 +345,7 @@ public final class DexOptHelper {
    }

    @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
    public void performPackageDexOptUpgradeIfNeeded() {
    public void performPackageDexOptUpgradeIfNeeded() throws LegacyDexoptDisabledException {
        PackageManagerServiceUtils.enforceSystemOrRoot(
                "Only the system can request package update");

@@ -413,7 +417,12 @@ public final class DexOptHelper {
        }

        if (options.isDexoptOnlySecondaryDex()) {
            // TODO(b/251903639): Call into ART Service.
            try {
                return mPm.getDexManager().dexoptSecondaryDex(options);
            } catch (LegacyDexoptDisabledException e) {
                throw new RuntimeException(e);
            }
        } else {
            int dexoptStatus = performDexOptWithStatus(options);
            return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
@@ -470,6 +479,8 @@ public final class DexOptHelper {
        final long callingId = Binder.clearCallingIdentity();
        try {
            return performDexOptInternalWithDependenciesLI(p, pkgSetting, options);
        } catch (LegacyDexoptDisabledException e) {
            throw new RuntimeException(e);
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
@@ -542,7 +553,8 @@ public final class DexOptHelper {

    @DexOptResult
    private int performDexOptInternalWithDependenciesLI(
            AndroidPackage p, @NonNull PackageStateInternal pkgSetting, DexoptOptions options) {
            AndroidPackage p, @NonNull PackageStateInternal pkgSetting, DexoptOptions options)
            throws LegacyDexoptDisabledException {
        // System server gets a special path.
        if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) {
            return mPm.getDexManager().dexoptSystemServer(options);
@@ -621,7 +633,11 @@ public final class DexOptHelper {
        if (artSrvRes.isPresent()) {
            res = artSrvRes.get();
        } else {
            try {
                res = performDexOptInternalWithDependenciesLI(pkg, packageState, options);
            } catch (LegacyDexoptDisabledException e) {
                throw new RuntimeException(e);
            }
        }

        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -666,8 +682,8 @@ public final class DexOptHelper {
        return installerPkg.getUid() == Binder.getCallingUid();
    }

    public boolean performDexOptSecondary(String packageName, String compilerFilter,
            boolean force) {
    public boolean performDexOptSecondary(
            String packageName, String compilerFilter, boolean force) {
        int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX
                | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES
                | DexoptOptions.DEXOPT_BOOT_COMPLETE
@@ -869,7 +885,7 @@ public final class DexOptHelper {
        }
    }

    /*package*/ void controlDexOptBlocking(boolean block) {
    /*package*/ void controlDexOptBlocking(boolean block) throws LegacyDexoptDisabledException {
        mPm.mPackageDexOptimizer.controlDexOptBlocking(block);
    }

@@ -928,11 +944,18 @@ public final class DexOptHelper {
        Slog.i(TAG, "Falling back to old PackageManager dexopt for " + packageName + ": " + reason);
    }

    /**
     * Returns true if ART Service should be used for package optimization.
     */
    public static boolean useArtService() {
        return SystemProperties.getBoolean("dalvik.vm.useartservice", false);
    }

    /**
     * Returns {@link DexUseManagerLocal} if ART Service should be used for package optimization.
     */
    public static @Nullable DexUseManagerLocal getDexUseManagerLocal() {
        if (!"true".equals(SystemProperties.get("dalvik.vm.useartservice", ""))) {
        if (!useArtService()) {
            return null;
        }
        try {
@@ -946,7 +969,7 @@ public final class DexOptHelper {
     * Returns {@link ArtManagerLocal} if ART Service should be used for package optimization.
     */
    private static @Nullable ArtManagerLocal getArtManagerLocal() {
        if (!"true".equals(SystemProperties.get("dalvik.vm.useartservice", ""))) {
        if (!useArtService()) {
            return null;
        }
        try {
Loading