summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java53
-rw-r--r--luni/src/test/java/libcore/dalvik/system/BaseDexClassLoaderTest.java171
-rw-r--r--mmodules/core_platform_api/api/platform/current-api.txt2
3 files changed, 162 insertions, 64 deletions
diff --git a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
index d0a449bdb7..4a9e863118 100644
--- a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
+++ b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
@@ -24,8 +24,11 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Enumeration;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import sun.misc.CompoundEnumeration;
/**
@@ -134,28 +137,17 @@ public class BaseDexClassLoader extends ClassLoader {
* Reports the current class loader chain to the registered {@code reporter}.
*/
private void reportClassLoaderChain() {
- ArrayList<ClassLoader> classLoadersChain = new ArrayList<>();
- ArrayList<String> classPaths = new ArrayList<>();
-
- classLoadersChain.add(this);
- classPaths.add(String.join(File.pathSeparator, pathList.getDexPaths()));
-
- ClassLoader bootClassLoader = ClassLoader.getSystemClassLoader().getParent();
- ClassLoader current = getParent();
-
- while (current != null && current != bootClassLoader) {
- classLoadersChain.add(current);
- if (current instanceof BaseDexClassLoader) {
- BaseDexClassLoader bdcCurrent = (BaseDexClassLoader) current;
- classPaths.add(String.join(File.pathSeparator, bdcCurrent.pathList.getDexPaths()));
- } else {
- // We can't determine the classpath for arbitrary class loaders.
- classPaths.add(null);
- }
- current = current.getParent();
+ String[] classPathAndClassLoaderContexts = computeClassLoaderContextsNative();
+ if (classPathAndClassLoaderContexts.length == 0) {
+ return;
}
-
- reporter.report(classLoadersChain, classPaths);
+ Map<String, String> dexFileMapping =
+ new HashMap<>(classPathAndClassLoaderContexts.length / 2);
+ for (int i = 0; i < classPathAndClassLoaderContexts.length; i += 2) {
+ dexFileMapping.put(classPathAndClassLoaderContexts[i],
+ classPathAndClassLoaderContexts[i + 1]);
+ }
+ reporter.report(Collections.unmodifiableMap(dexFileMapping));
}
/**
@@ -373,19 +365,16 @@ public class BaseDexClassLoader extends ClassLoader {
@libcore.api.CorePlatformApi
public interface Reporter {
/**
- * Reports the construction of a BaseDexClassLoader and provides information about the
- * class loader chain.
+ * Reports the construction of a BaseDexClassLoader and provides opaque information about
+ * the class loader chain. For example, if the childmost ClassLoader in the chain:
+ * {@quote BaseDexClassLoader { foo.dex } -> BaseDexClassLoader { base.apk }
+ * -> BootClassLoader } was just initialized then the load of {@code "foo.dex"} would be
+ * reported with a classLoaderContext of {@code "PCL[];PCL[base.apk]"}.
*
- * @param classLoadersChain the chain of class loaders used during the construction of the
- * class loader. The first element is the BaseDexClassLoader being constructed,
- * the second element is its parent, and so on.
- * @param classPaths the class paths of the class loaders present in
- * {@param classLoadersChain}. The first element corresponds to the first class
- * loader and so on. A classpath is represented as a list of dex files separated by
- * {@code File.pathSeparator}. If the class loader is not a BaseDexClassLoader the
- * classpath will be null.
+ * @param contextsMap A map from dex file paths to the class loader context used to load
+ * each dex file.
*/
@libcore.api.CorePlatformApi
- void report(List<ClassLoader> classLoadersChain, List<String> classPaths);
+ void report(Map<String, String> contextsMap);
}
}
diff --git a/luni/src/test/java/libcore/dalvik/system/BaseDexClassLoaderTest.java b/luni/src/test/java/libcore/dalvik/system/BaseDexClassLoaderTest.java
index 1642a11f47..7d3b6f2b87 100644
--- a/luni/src/test/java/libcore/dalvik/system/BaseDexClassLoaderTest.java
+++ b/luni/src/test/java/libcore/dalvik/system/BaseDexClassLoaderTest.java
@@ -34,6 +34,7 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -48,18 +49,15 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public final class BaseDexClassLoaderTest {
private static class Reporter implements BaseDexClassLoader.Reporter {
- public final List<ClassLoader> classLoaders = new ArrayList<>();
- public final List<String> loadedDexPaths = new ArrayList<>();
+ public final Map<String, String> loadedDexMapping = new HashMap<String, String>();
@Override
- public void report(List<ClassLoader> loaders, List<String> dexPaths) {
- classLoaders.addAll(loaders);
- loadedDexPaths.addAll(dexPaths);
+ public void report(Map<String, String> contextMap) {
+ loadedDexMapping.putAll(contextMap);
}
void reset() {
- classLoaders.clear();
- loadedDexPaths.clear();
+ loadedDexMapping.clear();
}
}
@@ -122,17 +120,15 @@ public final class BaseDexClassLoaderTest {
BaseDexClassLoader cl1 = new PathClassLoader(jar.getPath(),
ClassLoader.getSystemClassLoader());
- // Verify the reported data.
- assertEquals(2, reporter.loadedDexPaths.size());
- assertEquals(2, reporter.classLoaders.size());
+ assertEquals(1, reporter.loadedDexMapping.size());
- // First class loader should be the one loading the files
- assertEquals(jar.getPath(), reporter.loadedDexPaths.get(0));
- assertEquals(cl1, reporter.classLoaders.get(0));
-
- // Second class loader should be the system class loader.
- // Don't check the actual classpath as that might vary based on system properties.
- assertEquals(ClassLoader.getSystemClassLoader(), reporter.classLoaders.get(1));
+ String[] contexts = reporter.loadedDexMapping.get(jar.getPath()).split(";");
+ assertEquals(2, contexts.length);
+ // Verify the context for the loaded dex files.
+ assertEquals("PCL[]", contexts[0]);
+ // We cannot fully verify the context of the system class loader because its classpath
+ // may vary based on system properties and whether or not we are in a test environment.
+ assertTrue(contexts[1].startsWith("PCL["));
}
@Test
@@ -141,16 +137,10 @@ public final class BaseDexClassLoaderTest {
ClassLoader unknownLoader = new ClassLoader(ClassLoader.getSystemClassLoader()) {};
BaseDexClassLoader cl1 = new PathClassLoader(jar.getPath(), unknownLoader);
- assertEquals(3, reporter.loadedDexPaths.size());
- assertEquals(3, reporter.classLoaders.size());
-
- assertEquals(jar.getPath(), reporter.loadedDexPaths.get(0));
- assertEquals(cl1, reporter.classLoaders.get(0));
-
- assertNull(reporter.loadedDexPaths.get(1));
- assertEquals(unknownLoader, reporter.classLoaders.get(1));
-
- assertEquals(ClassLoader.getSystemClassLoader(), reporter.classLoaders.get(2));
+ // Verify the dex path gets reported, but with no class loader context due to the foreign
+ // class loader.
+ assertEquals(Map.of(jar.getPath(), "=UnsupportedClassLoaderContext="),
+ reporter.loadedDexMapping);
}
@Test
@@ -158,8 +148,15 @@ public final class BaseDexClassLoaderTest {
BaseDexClassLoader cl1 = new PathClassLoader(jar.getPath(),
ClassLoader.getSystemClassLoader());
- assertEquals(2, reporter.loadedDexPaths.size());
- assertEquals(2, reporter.classLoaders.size());
+ assertEquals(1, reporter.loadedDexMapping.size());
+
+ String[] contexts = reporter.loadedDexMapping.get(jar.getPath()).split(";");
+ assertEquals(2, contexts.length);
+ // Verify the context for the loaded dex files.
+ assertEquals("PCL[]", contexts[0]);
+ // We cannot fully verify the context of the system class loader because its classpath
+ // may vary based on system properties and whether or not we are in a test environment.
+ assertTrue(contexts[1].startsWith("PCL["));
// Check we don't report after the reporter is unregistered.
unregisterReporter();
@@ -169,8 +166,120 @@ public final class BaseDexClassLoaderTest {
BaseDexClassLoader cl2 = new PathClassLoader(jar.getPath(), pcl);
// Verify nothing reported
- assertEquals(0, reporter.loadedDexPaths.size());
- assertEquals(0, reporter.classLoaders.size());
+ assertEquals(Map.<String, String>of(), reporter.loadedDexMapping);
+ }
+
+ @Test
+ public void testReporting_multipleJars() throws Exception {
+ // Load the jar file using a PathClassLoader.
+ BaseDexClassLoader cl1 = new PathClassLoader(
+ String.join(File.pathSeparator, jar.getPath(), jar2.getPath()),
+ ClassLoader.getSystemClassLoader());
+
+ assertEquals(2, reporter.loadedDexMapping.size());
+ // Verify the first jar.
+ String[] contexts = reporter.loadedDexMapping.get(jar.getPath()).split(";");
+ assertEquals(2, contexts.length);
+ // Verify the context for the loaded dex files.
+ assertEquals("PCL[]", contexts[0]);
+ // We cannot fully verify the context of the system class loader because its classpath
+ // may vary based on system properties and whether or not we are in a test environment.
+ assertTrue(contexts[1].startsWith("PCL["));
+
+ // Verify the second jar.
+ String[] contexts2 = reporter.loadedDexMapping.get(jar2.getPath()).split(";");
+ assertEquals(2, contexts2.length);
+ // Verify the context for the loaded dex files.
+ assertEquals("PCL[" + jar.getPath() + "]", contexts2[0]);
+ // We cannot fully verify the context of the system class loader because its classpath
+ // may vary based on system properties and whether or not we are in a test environment.
+ assertTrue(contexts2[1].startsWith("PCL["));
+ }
+
+
+ /**
+ * Separates the system class loader context from the rest of the context.
+ * Returns an array of 2 elements, where index 0 is the application class loader context
+ * without the system class loader and index 0 is the system class loader context.
+ */
+ private String[] separateSystemClassLoaderContext(String context) {
+ int clcSeparatorIndex = context.lastIndexOf(";");
+ String jarContext = context.substring(0, clcSeparatorIndex);
+ String systemClassLoaderContext = context.substring(clcSeparatorIndex + 1);
+ return new String[] {jarContext, systemClassLoaderContext};
+ }
+
+ @Test
+ public void testReporting_withSharedLibraries() throws Exception {
+ final ClassLoader parent = ClassLoader.getSystemClassLoader();
+ final ClassLoader sharedLoaders[] = new ClassLoader[] {
+ new PathClassLoader(jar2.getPath(), /* librarySearchPath */ null, parent),
+ };
+ // Reset so we don't get load reports from creating the shared library CL
+ reporter.reset();
+
+ BaseDexClassLoader bdcl = new PathClassLoader(jar.getPath(), null, parent, sharedLoaders);
+
+ assertEquals(1, reporter.loadedDexMapping.size());
+
+ String[] contexts = separateSystemClassLoaderContext(
+ reporter.loadedDexMapping.get(jar.getPath()));
+ // We cannot fully verify the context of the system class loader because its classpath
+ // may vary based on system properties and whether or not we are in a test environment.
+ assertTrue(contexts[1].startsWith("PCL["));
+ // Verify the context for the loaded dex files. The system class loader should be part
+ // of the shared library class loader.
+ assertEquals("PCL[]{PCL[" + jar2.getPath() + "];" + contexts[1] + "}",
+ contexts[0]);
+ }
+
+ @Test
+ public void testReporting_multipleJars_withSharedLibraries() throws Exception {
+ final ClassLoader parent = ClassLoader.getSystemClassLoader();
+ final String sharedJarPath = resourcesMap.get("parent.jar").getAbsolutePath();
+ final ClassLoader sharedLoaders[] = new ClassLoader[] {
+ new PathClassLoader(sharedJarPath, /* librarySearchPath */ null, parent),
+ };
+ // Reset so we don't get load reports from creating the shared library CL
+ reporter.reset();
+
+ BaseDexClassLoader bdcl = new PathClassLoader(
+ String.join(File.pathSeparator, jar.getPath(), jar2.getPath()),
+ null, parent, sharedLoaders);
+
+ assertEquals(2, reporter.loadedDexMapping.size());
+
+
+ // Verify the first jar.
+ String[] contexts = separateSystemClassLoaderContext(
+ reporter.loadedDexMapping.get(jar.getPath()));
+ String contextSuffix = "{PCL[" + sharedJarPath + "];" + contexts[1] + "}";
+
+ // We cannot fully verify the context of the system class loader because its classpath
+ // may vary based on system properties and whether or not we are in a test environment.
+ assertTrue(contexts[1].startsWith("PCL["));
+ // Verify the context for the loaded dex files.
+ assertEquals("PCL[]" + contextSuffix, contexts[0]);
+ // We cannot fully verify the context of the system class loader because its classpath
+ // may vary based on system properties and whether or not we are in a test environment.
+ assertTrue(contexts[1].startsWith("PCL["));
+
+ // Verify the second jar.
+ String[] contexts2 = separateSystemClassLoaderContext(
+ reporter.loadedDexMapping.get(jar2.getPath()));
+ String contextSuffix2 = "{PCL[" + sharedJarPath + "];" + contexts2[1] + "}";
+
+ // Verify the context for the loaded dex files.
+ assertEquals("PCL[" + jar.getPath() + "]" + contextSuffix2, contexts2[0]);
+ // We cannot fully verify the context of the system class loader because its classpath
+ // may vary based on system properties and whether or not we are in a test environment.
+ assertTrue(contexts2[1].startsWith("PCL[")) ;
+ }
+
+ @Test
+ public void testReporting_emptyPath() throws Exception {
+ BaseDexClassLoader cl1 = new PathClassLoader("", ClassLoader.getSystemClassLoader());
+ assertEquals(Map.<String, String>of(), reporter.loadedDexMapping);
}
/* package */ static List<String> readResources(ClassLoader cl, String resourceName)
diff --git a/mmodules/core_platform_api/api/platform/current-api.txt b/mmodules/core_platform_api/api/platform/current-api.txt
index 707e3a3b35..1ca16bc169 100644
--- a/mmodules/core_platform_api/api/platform/current-api.txt
+++ b/mmodules/core_platform_api/api/platform/current-api.txt
@@ -535,7 +535,7 @@ package dalvik.system {
}
public static interface BaseDexClassLoader.Reporter {
- method public void report(java.util.List<java.lang.ClassLoader>, java.util.List<java.lang.String>);
+ method public void report(java.util.Map<java.lang.String,java.lang.String>);
}
public final class BlockGuard {