Loading core/java/android/content/pm/ApplicationInfo.java +4 −0 Original line number Diff line number Diff line Loading @@ -688,6 +688,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * Cycles do not exist because they are illegal and screened for during installation. * * May be null if no splits are installed, or if no dependencies exist between them. * * NOTE: Any change to the way split dependencies are stored must update the logic that * creates the class loader context for dexopt (DexoptUtils#getClassLoaderContexts). * * @hide */ public SparseArray<int[]> splitDependencies; Loading services/core/java/com/android/server/pm/PackageDexOptimizer.java +13 −3 Original line number Diff line number Diff line Loading @@ -159,8 +159,13 @@ public class PackageDexOptimizer { // Get the class loader context dependencies. // For each code path in the package, this array contains the class loader context that // needs to be passed to dexopt in order to ensure correct optimizations. boolean[] pathsWithCode = new boolean[paths.size()]; pathsWithCode[0] = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0; for (int i = 1; i < paths.size(); i++) { pathsWithCode[i] = (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) != 0; } String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts( pkg.applicationInfo, sharedLibraries); pkg.applicationInfo, sharedLibraries, pathsWithCode); // Sanity check that we do not call dexopt with inconsistent data. if (paths.size() != classLoaderContexts.length) { Loading @@ -176,10 +181,15 @@ public class PackageDexOptimizer { int result = DEX_OPT_SKIPPED; 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)) { if (!pathsWithCode[i]) { continue; } if (classLoaderContexts[i] == null) { throw new IllegalStateException("Inconsistent information in the " + "package structure. A split is marked to contain code " + "but has no dependency listed. Index=" + i + " path=" + paths.get(i)); } // Append shared libraries with split dependencies for this split. String path = paths.get(i); if (options.getSplitName() != null) { Loading services/core/java/com/android/server/pm/dex/DexoptUtils.java +36 −8 Original line number Diff line number Diff line Loading @@ -33,7 +33,9 @@ public final class DexoptUtils { /** * Creates the class loader context dependencies for each of the application code paths. * The returned array contains the class loader contexts that needs to be passed to dexopt in * order to ensure correct optimizations. * order to ensure correct optimizations. "Code" paths with no actual code, as specified by * {@param pathsWithCode}, are ignored and will have null as their context in the returned array * (configuration splits are an example of paths without code). * * A class loader context describes how the class loader chain should be built by dex2oat * in order to ensure that classes are resolved during compilation as they would be resolved Loading @@ -58,7 +60,8 @@ public final class DexoptUtils { * {@link android.app.LoadedApk#makePaths( * android.app.ActivityThread, boolean, ApplicationInfo, List, List)}. */ public static String[] getClassLoaderContexts(ApplicationInfo info, String[] sharedLibraries) { public static String[] getClassLoaderContexts(ApplicationInfo info, String[] sharedLibraries, boolean[] pathsWithCode) { // The base class loader context contains only the shared library. String sharedLibrariesClassPath = encodeClasspath(sharedLibraries); String baseApkContextClassLoader = encodeClassLoader( Loading @@ -84,7 +87,7 @@ public final class DexoptUtils { // 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 + splitRelativeCodePaths.length]; classLoaderContexts[0] = baseApkContextClassLoader; classLoaderContexts[0] = pathsWithCode[0] ? baseApkContextClassLoader : null; 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 Loading @@ -92,7 +95,15 @@ public final class DexoptUtils { // apk class loader (in the order of their definition). String classpath = sharedLibrariesAndBaseClassPath; for (int i = 1; i < classLoaderContexts.length; i++) { classLoaderContexts[i] = encodeClassLoader(classpath, "dalvik.system.PathClassLoader"); classLoaderContexts[i] = pathsWithCode[i] ? encodeClassLoader(classpath, "dalvik.system.PathClassLoader") : null; // Note that the splits with no code are not removed from the classpath computation. // i.e. split_n might get the split_n-1 in its classpath dependency even // if split_n-1 has no code. // The splits with no code do not matter for the runtime which ignores // apks without code when doing the classpath checks. As such we could actually // filter them but we don't do it in order to keep consistency with how the apps // are loaded. classpath = encodeClasspath(classpath, splitRelativeCodePaths[i - 1]); } } else { Loading @@ -114,18 +125,35 @@ public final class DexoptUtils { String splitDependencyOnBase = encodeClassLoader( sharedLibrariesAndBaseClassPath, "dalvik.system.PathClassLoader"); SparseArray<int[]> splitDependencies = info.splitDependencies; // Note that not all splits have dependencies (e.g. configuration splits) // The splits without dependencies will have classLoaderContexts[config_split_index] // set to null after this step. for (int i = 1; i < splitDependencies.size(); i++) { getParentDependencies(splitDependencies.keyAt(i), splitClassLoaderEncodingCache, int splitIndex = splitDependencies.keyAt(i); if (pathsWithCode[splitIndex]) { // Compute the class loader context only for the splits with code. getParentDependencies(splitIndex, 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]); if (pathsWithCode[i]) { // If classLoaderContexts[i] is null it means that the split does not have // any dependency. In this case its context equals its declared class loader. classLoaderContexts[i] = classLoaderContexts[i] == null ? splitClassLoader : encodeClassLoaderChain(splitClassLoader, classLoaderContexts[i]); } else { // This is a split without code, it has no dependency and it is not compiled. // Its context will be null. classLoaderContexts[i] = null; } } } Loading services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java +126 −34 Original line number Diff line number Diff line Loading @@ -46,24 +46,46 @@ public class DexoptUtilsTest { private static final String DELEGATE_LAST_CLASS_LOADER_NAME = DelegateLastClassLoader.class.getName(); private ApplicationInfo createMockApplicationInfo(String baseClassLoader, boolean addSplits, private static class TestData { ApplicationInfo info; boolean[] pathsWithCode; } private TestData createMockApplicationInfo(String baseClassLoader, boolean addSplits, boolean addSplitDependencies) { 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; boolean[] pathsWithCode; if (!addSplits) { pathsWithCode = new boolean[] {true}; } else { pathsWithCode = new boolean[9]; Arrays.fill(pathsWithCode, true); pathsWithCode[7] = false; // config split if (addSplits) { ai.setSplitCodePaths(new String[]{ codeDir + "/base-1.dex", codeDir + "/base-2.dex", codeDir + "/base-3.dex", codeDir + "/base-4.dex", codeDir + "/base-5.dex", codeDir + "/base-6.dex"}); codeDir + "/base-6.dex", codeDir + "/config-split-7.dex", codeDir + "/feature-no-deps.dex"}); String[] splitClassLoaderNames = new String[]{ PATH_CLASS_LOADER_NAME, PATH_CLASS_LOADER_NAME, PATH_CLASS_LOADER_NAME, PATH_CLASS_LOADER_NAME, PATH_CLASS_LOADER_NAME, null, // A null class loader name should default to PathClassLoader. null, // The config split gets a null class loader. null}; // The feature split with no dependency and no specified class loader. if (addSplitDependencies) { ai.splitDependencies = new SparseArray<>(6 + 1); ai.splitDependencies = new SparseArray<>(splitClassLoaderNames.length + 1); ai.splitDependencies.put(0, new int[] {-1}); // base has no dependency ai.splitDependencies.put(1, new int[] {2}); // split 1 depends on 2 ai.splitDependencies.put(2, new int[] {4}); // split 2 depends on 4 Loading @@ -71,18 +93,24 @@ public class DexoptUtilsTest { ai.splitDependencies.put(4, new int[] {0}); // split 4 depends on base ai.splitDependencies.put(5, new int[] {0}); // split 5 depends on base ai.splitDependencies.put(6, new int[] {5}); // split 6 depends on 5 // Do not add the config split to the dependency list. // Do not add the feature split with no dependency to the dependency list. } } return ai; TestData data = new TestData(); data.info = ai; data.pathsWithCode = pathsWithCode; return data; } @Test public void testSplitChain() { ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true); TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true); String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(7, contexts.length); assertEquals(9, 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]); Loading @@ -91,15 +119,18 @@ public class DexoptUtilsTest { 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[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]); assertEquals(null, contexts[7]); // config split assertEquals("PCL[]", contexts[8]); // feature split with no dependency } @Test public void testSplitChainNoSplitDependencies() { ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false); TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false); String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(7, contexts.length); assertEquals(9, contexts.length); assertEquals("PCL[a.dex:b.dex]", contexts[0]); assertEquals("PCL[a.dex:b.dex:base.dex]", contexts[1]); assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex]", contexts[2]); Loading @@ -111,15 +142,21 @@ public class DexoptUtilsTest { 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]); assertEquals(null, contexts[7]); // config split assertEquals( "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]", contexts[8]); // feature split with no dependency } @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); TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true); data.info.privateFlags = data.info.privateFlags & (~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, null, data.pathsWithCode); assertEquals(7, contexts.length); assertEquals(9, contexts.length); assertEquals("PCL[]", contexts[0]); assertEquals("PCL[base.dex]", contexts[1]); assertEquals("PCL[base.dex:base-1.dex]", contexts[2]); Loading @@ -129,14 +166,20 @@ public class DexoptUtilsTest { assertEquals( "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]", contexts[6]); assertEquals(null, contexts[7]); // config split assertEquals( "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]", contexts[8]); // feature split with no dependency } @Test public void testSplitChainNoSharedLibraries() { ApplicationInfo ai = createMockApplicationInfo( TestData data = createMockApplicationInfo( DELEGATE_LAST_CLASS_LOADER_NAME, true, true); String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, null, data.pathsWithCode); assertEquals(7, contexts.length); assertEquals(9, contexts.length); assertEquals("PCL[]", contexts[0]); assertEquals("PCL[];PCL[base-2.dex];PCL[base-4.dex];PCL[base.dex]", contexts[1]); assertEquals("PCL[];PCL[base-4.dex];PCL[base.dex]", contexts[2]); Loading @@ -144,16 +187,19 @@ public class DexoptUtilsTest { assertEquals("PCL[];PCL[base.dex]", contexts[4]); assertEquals("PCL[];PCL[base.dex]", contexts[5]); assertEquals("PCL[];PCL[base-5.dex];PCL[base.dex]", contexts[6]); assertEquals(null, contexts[7]); // config split assertEquals("PCL[]", contexts[8]); // feature split with no dependency } @Test public void testSplitChainWithNullPrimaryClassLoader() { // A null classLoaderName should mean PathClassLoader. ApplicationInfo ai = createMockApplicationInfo(null, true, true); TestData data = createMockApplicationInfo(null, true, true); String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(7, contexts.length); assertEquals(9, 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-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]); Loading @@ -161,13 +207,16 @@ public class DexoptUtilsTest { 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[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]); assertEquals(null, contexts[7]); // config split assertEquals("PCL[]", contexts[8]); // feature split with no dependency } @Test public void tesNoSplits() { ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false); TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false); String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(1, contexts.length); assertEquals("PCL[a.dex:b.dex]", contexts[0]); Loading @@ -175,9 +224,10 @@ public class DexoptUtilsTest { @Test public void tesNoSplitsNullClassLoaderName() { ApplicationInfo ai = createMockApplicationInfo(null, false, false); TestData data = createMockApplicationInfo(null, false, false); String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(1, contexts.length); assertEquals("PCL[a.dex:b.dex]", contexts[0]); Loading @@ -185,10 +235,11 @@ public class DexoptUtilsTest { @Test public void tesNoSplitDelegateLast() { ApplicationInfo ai = createMockApplicationInfo( TestData data = createMockApplicationInfo( DELEGATE_LAST_CLASS_LOADER_NAME, false, false); String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(1, contexts.length); assertEquals("PCL[a.dex:b.dex]", contexts[0]); Loading @@ -196,8 +247,9 @@ public class DexoptUtilsTest { @Test public void tesNoSplitsNoSharedLibraries() { ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false); String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null); TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, null, data.pathsWithCode); assertEquals(1, contexts.length); assertEquals("PCL[]", contexts[0]); Loading @@ -205,14 +257,54 @@ public class DexoptUtilsTest { @Test public void tesNoSplitDelegateLastNoSharedLibraries() { ApplicationInfo ai = createMockApplicationInfo( TestData data = createMockApplicationInfo( DELEGATE_LAST_CLASS_LOADER_NAME, false, false); String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, null, data.pathsWithCode); assertEquals(1, contexts.length); assertEquals("PCL[]", contexts[0]); } @Test public void testContextWithNoCode() { TestData data = createMockApplicationInfo(null, true, false); Arrays.fill(data.pathsWithCode, false); String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(9, contexts.length); assertEquals(null, contexts[0]); assertEquals(null, contexts[1]); assertEquals(null, contexts[2]); assertEquals(null, contexts[3]); assertEquals(null, contexts[4]); assertEquals(null, contexts[5]); assertEquals(null, contexts[6]); assertEquals(null, contexts[7]); } @Test public void testContextBaseNoCode() { TestData data = createMockApplicationInfo(null, true, true); data.pathsWithCode[0] = false; String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(9, contexts.length); assertEquals(null, 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-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]); assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]); assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]); assertEquals(null, contexts[7]); } @Test public void testProcessContextForDexLoad() { List<String> classLoaders = Arrays.asList( Loading @@ -226,8 +318,8 @@ public class DexoptUtilsTest { String[] context = DexoptUtils.processContextForDexLoad(classLoaders, classPaths); assertNotNull(context); assertEquals(2, context.length); assertEquals("DLC[];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[0]); assertEquals("DLC[foo.dex];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[1]); assertEquals("PCL[];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[0]); assertEquals("PCL[foo.dex];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[1]); } @Test Loading Loading
core/java/android/content/pm/ApplicationInfo.java +4 −0 Original line number Diff line number Diff line Loading @@ -688,6 +688,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * Cycles do not exist because they are illegal and screened for during installation. * * May be null if no splits are installed, or if no dependencies exist between them. * * NOTE: Any change to the way split dependencies are stored must update the logic that * creates the class loader context for dexopt (DexoptUtils#getClassLoaderContexts). * * @hide */ public SparseArray<int[]> splitDependencies; Loading
services/core/java/com/android/server/pm/PackageDexOptimizer.java +13 −3 Original line number Diff line number Diff line Loading @@ -159,8 +159,13 @@ public class PackageDexOptimizer { // Get the class loader context dependencies. // For each code path in the package, this array contains the class loader context that // needs to be passed to dexopt in order to ensure correct optimizations. boolean[] pathsWithCode = new boolean[paths.size()]; pathsWithCode[0] = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0; for (int i = 1; i < paths.size(); i++) { pathsWithCode[i] = (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) != 0; } String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts( pkg.applicationInfo, sharedLibraries); pkg.applicationInfo, sharedLibraries, pathsWithCode); // Sanity check that we do not call dexopt with inconsistent data. if (paths.size() != classLoaderContexts.length) { Loading @@ -176,10 +181,15 @@ public class PackageDexOptimizer { int result = DEX_OPT_SKIPPED; 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)) { if (!pathsWithCode[i]) { continue; } if (classLoaderContexts[i] == null) { throw new IllegalStateException("Inconsistent information in the " + "package structure. A split is marked to contain code " + "but has no dependency listed. Index=" + i + " path=" + paths.get(i)); } // Append shared libraries with split dependencies for this split. String path = paths.get(i); if (options.getSplitName() != null) { Loading
services/core/java/com/android/server/pm/dex/DexoptUtils.java +36 −8 Original line number Diff line number Diff line Loading @@ -33,7 +33,9 @@ public final class DexoptUtils { /** * Creates the class loader context dependencies for each of the application code paths. * The returned array contains the class loader contexts that needs to be passed to dexopt in * order to ensure correct optimizations. * order to ensure correct optimizations. "Code" paths with no actual code, as specified by * {@param pathsWithCode}, are ignored and will have null as their context in the returned array * (configuration splits are an example of paths without code). * * A class loader context describes how the class loader chain should be built by dex2oat * in order to ensure that classes are resolved during compilation as they would be resolved Loading @@ -58,7 +60,8 @@ public final class DexoptUtils { * {@link android.app.LoadedApk#makePaths( * android.app.ActivityThread, boolean, ApplicationInfo, List, List)}. */ public static String[] getClassLoaderContexts(ApplicationInfo info, String[] sharedLibraries) { public static String[] getClassLoaderContexts(ApplicationInfo info, String[] sharedLibraries, boolean[] pathsWithCode) { // The base class loader context contains only the shared library. String sharedLibrariesClassPath = encodeClasspath(sharedLibraries); String baseApkContextClassLoader = encodeClassLoader( Loading @@ -84,7 +87,7 @@ public final class DexoptUtils { // 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 + splitRelativeCodePaths.length]; classLoaderContexts[0] = baseApkContextClassLoader; classLoaderContexts[0] = pathsWithCode[0] ? baseApkContextClassLoader : null; 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 Loading @@ -92,7 +95,15 @@ public final class DexoptUtils { // apk class loader (in the order of their definition). String classpath = sharedLibrariesAndBaseClassPath; for (int i = 1; i < classLoaderContexts.length; i++) { classLoaderContexts[i] = encodeClassLoader(classpath, "dalvik.system.PathClassLoader"); classLoaderContexts[i] = pathsWithCode[i] ? encodeClassLoader(classpath, "dalvik.system.PathClassLoader") : null; // Note that the splits with no code are not removed from the classpath computation. // i.e. split_n might get the split_n-1 in its classpath dependency even // if split_n-1 has no code. // The splits with no code do not matter for the runtime which ignores // apks without code when doing the classpath checks. As such we could actually // filter them but we don't do it in order to keep consistency with how the apps // are loaded. classpath = encodeClasspath(classpath, splitRelativeCodePaths[i - 1]); } } else { Loading @@ -114,18 +125,35 @@ public final class DexoptUtils { String splitDependencyOnBase = encodeClassLoader( sharedLibrariesAndBaseClassPath, "dalvik.system.PathClassLoader"); SparseArray<int[]> splitDependencies = info.splitDependencies; // Note that not all splits have dependencies (e.g. configuration splits) // The splits without dependencies will have classLoaderContexts[config_split_index] // set to null after this step. for (int i = 1; i < splitDependencies.size(); i++) { getParentDependencies(splitDependencies.keyAt(i), splitClassLoaderEncodingCache, int splitIndex = splitDependencies.keyAt(i); if (pathsWithCode[splitIndex]) { // Compute the class loader context only for the splits with code. getParentDependencies(splitIndex, 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]); if (pathsWithCode[i]) { // If classLoaderContexts[i] is null it means that the split does not have // any dependency. In this case its context equals its declared class loader. classLoaderContexts[i] = classLoaderContexts[i] == null ? splitClassLoader : encodeClassLoaderChain(splitClassLoader, classLoaderContexts[i]); } else { // This is a split without code, it has no dependency and it is not compiled. // Its context will be null. classLoaderContexts[i] = null; } } } Loading
services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java +126 −34 Original line number Diff line number Diff line Loading @@ -46,24 +46,46 @@ public class DexoptUtilsTest { private static final String DELEGATE_LAST_CLASS_LOADER_NAME = DelegateLastClassLoader.class.getName(); private ApplicationInfo createMockApplicationInfo(String baseClassLoader, boolean addSplits, private static class TestData { ApplicationInfo info; boolean[] pathsWithCode; } private TestData createMockApplicationInfo(String baseClassLoader, boolean addSplits, boolean addSplitDependencies) { 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; boolean[] pathsWithCode; if (!addSplits) { pathsWithCode = new boolean[] {true}; } else { pathsWithCode = new boolean[9]; Arrays.fill(pathsWithCode, true); pathsWithCode[7] = false; // config split if (addSplits) { ai.setSplitCodePaths(new String[]{ codeDir + "/base-1.dex", codeDir + "/base-2.dex", codeDir + "/base-3.dex", codeDir + "/base-4.dex", codeDir + "/base-5.dex", codeDir + "/base-6.dex"}); codeDir + "/base-6.dex", codeDir + "/config-split-7.dex", codeDir + "/feature-no-deps.dex"}); String[] splitClassLoaderNames = new String[]{ PATH_CLASS_LOADER_NAME, PATH_CLASS_LOADER_NAME, PATH_CLASS_LOADER_NAME, PATH_CLASS_LOADER_NAME, PATH_CLASS_LOADER_NAME, null, // A null class loader name should default to PathClassLoader. null, // The config split gets a null class loader. null}; // The feature split with no dependency and no specified class loader. if (addSplitDependencies) { ai.splitDependencies = new SparseArray<>(6 + 1); ai.splitDependencies = new SparseArray<>(splitClassLoaderNames.length + 1); ai.splitDependencies.put(0, new int[] {-1}); // base has no dependency ai.splitDependencies.put(1, new int[] {2}); // split 1 depends on 2 ai.splitDependencies.put(2, new int[] {4}); // split 2 depends on 4 Loading @@ -71,18 +93,24 @@ public class DexoptUtilsTest { ai.splitDependencies.put(4, new int[] {0}); // split 4 depends on base ai.splitDependencies.put(5, new int[] {0}); // split 5 depends on base ai.splitDependencies.put(6, new int[] {5}); // split 6 depends on 5 // Do not add the config split to the dependency list. // Do not add the feature split with no dependency to the dependency list. } } return ai; TestData data = new TestData(); data.info = ai; data.pathsWithCode = pathsWithCode; return data; } @Test public void testSplitChain() { ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true); TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true); String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(7, contexts.length); assertEquals(9, 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]); Loading @@ -91,15 +119,18 @@ public class DexoptUtilsTest { 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[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]); assertEquals(null, contexts[7]); // config split assertEquals("PCL[]", contexts[8]); // feature split with no dependency } @Test public void testSplitChainNoSplitDependencies() { ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false); TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false); String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(7, contexts.length); assertEquals(9, contexts.length); assertEquals("PCL[a.dex:b.dex]", contexts[0]); assertEquals("PCL[a.dex:b.dex:base.dex]", contexts[1]); assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex]", contexts[2]); Loading @@ -111,15 +142,21 @@ public class DexoptUtilsTest { 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]); assertEquals(null, contexts[7]); // config split assertEquals( "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]", contexts[8]); // feature split with no dependency } @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); TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true); data.info.privateFlags = data.info.privateFlags & (~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, null, data.pathsWithCode); assertEquals(7, contexts.length); assertEquals(9, contexts.length); assertEquals("PCL[]", contexts[0]); assertEquals("PCL[base.dex]", contexts[1]); assertEquals("PCL[base.dex:base-1.dex]", contexts[2]); Loading @@ -129,14 +166,20 @@ public class DexoptUtilsTest { assertEquals( "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]", contexts[6]); assertEquals(null, contexts[7]); // config split assertEquals( "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]", contexts[8]); // feature split with no dependency } @Test public void testSplitChainNoSharedLibraries() { ApplicationInfo ai = createMockApplicationInfo( TestData data = createMockApplicationInfo( DELEGATE_LAST_CLASS_LOADER_NAME, true, true); String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, null, data.pathsWithCode); assertEquals(7, contexts.length); assertEquals(9, contexts.length); assertEquals("PCL[]", contexts[0]); assertEquals("PCL[];PCL[base-2.dex];PCL[base-4.dex];PCL[base.dex]", contexts[1]); assertEquals("PCL[];PCL[base-4.dex];PCL[base.dex]", contexts[2]); Loading @@ -144,16 +187,19 @@ public class DexoptUtilsTest { assertEquals("PCL[];PCL[base.dex]", contexts[4]); assertEquals("PCL[];PCL[base.dex]", contexts[5]); assertEquals("PCL[];PCL[base-5.dex];PCL[base.dex]", contexts[6]); assertEquals(null, contexts[7]); // config split assertEquals("PCL[]", contexts[8]); // feature split with no dependency } @Test public void testSplitChainWithNullPrimaryClassLoader() { // A null classLoaderName should mean PathClassLoader. ApplicationInfo ai = createMockApplicationInfo(null, true, true); TestData data = createMockApplicationInfo(null, true, true); String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(7, contexts.length); assertEquals(9, 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-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]); Loading @@ -161,13 +207,16 @@ public class DexoptUtilsTest { 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[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]); assertEquals(null, contexts[7]); // config split assertEquals("PCL[]", contexts[8]); // feature split with no dependency } @Test public void tesNoSplits() { ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false); TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false); String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(1, contexts.length); assertEquals("PCL[a.dex:b.dex]", contexts[0]); Loading @@ -175,9 +224,10 @@ public class DexoptUtilsTest { @Test public void tesNoSplitsNullClassLoaderName() { ApplicationInfo ai = createMockApplicationInfo(null, false, false); TestData data = createMockApplicationInfo(null, false, false); String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(1, contexts.length); assertEquals("PCL[a.dex:b.dex]", contexts[0]); Loading @@ -185,10 +235,11 @@ public class DexoptUtilsTest { @Test public void tesNoSplitDelegateLast() { ApplicationInfo ai = createMockApplicationInfo( TestData data = createMockApplicationInfo( DELEGATE_LAST_CLASS_LOADER_NAME, false, false); String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts(ai, sharedLibrary); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(1, contexts.length); assertEquals("PCL[a.dex:b.dex]", contexts[0]); Loading @@ -196,8 +247,9 @@ public class DexoptUtilsTest { @Test public void tesNoSplitsNoSharedLibraries() { ApplicationInfo ai = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false); String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null); TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, null, data.pathsWithCode); assertEquals(1, contexts.length); assertEquals("PCL[]", contexts[0]); Loading @@ -205,14 +257,54 @@ public class DexoptUtilsTest { @Test public void tesNoSplitDelegateLastNoSharedLibraries() { ApplicationInfo ai = createMockApplicationInfo( TestData data = createMockApplicationInfo( DELEGATE_LAST_CLASS_LOADER_NAME, false, false); String[] contexts = DexoptUtils.getClassLoaderContexts(ai, null); String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, null, data.pathsWithCode); assertEquals(1, contexts.length); assertEquals("PCL[]", contexts[0]); } @Test public void testContextWithNoCode() { TestData data = createMockApplicationInfo(null, true, false); Arrays.fill(data.pathsWithCode, false); String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(9, contexts.length); assertEquals(null, contexts[0]); assertEquals(null, contexts[1]); assertEquals(null, contexts[2]); assertEquals(null, contexts[3]); assertEquals(null, contexts[4]); assertEquals(null, contexts[5]); assertEquals(null, contexts[6]); assertEquals(null, contexts[7]); } @Test public void testContextBaseNoCode() { TestData data = createMockApplicationInfo(null, true, true); data.pathsWithCode[0] = false; String[] sharedLibrary = new String[] {"a.dex", "b.dex"}; String[] contexts = DexoptUtils.getClassLoaderContexts( data.info, sharedLibrary, data.pathsWithCode); assertEquals(9, contexts.length); assertEquals(null, 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-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]); assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]); assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]); assertEquals(null, contexts[7]); } @Test public void testProcessContextForDexLoad() { List<String> classLoaders = Arrays.asList( Loading @@ -226,8 +318,8 @@ public class DexoptUtilsTest { String[] context = DexoptUtils.processContextForDexLoad(classLoaders, classPaths); assertNotNull(context); assertEquals(2, context.length); assertEquals("DLC[];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[0]); assertEquals("DLC[foo.dex];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[1]); assertEquals("PCL[];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[0]); assertEquals("PCL[foo.dex];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[1]); } @Test Loading