1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import java.lang.reflect.Field;
18 import java.lang.reflect.InvocationTargetException;
19 import java.lang.reflect.Method;
20 import java.util.List;
21 
22 class MyClassLoader extends ClassLoader {
MyClassLoader()23   MyClassLoader() throws Exception {
24     super(MyClassLoader.class.getClassLoader());
25 
26     // Some magic to get access to the pathList field of BaseDexClassLoader.
27     ClassLoader loader = getClass().getClassLoader();
28     Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
29     Field f = baseDexClassLoader.getDeclaredField("pathList");
30     f.setAccessible(true);
31     Object pathList = f.get(loader);
32 
33     // Some magic to get access to the dexField field of pathList.
34     f = pathList.getClass().getDeclaredField("dexElements");
35     f.setAccessible(true);
36     dexElements = (Object[]) f.get(pathList);
37     dexFileField = dexElements[0].getClass().getDeclaredField("dexFile");
38     dexFileField.setAccessible(true);
39   }
40 
41   Object[] dexElements;
42   Field dexFileField;
43 
loadClass(String className, boolean resolve)44   protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
45     // Mimic what DexPathList.findClass is doing.
46     try {
47       for (Object element : dexElements) {
48         Object dex = dexFileField.get(element);
49         Method method = dex.getClass().getDeclaredMethod(
50             "loadClassBinaryName", String.class, ClassLoader.class, List.class);
51 
52         if (dex != null) {
53           Class<?> clazz = (Class<?>)method.invoke(dex, className, this, null);
54           if (clazz != null) {
55             return clazz;
56           }
57         }
58       }
59     } catch (InvocationTargetException ite) {
60       throw new ClassNotFoundException(className, ite.getCause());
61     } catch (Exception e) {
62       throw new Error(e);
63     }
64     return getParent().loadClass(className);
65   }
66 }
67 
68 public class Main {
main(String[] args)69   public static void main(String[] args) throws Exception {
70     MyClassLoader o = new MyClassLoader();
71     try {
72       Class<?> foo = o.loadClass("Main");
73       throw new Error("Unreachable");
74     } catch (ClassNotFoundException cnfe) {
75       boolean unexpected = false;
76       if (!(cnfe.getCause() instanceof InternalError)) {
77         unexpected = true;
78       } else {
79         String message = cnfe.getCause().getMessage();
80         unexpected = !message.startsWith("Attempt to register dex file ") ||
81                      !message.endsWith(" with multiple class loaders");
82       }
83       if (unexpected) {
84         cnfe.getCause().printStackTrace(System.out);
85       }
86     }
87   }
88 }
89