Home | History | Annotate | Download | only in src
      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 {
     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 
     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 {
     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