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