diff options
-rw-r--r-- | services/core/java/com/android/server/pm/dex/DexoptUtils.java | 89 | ||||
-rw-r--r-- | services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java | 37 |
2 files changed, 85 insertions, 41 deletions
diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java index c750f6526c7d..abac52f7a8da 100644 --- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java +++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java @@ -16,12 +16,12 @@ package com.android.server.pm.dex; - import android.content.pm.ApplicationInfo; import android.util.Slog; import android.util.SparseArray; import java.io.File; +import java.util.List; public final class DexoptUtils { private static final String TAG = "DexoptUtils"; @@ -50,6 +50,11 @@ public final class DexoptUtils { * - index 0 contains the context for the base apk * - index 1 to n contain the context for the splits in the order determined by * {@code info.getSplitCodePaths()} + * + * IMPORTANT: keep this logic in sync with the loading code in {@link android.app.LoadedApk} + * and pay attention to the way the classpath is created for the non isolated mode in: + * {@link android.app.LoadedApk#makePaths( + * android.app.ActivityThread, boolean, ApplicationInfo, List, List)}. */ public static String[] getClassLoaderContexts(ApplicationInfo info, String[] sharedLibraries) { // The base class loader context contains only the shared library. @@ -57,35 +62,36 @@ public final class DexoptUtils { String baseApkContextClassLoader = encodeClassLoader( sharedLibrariesClassPath, "dalvik.system.PathClassLoader"); - String[] splitCodePaths = info.getSplitCodePaths(); - - if (splitCodePaths == null) { + if (info.getSplitCodePaths() == null) { // The application has no splits. return new String[] {baseApkContextClassLoader}; } // The application has splits. Compute their class loader contexts. + // First, cache the relative paths of the splits and do some sanity checks + String[] splitRelativeCodePaths = getSplitRelativeCodePaths(info); + // The splits have an implicit dependency on the base apk. // This means that we have to add the base apk file in addition to the shared libraries. String baseApkName = new File(info.getBaseCodePath()).getName(); - String splitDependencyOnBase = encodeClassLoader( - encodeClasspath(sharedLibrariesClassPath, baseApkName), - "dalvik.system.PathClassLoader"); + String sharedLibrariesAndBaseClassPath = + encodeClasspath(sharedLibrariesClassPath, baseApkName); // The result is stored in classLoaderContexts. // Index 0 is the class loaded context for the base apk. // Index `i` is the class loader context encoding for split `i`. - String[] classLoaderContexts = new String[/*base apk*/ 1 + splitCodePaths.length]; + String[] classLoaderContexts = new String[/*base apk*/ 1 + splitRelativeCodePaths.length]; classLoaderContexts[0] = baseApkContextClassLoader; - SparseArray<int[]> splitDependencies = info.splitDependencies; - - if (splitDependencies == null) { - // If there are no inter-split dependencies, populate the result with the implicit - // dependency on the base apk. + if (!info.requestsIsolatedSplitLoading() || info.splitDependencies == null) { + // If the app didn't request for the splits to be loaded in isolation or if it does not + // declare inter-split dependencies, then all the splits will be loaded in the base + // apk class loader (in the order of their definition). + String classpath = sharedLibrariesAndBaseClassPath; for (int i = 1; i < classLoaderContexts.length; i++) { - classLoaderContexts[i] = splitDependencyOnBase; + classLoaderContexts[i] = encodeClassLoader(classpath, "dalvik.system.PathClassLoader"); + classpath = encodeClasspath(classpath, splitRelativeCodePaths[i - 1]); } } else { // In case of inter-split dependencies, we need to walk the dependency chain of each @@ -98,33 +104,27 @@ public final class DexoptUtils { // classLoaderContexts is that the later contains the full chain of class loaders for // a given split while splitClassLoaderEncodingCache only contains a single class loader // encoding. - String baseCodePath = new File(info.getBaseCodePath()).getParent(); - String[] splitClassLoaderEncodingCache = new String[splitCodePaths.length]; - for (int i = 0; i < splitCodePaths.length; i++) { - File pathFile = new File(splitCodePaths[i]); - String fileName = pathFile.getName(); - splitClassLoaderEncodingCache[i] = encodeClassLoader(fileName, + String[] splitClassLoaderEncodingCache = new String[splitRelativeCodePaths.length]; + for (int i = 0; i < splitRelativeCodePaths.length; i++) { + splitClassLoaderEncodingCache[i] = encodeClassLoader(splitRelativeCodePaths[i], "dalvik.system.PathClassLoader"); - // 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); - } } + String splitDependencyOnBase = encodeClassLoader( + sharedLibrariesAndBaseClassPath, "dalvik.system.PathClassLoader"); + SparseArray<int[]> splitDependencies = info.splitDependencies; for (int i = 1; i < splitDependencies.size(); i++) { getParentDependencies(splitDependencies.keyAt(i), splitClassLoaderEncodingCache, splitDependencies, classLoaderContexts, splitDependencyOnBase); } - } - // At this point classLoaderContexts contains only the parent dependencies. - // We also need to add the class loader of the current split which should - // come first in the context. - for (int i = 1; i < classLoaderContexts.length; i++) { - String splitClassLoader = encodeClassLoader("", "dalvik.system.PathClassLoader"); - classLoaderContexts[i] = encodeClassLoaderChain( - splitClassLoader, classLoaderContexts[i]); + // At this point classLoaderContexts contains only the parent dependencies. + // We also need to add the class loader of the current split which should + // come first in the context. + for (int i = 1; i < classLoaderContexts.length; i++) { + String splitClassLoader = encodeClassLoader("", "dalvik.system.PathClassLoader"); + classLoaderContexts[i] = encodeClassLoaderChain( + splitClassLoader, classLoaderContexts[i]); + } } return classLoaderContexts; @@ -227,4 +227,25 @@ public final class DexoptUtils { private static String encodeClassLoaderChain(String cl1, String cl2) { return cl1.isEmpty() ? cl2 : (cl1 + ";" + cl2); } + + /** + * Returns the relative paths of the splits declared by the application {@code info}. + * Assumes that the application declares a non-null array of splits. + */ + private static String[] getSplitRelativeCodePaths(ApplicationInfo info) { + String baseCodePath = new File(info.getBaseCodePath()).getParent(); + String[] splitCodePaths = info.getSplitCodePaths(); + String[] splitRelativeCodePaths = new String[splitCodePaths.length]; + for (int i = 0; i < splitCodePaths.length; i++) { + File pathFile = new File(splitCodePaths[i]); + splitRelativeCodePaths[i] = pathFile.getName(); + // 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); + } + } + return splitRelativeCodePaths; + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java index f1f176789148..21b286e68adf 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java @@ -41,6 +41,7 @@ public class DexoptUtilsTest { ApplicationInfo ai = new ApplicationInfo(); String codeDir = "/data/app/mock.android.com"; ai.setBaseCodePath(codeDir + "/base.dex"); + ai.privateFlags = ai.privateFlags | ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING; if (addSplits) { ai.setSplitCodePaths(new String[]{ @@ -73,7 +74,8 @@ public class DexoptUtilsTest { assertEquals(7, contexts.length); assertEquals("PCL[a.dex:b.dex]", contexts[0]); - assertEquals("PCL[];PCL[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]); + assertEquals("PCL[];PCL[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", + contexts[1]); assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]); assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]); assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]); @@ -89,15 +91,36 @@ public class DexoptUtilsTest { assertEquals(7, contexts.length); assertEquals("PCL[a.dex:b.dex]", contexts[0]); - assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[1]); - assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[2]); - assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[3]); - assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]); - assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]); - assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[6]); + assertEquals("PCL[a.dex:b.dex:base.dex]", contexts[1]); + assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex]", contexts[2]); + assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex]", contexts[3]); + assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex]", contexts[4]); + assertEquals( + "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex]", + contexts[5]); + assertEquals( + "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]", + contexts[6]); } @Test + public void testSplitChainNoIsolationNoSharedLibrary() { + ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true); + ai.privateFlags = ai.privateFlags & (~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING); + String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null); + + assertEquals(7, contexts.length); + assertEquals("PCL[]", contexts[0]); + assertEquals("PCL[base.dex]", contexts[1]); + assertEquals("PCL[base.dex:base-1.dex]", contexts[2]); + assertEquals("PCL[base.dex:base-1.dex:base-2.dex]", contexts[3]); + assertEquals("PCL[base.dex:base-1.dex:base-2.dex:base-3.dex]", contexts[4]); + assertEquals("PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex]", contexts[5]); + assertEquals( + "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]", + contexts[6]); + } + @Test public void testSplitChainNoSharedLibraries() { ApplicationInfo ai = createMockApplicationInfo( DELEGATE_LAST_CLASS_LOADER_NAME, true, true); |