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

Commit 2c6761d6 authored by Jeff Hao's avatar Jeff Hao Committed by android-build-merger
Browse files

Merge "Gather shared libs for split apks based on dependencies." into oc-dev

am: dc088a64

Change-Id: I186c0c1a189836e7684f7e6204e0ee86fc810a97
parents 58510a2d dc088a64
Loading
Loading
Loading
Loading
+85 −23
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.os.UserHandle;
import android.os.WorkSource;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
@@ -141,7 +142,7 @@ public class PackageDexOptimizer {
        final String[] instructionSets = targetInstructionSets != null ?
                targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
        final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
        final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
        final List<String> paths = pkg.getAllCodePaths();
        final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);

        final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
@@ -149,18 +150,32 @@ public class PackageDexOptimizer {
        final boolean profileUpdated = checkForProfileUpdates &&
                isProfileUpdated(pkg, sharedGid, compilerFilter);

        String sharedLibrariesPath = getSharedLibrariesPath(sharedLibraries);
        final String sharedLibrariesPath = getSharedLibrariesPath(sharedLibraries);
        // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
        final int dexoptFlags = getDexFlags(pkg, compilerFilter);
        // Get the dependencies of each split in the package. For each code path in the package,
        // this array contains the relative paths of each split it depends on, separated by colons.
        String[] splitDependencies = getSplitDependencies(pkg);

        int result = DEX_OPT_SKIPPED;
        // TODO: Iterate based on dependency hierarchy (currently alphabetically by name)
        // (b/37480811).
        String basePathCheck = null;
        for (String path : paths) {
        for (int i = 0; i < paths.size(); i++) {
            // Skip paths that have no code.
            if ((i == 0 && (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) ||
                    (i != 0 && (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) == 0)) {
                continue;
            }
            // Append shared libraries with split dependencies for this split.
            String path = paths.get(i);
            String sharedLibrariesPathWithSplits;
            if (sharedLibrariesPath != null && splitDependencies[i] != null) {
                sharedLibrariesPathWithSplits = sharedLibrariesPath + ":" + splitDependencies[i];
            } else {
                sharedLibrariesPathWithSplits =
                        splitDependencies[i] != null ? splitDependencies[i] : sharedLibrariesPath;
            }
            for (String dexCodeIsa : dexCodeInstructionSets) {
                int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter, profileUpdated,
                        sharedLibrariesPath, dexoptFlags, sharedGid, packageStats);
                        sharedLibrariesPathWithSplits, dexoptFlags, sharedGid, packageStats);
                // The end result is:
                //  - FAILED if any path failed,
                //  - PERFORMED if at least one path needed compilation,
@@ -168,22 +183,6 @@ public class PackageDexOptimizer {
                if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) {
                    result = newResult;
                }
                // Add the relative path of code we just compiled to the shared libraries.
                int slashIndex = path.lastIndexOf('/') + 1;
                String relativePath = path.substring(slashIndex);
                if (sharedLibrariesPath == null) {
                    sharedLibrariesPath = relativePath;
                } else {
                    sharedLibrariesPath += ":" + relativePath;
                }
                // Sanity check that the base paths are all the same.
                String basePath = path.substring(0, slashIndex);
                if (basePathCheck == null) {
                    basePathCheck = basePath;
                } else if (!basePath.equals(basePathCheck)) {
                    Slog.wtf(TAG, "Split paths have different base paths: " + basePath + " and " +
                        basePathCheck);
                }
            }
        }
        return result;
@@ -436,6 +435,69 @@ public class PackageDexOptimizer {
        return sb.toString();
    }

    /**
     * Walks dependency tree and gathers the dependencies for each split in a split apk.
     * The split paths are stored as relative paths, separated by colons.
     */
    private String[] getSplitDependencies(PackageParser.Package pkg) {
        // Convert all the code paths to relative paths.
        String baseCodePath = new File(pkg.baseCodePath).getParent();
        List<String> paths = pkg.getAllCodePaths();
        String[] splitDependencies = new String[paths.size()];
        for (int i = 0; i < paths.size(); i++) {
            File pathFile = new File(paths.get(i));
            String fileName = pathFile.getName();
            paths.set(i, fileName);

            // Sanity check that the base paths of the splits are all the same.
            String basePath = pathFile.getParent();
            if (!basePath.equals(baseCodePath)) {
                Slog.wtf(TAG, "Split paths have different base paths: " + basePath + " and " +
                        baseCodePath);
            }
        }

        // If there are no other dependencies, fill in the implicit dependency on the base apk.
        SparseArray<int[]> dependencies = pkg.applicationInfo.splitDependencies;
        if (dependencies == null) {
            for (int i = 1; i < paths.size(); i++) {
                splitDependencies[i] = paths.get(0);
            }
            return splitDependencies;
        }

        // Fill in the dependencies, skipping the base apk which has no dependencies.
        for (int i = 1; i < dependencies.size(); i++) {
            getParentDependencies(dependencies.keyAt(i), paths, dependencies, splitDependencies);
        }

        return splitDependencies;
    }

    /**
     * Recursive method to generate dependencies for a particular split.
     * The index is a key from the package's splitDependencies.
     */
    private String getParentDependencies(int index, List<String> paths,
            SparseArray<int[]> dependencies, String[] splitDependencies) {
        // The base apk is always first, and has no dependencies.
        if (index == 0) {
            return null;
        }
        // Return the result if we've computed the dependencies for this index already.
        if (splitDependencies[index] != null) {
            return splitDependencies[index];
        }
        // Get the dependencies for the parent of this index and append its path to it.
        int parent = dependencies.get(index)[0];
        String parentDependencies =
                getParentDependencies(parent, paths, dependencies, splitDependencies);
        String path = parentDependencies == null ? paths.get(parent) :
                parentDependencies + ":" + paths.get(parent);
        splitDependencies[index] = path;
        return path;
    }

    /**
     * Checks if there is an update on the profile information of the {@code pkg}.
     * If the compiler filter is not profile guided the method returns false.