Loading core/java/android/content/pm/ApplicationInfo.java +4 −0 Original line number Diff line number Diff line Loading @@ -718,6 +718,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 @@ -147,8 +147,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 @@ -164,10 +169,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 @@ -35,7 +35,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 @@ -60,7 +62,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 @@ -86,7 +89,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 @@ -94,7 +97,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, info.classLoaderName); classLoaderContexts[i] = pathsWithCode[i] ? encodeClassLoader(classpath, info.classLoaderName) : 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 @@ -116,18 +127,35 @@ public final class DexoptUtils { String splitDependencyOnBase = encodeClassLoader( sharedLibrariesAndBaseClassPath, info.classLoaderName); 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("", info.splitClassLoaderNames[i - 1]); 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 +116 −31 Original line number Diff line number Diff line Loading @@ -46,22 +46,35 @@ 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.classLoaderName = baseClassLoader; 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"}); ai.splitClassLoaderNames = new String[]{ DELEGATE_LAST_CLASS_LOADER_NAME, Loading @@ -69,7 +82,9 @@ public class DexoptUtilsTest { PATH_CLASS_LOADER_NAME, DEX_CLASS_LOADER_NAME, PATH_CLASS_LOADER_NAME, null}; // A null class loader name should default to PathClassLoader. 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<>(ai.splitClassLoaderNames.length + 1); ai.splitDependencies.put(0, new int[] {-1}); // base has no dependency Loading @@ -79,18 +94,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("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]); Loading @@ -99,15 +120,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 @@ -119,15 +143,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 @@ -137,14 +167,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("DLC[]", contexts[0]); assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];DLC[base.dex]", contexts[1]); assertEquals("DLC[];PCL[base-4.dex];DLC[base.dex]", contexts[2]); Loading @@ -152,16 +188,19 @@ public class DexoptUtilsTest { assertEquals("PCL[];DLC[base.dex]", contexts[4]); assertEquals("PCL[];DLC[base.dex]", contexts[5]); assertEquals("PCL[];PCL[base-5.dex];DLC[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("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]); assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]); Loading @@ -169,13 +208,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 @@ -183,9 +225,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 @@ -193,10 +236,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("DLC[a.dex:b.dex]", contexts[0]); Loading @@ -204,8 +248,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 @@ -213,14 +258,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("DLC[]", 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("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]); assertEquals("DLC[];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 Loading
core/java/android/content/pm/ApplicationInfo.java +4 −0 Original line number Diff line number Diff line Loading @@ -718,6 +718,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 @@ -147,8 +147,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 @@ -164,10 +169,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 @@ -35,7 +35,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 @@ -60,7 +62,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 @@ -86,7 +89,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 @@ -94,7 +97,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, info.classLoaderName); classLoaderContexts[i] = pathsWithCode[i] ? encodeClassLoader(classpath, info.classLoaderName) : 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 @@ -116,18 +127,35 @@ public final class DexoptUtils { String splitDependencyOnBase = encodeClassLoader( sharedLibrariesAndBaseClassPath, info.classLoaderName); 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("", info.splitClassLoaderNames[i - 1]); 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 +116 −31 Original line number Diff line number Diff line Loading @@ -46,22 +46,35 @@ 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.classLoaderName = baseClassLoader; 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"}); ai.splitClassLoaderNames = new String[]{ DELEGATE_LAST_CLASS_LOADER_NAME, Loading @@ -69,7 +82,9 @@ public class DexoptUtilsTest { PATH_CLASS_LOADER_NAME, DEX_CLASS_LOADER_NAME, PATH_CLASS_LOADER_NAME, null}; // A null class loader name should default to PathClassLoader. 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<>(ai.splitClassLoaderNames.length + 1); ai.splitDependencies.put(0, new int[] {-1}); // base has no dependency Loading @@ -79,18 +94,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("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]); Loading @@ -99,15 +120,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 @@ -119,15 +143,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 @@ -137,14 +167,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("DLC[]", contexts[0]); assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];DLC[base.dex]", contexts[1]); assertEquals("DLC[];PCL[base-4.dex];DLC[base.dex]", contexts[2]); Loading @@ -152,16 +188,19 @@ public class DexoptUtilsTest { assertEquals("PCL[];DLC[base.dex]", contexts[4]); assertEquals("PCL[];DLC[base.dex]", contexts[5]); assertEquals("PCL[];PCL[base-5.dex];DLC[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("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]); assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]); Loading @@ -169,13 +208,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 @@ -183,9 +225,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 @@ -193,10 +236,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("DLC[a.dex:b.dex]", contexts[0]); Loading @@ -204,8 +248,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 @@ -213,14 +258,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("DLC[]", 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("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]); assertEquals("DLC[];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