summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Hao <jeffhao@google.com>2017-04-19 14:42:16 -0700
committerJeff Hao <jeffhao@google.com>2017-04-20 15:43:06 -0700
commit08693df39d0c3248068c8d0374a8aeec65391b51 (patch)
treeafb556343d3d52a986789783f7599d597db150ee
parentec41ae655fe5fc096da0556a8e86e99d0250ab0e (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.java108
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.
*