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

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

Use PackageUseInfo in DexOptimizer

Pass the PackageUseInfo directly to DexOptimizer and use it to detect if a
package is used by other apps. Move the usage checks closer to dexopt so
that they can be easily adapted when we add usage info for each of the
app's code paths separately.

This is a refactoring CLs to reduce the size and complexity of the
upcoming CLs which record the usage info for each of the application
splits.

(cherry picked from commit 3b74c417)

Bug: 64124380
Test: runtest -x
services/tests/servicestests/src/com/android/server/pm/dex/*

Merged-In: I8031590cdaff81ab1792ca19baddb6cb36dc021d
Change-Id: I8031590cdaff81ab1792ca19baddb6cb36dc021d
parent a18e9927
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -318,7 +318,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub {
        optimizer.performDexOpt(pkg, libraryDependencies,
                null /* ISAs */,
                null /* CompilerStats.PackageStats */,
                mPackageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName),
                mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(pkg.packageName),
                new DexoptOptions(pkg.packageName, compilationReason,
                        DexoptOptions.DEXOPT_BOOT_COMPLETE));

+15 −11
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.DexoptUtils;
import com.android.server.pm.dex.PackageDexUsage;
@@ -123,7 +124,7 @@ public class PackageDexOptimizer {
     */
    int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
            String[] instructionSets, CompilerStats.PackageStats packageStats,
            boolean isUsedByOtherApps, DexoptOptions options) {
            PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
        if (!canOptimizePackage(pkg)) {
            return DEX_OPT_SKIPPED;
        }
@@ -131,7 +132,7 @@ public class PackageDexOptimizer {
            final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
            try {
                return performDexOptLI(pkg, sharedLibraries, instructionSets,
                        packageStats, isUsedByOtherApps, options);
                        packageStats, packageUseInfo, options);
            } finally {
                releaseWakeLockLI(acquireTime);
            }
@@ -145,21 +146,13 @@ public class PackageDexOptimizer {
    @GuardedBy("mInstallLock")
    private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
            String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
            boolean isUsedByOtherApps, DexoptOptions options) {
            PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
        final String[] instructionSets = targetInstructionSets != null ?
                targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
        final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
        final List<String> paths = pkg.getAllCodePaths();
        final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);

        final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
                options.getCompilerFilter(), isUsedByOtherApps);
        final boolean profileUpdated = options.isCheckForProfileUpdates() &&
                isProfileUpdated(pkg, sharedGid, compilerFilter);

        // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
        final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete());

        // Get the class loader context dependencies.
        // For each code path in the package, this array contains the class loader context that
        // needs to be passed to dexopt in order to ensure correct optimizations.
@@ -183,6 +176,17 @@ public class PackageDexOptimizer {
                }
            }

            final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
                    || packageUseInfo.isUsedByOtherApps();
            final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
                options.getCompilerFilter(), isUsedByOtherApps);
            final boolean profileUpdated = options.isCheckForProfileUpdates() &&
                isProfileUpdated(pkg, sharedGid, compilerFilter);

            // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
            // flags.
            final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete());

            for (String dexCodeIsa : dexCodeInstructionSets) {
                int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
                        profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
+8 −6
Original line number Diff line number Diff line
@@ -9620,17 +9620,19 @@ public class PackageManagerService extends IPackageManager.Stub
        Collection<PackageParser.Package> deps = findSharedNonSystemLibraries(p);
        final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
        if (!deps.isEmpty()) {
            DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
                    options.getCompilerFilter(), options.getSplitName(),
                    options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
            for (PackageParser.Package depPackage : deps) {
                // TODO: Analyze and investigate if we (should) profile libraries.
                pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets,
                        getOrCreateCompilerPackageStats(depPackage),
                        true /* isUsedByOtherApps */,
                        options);
                    mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions);
            }
        }
        return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets,
                getOrCreateCompilerPackageStats(p),
                mDexManager.isUsedByOtherApps(p.packageName), options);
                mDexManager.getPackageUseInfoOrDefault(p.packageName), options);
    }
    /**
@@ -18281,7 +18283,7 @@ public class PackageManagerService extends IPackageManager.Stub
                mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
                        null /* instructionSets */,
                        getOrCreateCompilerPackageStats(pkg),
                        mDexManager.isUsedByOtherApps(pkg.packageName),
                        mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
                        dexoptOptions);
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
@@ -25066,8 +25068,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
                if (ps == null) {
                    continue;
                }
                PackageDexUsage.PackageUseInfo packageUseInfo = getDexManager().getPackageUseInfo(
                        pkg.packageName);
                PackageDexUsage.PackageUseInfo packageUseInfo =
                      getDexManager().getPackageUseInfoOrDefault(pkg.packageName);
                if (PackageManagerServiceUtils
                        .isUnusedSinceTimeInMillis(ps.firstInstallTime, currentTimeInMillis,
                                downgradeTimeThresholdMillis, packageUseInfo,
+2 −2
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import com.android.internal.util.ArrayUtils;
import android.annotation.NonNull;
import android.app.AppGlobals;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageParser;
import android.content.pm.ResolveInfo;
import android.os.Build;
@@ -144,8 +143,9 @@ public class PackageManagerServiceUtils {
                sortTemp, packageManagerService);

        // Give priority to apps used by other apps.
        DexManager dexManager = packageManagerService.getDexManager();
        applyPackageFilter((pkg) ->
                packageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName), result,
                dexManager.getPackageUseInfoOrDefault(pkg.packageName).isUsedByOtherApps(), result,
                remainingPkgs, sortTemp, packageManagerService);

        // Filter out packages that aren't recently used, add all remaining apps.
+37 −22
Original line number Diff line number Diff line
@@ -81,6 +81,19 @@ public class DexManager {
    private static int DEX_SEARCH_FOUND_SPLIT = 2;  // dex file is a split apk
    private static int DEX_SEARCH_FOUND_SECONDARY = 3;  // dex file is a secondary dex

    /**
     * We do not record packages that have no secondary dex files or that are not used by other
     * apps. This is an optimization to reduce the amount of data that needs to be written to
     * disk (apps will not usually be shared so this trims quite a bit the number we record).
     *
     * To make this behaviour transparent to the callers which need use information on packages,
     * DexManager will return this DEFAULT instance from
     * {@link DexManager#getPackageUseInfoOrDefault}. It has no data about secondary dex files and
     * is marked as not being used by other apps. This reflects the intended behaviour when we don't
     * find the package in the underlying data file.
     */
    private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();

    public DexManager(IPackageManager pms, PackageDexOptimizer pdo,
            Installer installer, Object installLock) {
      mPackageCodeLocationsCache = new HashMap<>();
@@ -321,10 +334,29 @@ public class DexManager {

    /**
     * Get the package dex usage for the given package name.
     * @return the package data or null if there is no data available for this package.
     * If there is no usage info the method will return a default {@code PackageUseInfo} with
     * no data about secondary dex files and marked as not being used by other apps.
     *
     * Note that no use info means the package was not used or it was used but not by other apps.
     * Also, note that right now we might prune packages which are not used by other apps.
     * TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
     * to access the package use.
     */
    public PackageUseInfo getPackageUseInfoOrDefault(String packageName) {
        PackageUseInfo useInfo = mPackageDexUsage.getPackageUseInfo(packageName);
        return useInfo == null ? DEFAULT_USE_INFO : useInfo;
    }

    /**
     * Return whether or not the manager has usage information on the give package.
     *
     * Note that no use info means the package was not used or it was used but not by other apps.
     * Also, note that right now we might prune packages which are not used by other apps.
     * TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
     * to access the package use.
     */
    public PackageUseInfo getPackageUseInfo(String packageName) {
        return mPackageDexUsage.getPackageUseInfo(packageName);
    /*package*/ boolean hasInfoOnPackage(String packageName) {
        return mPackageDexUsage.getPackageUseInfo(packageName) != null;
    }

    /**
@@ -343,7 +375,7 @@ public class DexManager {
                ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
                : mPackageDexOptimizer;
        String packageName = options.getPackageName();
        PackageUseInfo useInfo = getPackageUseInfo(packageName);
        PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
        if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
            if (DEBUG) {
                Slog.d(TAG, "No secondary dex use for package:" + packageName);
@@ -387,7 +419,7 @@ public class DexManager {
     * deleted, update the internal records and delete any generated oat files.
     */
    public void reconcileSecondaryDexFiles(String packageName) {
        PackageUseInfo useInfo = getPackageUseInfo(packageName);
        PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
        if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
            if (DEBUG) {
                Slog.d(TAG, "No secondary dex use for package:" + packageName);
@@ -518,23 +550,6 @@ public class DexManager {
        return mPackageDexUsage.getAllPackagesWithSecondaryDexFiles();
    }

    /**
     * Return true if the profiling data collected for the given app indicate
     * that the apps's APK has been loaded by another app.
     * Note that this returns false for all apps without any collected profiling data.
    */
    public boolean isUsedByOtherApps(String packageName) {
        PackageUseInfo useInfo = getPackageUseInfo(packageName);
        if (useInfo == null) {
            // No use info, means the package was not used or it was used but not by other apps.
            // Note that right now we might prune packages which are not used by other apps.
            // TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
            // to access the package use.
            return false;
        }
        return useInfo.isUsedByOtherApps();
    }

    /**
     * Retrieves the package which owns the given dexPath.
     */
Loading