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

Commit 3f2b728b authored by Calin Juravle's avatar Calin Juravle
Browse files

Allow PackageDexUsage to record code paths not used by others

The restriction was just an optimization to make the processing simpler
and clearer. All packages are expected to use their code paths and
recording this info was redundant and added noise. However, in
prepartion to record system server code paths we have to record
everything that belongs to a package, even if it's not used by others.

This CL makes PackageDexUsage agnostic of the isUsedByOther flags: it
will record everything that is given to it.

Note that this applies only to PackageDexUsage. DexManager may still
decide to skip the recording (which it does for non system server apps)

Test: atest PackageDexUsageTests DexManagerTests
Bug: 148774920
Change-Id: Icdb9ff45330a8fb15d80cb07495723b473228dda
parent 33fc19ce
Loading
Loading
Loading
Loading
+18 −17
Original line number Diff line number Diff line
@@ -103,19 +103,6 @@ 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(Context context, IPackageManager pms, PackageDexOptimizer pdo,
            Installer installer, Object installLock) {
        mContext = context;
@@ -194,6 +181,8 @@ public class DexManager {
                    // If the dex file is the primary apk (or a split) and not isUsedByOtherApps
                    // do not record it. This case does not bring any new usable information
                    // and can be safely skipped.
                    // Note this is just an optimization that makes things easier to read in the
                    // package-dex-use file since we don't need to pollute it with redundant info.
                    continue;
                }

@@ -211,7 +200,7 @@ public class DexManager {
                    // async write to disk to make sure we don't loose the data in case of a reboot.

                    if (mPackageDexUsage.record(searchResult.mOwningPackageName,
                            dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
                            dexPath, loaderUserId, loaderIsa, primaryOrSplit,
                            loadingAppInfo.packageName, classLoaderContext)) {
                        mPackageDexUsage.maybeWriteAsync();
                    }
@@ -391,8 +380,17 @@ public class DexManager {
     * to access the package use.
     */
    public PackageUseInfo getPackageUseInfoOrDefault(String packageName) {
        // 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.
        PackageUseInfo useInfo = mPackageDexUsage.getPackageUseInfo(packageName);
        return useInfo == null ? DEFAULT_USE_INFO : useInfo;
        return useInfo == null ? new PackageUseInfo(packageName) : useInfo;
    }

    /**
@@ -542,7 +540,7 @@ public class DexManager {
    // TODO(calin): questionable API in the presence of class loaders context. Needs amends as the
    // compilation happening here will use a pessimistic context.
    public RegisterDexModuleResult registerDexModule(ApplicationInfo info, String dexPath,
            boolean isUsedByOtherApps, int userId) {
            boolean isSharedModule, int userId) {
        // Find the owning package record.
        DexSearchResult searchResult = getDexPackage(info, dexPath, userId);

@@ -559,9 +557,12 @@ public class DexManager {

        // We found the package. Now record the usage for all declared ISAs.
        boolean update = false;
        // If this is a shared module set the loading package to an arbitrary package name
        // so that we can mark that module as usedByOthers.
        String loadingPackage = isSharedModule ? ".shared.module" : searchResult.mOwningPackageName;
        for (String isa : getAppDexInstructionSets(info.primaryCpuAbi, info.secondaryCpuAbi)) {
            boolean newUpdate = mPackageDexUsage.record(searchResult.mOwningPackageName,
                    dexPath, userId, isa, isUsedByOtherApps, /*primaryOrSplit*/ false,
                    dexPath, userId, isa, /*primaryOrSplit*/ false,
                    searchResult.mOwningPackageName,
                    PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT);
            update |= newUpdate;
+52 −40
Original line number Diff line number Diff line
@@ -39,10 +39,12 @@ import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -118,7 +120,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
     *         has been seen before.
     */
    /* package */ boolean record(String owningPackageName, String dexPath, int ownerUserId,
            String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit,
            String loaderIsa, boolean primaryOrSplit,
            String loadingPackageName, String classLoaderContext) {
        if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
            throw new IllegalArgumentException("loaderIsa " + loaderIsa + " is unsupported");
@@ -127,20 +129,22 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
            throw new IllegalArgumentException("Null classLoaderContext");
        }
        if (classLoaderContext.equals(UNSUPPORTED_CLASS_LOADER_CONTEXT)) {
            Slog.e(TAG, "Unsupported context?");
            return false;
        }

        boolean isUsedByOtherApps = !owningPackageName.equals(loadingPackageName);

        synchronized (mPackageUseInfoMap) {
            PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(owningPackageName);
            if (packageUseInfo == null) {
                // This is the first time we see the package.
                packageUseInfo = new PackageUseInfo();
                packageUseInfo = new PackageUseInfo(owningPackageName);
                if (primaryOrSplit) {
                    // If we have a primary or a split apk, set isUsedByOtherApps.
                    // We do not need to record the loaderIsa or the owner because we compile
                    // primaries for all users and all ISAs.
                    packageUseInfo.mergeCodePathUsedByOtherApps(dexPath, isUsedByOtherApps,
                            owningPackageName, loadingPackageName);
                    packageUseInfo.mergePrimaryCodePaths(dexPath, loadingPackageName);
                } else {
                    // For secondary dex files record the loaderISA and the owner. We'll need
                    // to know under which user to compile and for what ISA.
@@ -156,9 +160,8 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
                // We already have data on this package. Amend it.
                if (primaryOrSplit) {
                    // We have a possible update on the primary apk usage. Merge
                    // isUsedByOtherApps information and return if there was an update.
                    return packageUseInfo.mergeCodePathUsedByOtherApps(
                            dexPath, isUsedByOtherApps, owningPackageName, loadingPackageName);
                    // dex path information and return if there was an update.
                    return packageUseInfo.mergePrimaryCodePaths(dexPath, loadingPackageName);
                } else {
                    DexUseInfo newData = new DexUseInfo(
                            isUsedByOtherApps, ownerUserId, classLoaderContext, loaderIsa);
@@ -273,7 +276,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {

            // Write the code paths used by other apps.
            for (Map.Entry<String, Set<String>> codeEntry :
                    packageUseInfo.mCodePathsUsedByOtherApps.entrySet()) {
                    packageUseInfo.mPrimaryCodePaths.entrySet()) {
                String codePath = codeEntry.getKey();
                Set<String> loadingPackages = codeEntry.getValue();
                fpw.println(CODE_PATH_LINE_CHAR + codePath);
@@ -408,11 +411,11 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
                //    @loading_packages
                String codePath = line.substring(CODE_PATH_LINE_CHAR.length());
                Set<String> loadingPackages = readLoadingPackages(in, version);
                currentPackageData.mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
                currentPackageData.mPrimaryCodePaths.put(codePath, loadingPackages);
            } else {
                // This is a package line.
                currentPackage = line;
                currentPackageData = new PackageUseInfo();
                currentPackageData = new PackageUseInfo(currentPackage);
                data.put(currentPackage, currentPackageData);
            }
        }
@@ -499,7 +502,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
                    // Sync the code paths.
                    Set<String> codePaths = packageToCodePaths.get(packageName);
                    Iterator<Map.Entry<String, Set<String>>> codeIt =
                        packageUseInfo.mCodePathsUsedByOtherApps.entrySet().iterator();
                            packageUseInfo.mPrimaryCodePaths.entrySet().iterator();
                    while (codeIt.hasNext()) {
                        if (!codePaths.contains(codeIt.next().getKey())) {
                            codeIt.remove();
@@ -667,22 +670,26 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
     * Stores data on how a package and its dex files are used.
     */
    public static class PackageUseInfo {
        // The name of the package this info belongs to.
        private final String mPackageName;
        // The app's code paths that are used by other apps.
        // The key is the code path and the value is the set of loading packages.
        private final Map<String, Set<String>> mCodePathsUsedByOtherApps;
        private final Map<String, Set<String>> mPrimaryCodePaths;
        // Map dex paths to their data (isUsedByOtherApps, owner id, loader isa).
        private final Map<String, DexUseInfo> mDexUseInfoMap;

        /*package*/ PackageUseInfo() {
            mCodePathsUsedByOtherApps = new HashMap<>();
        /*package*/ PackageUseInfo(String packageName) {
            mPrimaryCodePaths = new HashMap<>();
            mDexUseInfoMap = new HashMap<>();
            mPackageName = packageName;
        }

        // Creates a deep copy of the `other`.
        private PackageUseInfo(PackageUseInfo other) {
            mCodePathsUsedByOtherApps = new HashMap<>();
            for (Map.Entry<String, Set<String>> e : other.mCodePathsUsedByOtherApps.entrySet()) {
                mCodePathsUsedByOtherApps.put(e.getKey(), new HashSet<>(e.getValue()));
            mPackageName = other.mPackageName;
            mPrimaryCodePaths = new HashMap<>();
            for (Map.Entry<String, Set<String>> e : other.mPrimaryCodePaths.entrySet()) {
                mPrimaryCodePaths.put(e.getKey(), new HashSet<>(e.getValue()));
            }

            mDexUseInfoMap = new HashMap<>();
@@ -691,28 +698,29 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
            }
        }

        private boolean mergeCodePathUsedByOtherApps(String codePath, boolean isUsedByOtherApps,
                String owningPackageName, String loadingPackage) {
            if (!isUsedByOtherApps) {
                // Nothing to update if the the code path is not used by other apps.
                return false;
            }

            boolean newCodePath = false;
            Set<String> loadingPackages = mCodePathsUsedByOtherApps.get(codePath);
        private boolean mergePrimaryCodePaths(String codePath, String loadingPackage) {
            Set<String> loadingPackages = mPrimaryCodePaths.get(codePath);
            if (loadingPackages == null) {
                loadingPackages = new HashSet<>();
                mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
                newCodePath = true;
                mPrimaryCodePaths.put(codePath, loadingPackages);
            }
            boolean newLoadingPackage = loadingPackage != null
                    && !loadingPackage.equals(owningPackageName)
                    && loadingPackages.add(loadingPackage);
            return newCodePath || newLoadingPackage;
            return loadingPackages.add(loadingPackage);
        }

        public boolean isUsedByOtherApps(String codePath) {
            return mCodePathsUsedByOtherApps.containsKey(codePath);
            if (mPrimaryCodePaths.containsKey(codePath)) {
                Set<String> loadingPackages = mPrimaryCodePaths.get(codePath);
                if (loadingPackages.contains(mPackageName)) {
                    // If the owning package is in the list then this code path
                    // is used by others if there are other packages in the list.
                    return loadingPackages.size() > 1;
                } else {
                    // The owning package is not in the loading packages. So if
                    // the list is non-empty then the code path is used by others.
                    return !loadingPackages.isEmpty();
                }
            }
            return false;
        }

        public Map<String, DexUseInfo> getDexUseInfoMap() {
@@ -720,11 +728,11 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
        }

        public Set<String> getLoadingPackages(String codePath) {
            return mCodePathsUsedByOtherApps.getOrDefault(codePath, null);
            return mPrimaryCodePaths.getOrDefault(codePath, null);
        }

        public boolean isAnyCodePathUsedByOtherApps() {
            return !mCodePathsUsedByOtherApps.isEmpty();
            return !mPrimaryCodePaths.isEmpty();
        }

        /**
@@ -732,12 +740,16 @@ public class PackageDexUsage extends AbstractStatsBase<Void> {
         * Returns whether or not there was an update.
         */
        /*package*/ boolean clearCodePathUsedByOtherApps() {
            if (mCodePathsUsedByOtherApps.isEmpty()) {
                return false;
            } else {
                mCodePathsUsedByOtherApps.clear();
                return true;
            boolean updated = false;
            List<String> retainOnlyOwningPackage = new ArrayList<>(1);
            retainOnlyOwningPackage.add(mPackageName);
            for (Map.Entry<String, Set<String>> entry : mPrimaryCodePaths.entrySet()) {
                // Remove or loading packages but the owning one.
                if (entry.getValue().retainAll(retainOnlyOwningPackage)) {
                    updated = true;
                }
            }
            return updated;
        }
    }

+141 −41

File changed.

Preview size limit exceeded, changes collapsed.