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

Commit 563a5cad authored by Calin Juravle's avatar Calin Juravle Committed by Andreas Gampe
Browse files

Ensure dexopt is executed only with consistent package data

The main objects bookkeeping packages (PackageParser.Package and its
corresponding ApplicationInfo) may be updated at different times. This
creates a window where the data stored in the ApplicationInfo is out of
date with respect to the data stored in PackageParser.Package.

During install, the two objects are "synced" during doRename which updates
the package code paths.

This CLs moves the dexopt invocation from the install flow after doRename
to ensure that dexopt logic gets a consistent view of the package.

(cherry picked from commit 4c2b9555)

Bug: 64493351

Test: run cts-dev -t android.appsecurity.cts.ClassloaderSplitsTest -m
CtsAppSecurityHostTestCases
      inspect oat files after
adb install-multiple CtsClassloaderSplitApp/CtsClassloaderSplitApp.apk
    CtsClassloaderSplitAppFeatureA/CtsClassloaderSplitAppFeatureA.apk
    CtsClassloaderSplitAppFeatureB/CtsClassloaderSplitAppFeatureB.apk

Merged-In: I9131bcf49eb473a8fdc5eb0032d94080d4e9e94b
Change-Id: I9131bcf49eb473a8fdc5eb0032d94080d4e9e94b
parent dfe472ec
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import com.android.server.pm.dex.PackageDexUsage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@@ -161,6 +162,17 @@ public class PackageDexOptimizer {
        String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts(
                pkg.applicationInfo, sharedLibraries);

        // Sanity check that we do not call dexopt with inconsistent data.
        if (paths.size() != classLoaderContexts.length) {
            String[] splitCodePaths = pkg.applicationInfo.getSplitCodePaths();
            throw new IllegalStateException("Inconsistent information "
                + "between PackageParser.Package and its ApplicationInfo. "
                + "pkg.getAllCodePaths=" + paths
                + " pkg.applicationInfo.getBaseCodePath=" + pkg.applicationInfo.getBaseCodePath()
                + " pkg.applicationInfo.getSplitCodePaths="
                + (splitCodePaths == null ? "null" : Arrays.toString(splitCodePaths)));
        }

        int result = DEX_OPT_SKIPPED;
        for (int i = 0; i < paths.size(); i++) {
            // Skip paths that have no code.
+44 −30
Original line number Diff line number Diff line
@@ -18263,15 +18263,35 @@ public class PackageManagerService extends IPackageManager.Stub
                    Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
                }
            }
        }
        if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
            res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
            return;
        }
            // dexopt can take some time to complete, so, for instant apps, 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 user instead of mysteriously blocking somewhere in the
        // Verify if we need to dexopt the app.
        //
        // NOTE: it is *important* to call dexopt after doRename which will sync the
        // package data from PackageParser.Package and its corresponding ApplicationInfo.
        //
        // We only need to dexopt if the package meets ALL of the following conditions:
        //   1) it is not forward locked.
        //   2) it is not on on an external ASEC container.
        //   3) it is not an instant app or if it is then dexopt is enabled via gservices.
        //
        // 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.
            if (!instantApp || Global.getInt(
                        mContext.getContentResolver(), Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) {
        final boolean performDexopt = !forwardLocked
            && !pkg.applicationInfo.isExternalAsec()
            && (!instantApp || Global.getInt(mContext.getContentResolver(),
                    Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0);
        if (performDexopt) {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
            // Do not run PackageDexOptimizer through the local performDexOpt
            // method because `pkg` may not be in `mPackages` yet.
@@ -18290,15 +18310,9 @@ public class PackageManagerService extends IPackageManager.Stub
        // Notify BackgroundDexOptService that the package has been changed.
        // If this is an update of a package which used to fail to compile,
            // BDOS will remove it from its blacklist.
        // BackgroundDexOptService will remove it from its blacklist.
        // TODO: Layering violation
        BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
        }
        if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
            res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
            return;
        }
        startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);