Merge "Fix shared libraries not being reported via Reporter" am: 3098ae7952 am: ef5ce9baec
Change-Id: Icd2a7ba53b0de387deb031a448003a10729cee7f
diff --git a/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java b/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
index d0a449b..4a9e8631 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.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 @@
* 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 @@
@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 1642a11..ba7c232 100644
--- a/luni/src/test/java/libcore/dalvik/system/BaseDexClassLoaderTest.java
+++ b/luni/src/test/java/libcore/dalvik/system/BaseDexClassLoaderTest.java
@@ -33,7 +33,9 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Enumeration;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -48,18 +50,15 @@
@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 +121,9 @@
BaseDexClassLoader cl1 = new PathClassLoader(jar.getPath(),
ClassLoader.getSystemClassLoader());
- // Verify the reported data.
- assertEquals(2, reporter.loadedDexPaths.size());
- assertEquals(2, reporter.classLoaders.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));
+ // Verify the reported data. The only class loader context should be two empty PCLs
+ // (the system class loader is a PCL)
+ assertEquals(Map.of(jar.getPath(), "PCL[];PCL[]"), reporter.loadedDexMapping);
}
@Test
@@ -141,16 +132,10 @@
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 +143,7 @@
BaseDexClassLoader cl1 = new PathClassLoader(jar.getPath(),
ClassLoader.getSystemClassLoader());
- assertEquals(2, reporter.loadedDexPaths.size());
- assertEquals(2, reporter.classLoaders.size());
+ assertEquals(Map.of(jar.getPath(), "PCL[];PCL[]"), reporter.loadedDexMapping);
// Check we don't report after the reporter is unregistered.
unregisterReporter();
@@ -169,8 +153,67 @@
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());
+
+ // The first class loader context should be two empty PCLs (the system class loader is a
+ // PCL) and the second should be the same, but the bottom classloader contains the first
+ // jar.
+ assertEquals(Map.of(jar.getPath(), "PCL[];PCL[]",
+ jar2.getPath(), "PCL[" + jar.getPath() + "];PCL[]"),
+ reporter.loadedDexMapping);
+ }
+
+ @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);
+
+ // Verify the loaded dex file contains jar2 encoded as a shared library in its encoded class
+ // loader context.
+ assertEquals(
+ Map.of(jar.getPath(), "PCL[]{PCL[" + jar2.getPath() + "];PCL[]};PCL[]"),
+ reporter.loadedDexMapping);
+ }
+
+ @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);
+
+ final String contextSuffix = "{PCL[" + sharedJarPath + "];PCL[]};PCL[]";
+
+ assertEquals(Map.of(jar.getPath(), "PCL[]" + contextSuffix,
+ jar2.getPath(), "PCL[" + jar.getPath() + "]" + contextSuffix),
+ reporter.loadedDexMapping);
+ }
+
+ @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 707e3a3..1ca16bc 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 @@
}
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 {