diff options
author | Jeff Hao <jeffhao@google.com> | 2017-04-19 14:42:16 -0700 |
---|---|---|
committer | Jeff Hao <jeffhao@google.com> | 2017-04-20 15:43:06 -0700 |
commit | 08693df39d0c3248068c8d0374a8aeec65391b51 (patch) | |
tree | afb556343d3d52a986789783f7599d597db150ee | |
parent | ec41ae655fe5fc096da0556a8e86e99d0250ab0e (diff) |
Gather shared libs for split apks based on dependencies.
Traverses split dependency hierarchy to determine what the split paths
should be to add to the shared libs to pass down to dex2oat.
Bug: 34169257
Bug: 37480811
Test: cts-tradefed run singleCommand cts -d --module
CtsAppSecurityHostTestCases -t android.appsecurity.cts.SplitTests
Checked not only that CTS tests pass, but that shared libraries match
and collision checks are not being performed.
E.g. testInstallAllFeatureSplits has dependencies where:
APK_BASE <- APK_FEATURE_A <- APK_FEATURE_B and
APK_BASE <- APK_FEATURE_C.
Shared libraries passed to dex2oat:
for base.apk: /system/framework/android.test.runner.jar
for feature_a: /system/framework/android.test.runner.jar:base.apk
for feature_b: /system/framework/android.test.runner.jar:base.apk:split_feature_a.apk
for feature_c: /system/framework/android.test.runner.jar:base.apk
Change-Id: Ia2b0c5a7c9d6c1a2e9c5bde52ae7c53ddf964b96
-rw-r--r-- | services/core/java/com/android/server/pm/PackageDexOptimizer.java | 108 |
1 files changed, 85 insertions, 23 deletions
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 8413491aaef8..254bc2ac0e8b 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -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; @@ -437,6 +436,69 @@ public class PackageDexOptimizer { } /** + * 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. * |