Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2015 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.Method;
     19 import java.util.List;
     20 
     21 class MyClassLoader extends ClassLoader {
     22   MyClassLoader() throws Exception {
     23     super(MyClassLoader.class.getClassLoader());
     24 
     25     // Some magic to get access to the pathList field of BaseDexClassLoader.
     26     ClassLoader loader = getClass().getClassLoader();
     27     Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
     28     Field f = baseDexClassLoader.getDeclaredField("pathList");
     29     f.setAccessible(true);
     30     Object pathList = f.get(loader);
     31 
     32     // Some magic to get access to the dexField field of pathList.
     33     f = pathList.getClass().getDeclaredField("dexElements");
     34     f.setAccessible(true);
     35     dexElements = (Object[]) f.get(pathList);
     36     dexFileField = dexElements[0].getClass().getDeclaredField("dexFile");
     37     dexFileField.setAccessible(true);
     38   }
     39 
     40   Object[] dexElements;
     41   Field dexFileField;
     42 
     43   static ClassLoader level1ClassLoader;
     44 
     45   protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
     46     if (this != level1ClassLoader) {
     47       if (className.equals("Level1")) {
     48         return level1ClassLoader.loadClass(className);
     49       } else if (className.equals("Level2")) {
     50         throw new ClassNotFoundException("None of my methods require Level2!");
     51       } else if (!className.equals("LoadedByMyClassLoader")) {
     52         // We're only going to handle LoadedByMyClassLoader.
     53         return getParent().loadClass(className);
     54       }
     55     } else {
     56       if (className != "Level1" && className != "Level2") {
     57         return getParent().loadClass(className);
     58       }
     59     }
     60 
     61     // Mimic what DexPathList.findClass is doing.
     62     try {
     63       for (Object element : dexElements) {
     64         Object dex = dexFileField.get(element);
     65         Method method = dex.getClass().getDeclaredMethod(
     66             "loadClassBinaryName", String.class, ClassLoader.class, List.class);
     67 
     68         if (dex != null) {
     69           Class<?> clazz = (Class<?>)method.invoke(dex, className, this, null);
     70           if (clazz != null) {
     71             return clazz;
     72           }
     73         }
     74       }
     75     } catch (Exception e) { /* Ignore */ }
     76     return null;
     77   }
     78 }
     79 
     80 class LoadedByMyClassLoader {
     81   public static void bar() {
     82     Level1.$inline$bar();
     83   }
     84 }
     85 
     86 class Main {
     87   public static void main(String[] args) throws Exception {
     88     System.loadLibrary(args[0]);
     89     // Clone resolved methods, to restore the original version just
     90     // before we walk the stack in $noinline$bar.
     91     savedResolvedMethods = cloneResolvedMethods(Main.class);
     92 
     93     MyClassLoader o = new MyClassLoader();
     94     MyClassLoader.level1ClassLoader = new MyClassLoader();
     95     Class<?> foo = o.loadClass("LoadedByMyClassLoader");
     96     Method m = foo.getDeclaredMethod("bar");
     97     try {
     98       m.invoke(null);
     99     } catch (Error e) { /* Ignore */ }
    100   }
    101 
    102   public static void $inline$bar() {
    103   }
    104 
    105   public static void $noinline$bar() {
    106     try {
    107       // Be evil and clear all dex cache entries.
    108       Field f = Class.class.getDeclaredField("dexCache");
    109       f.setAccessible(true);
    110       Object dexCache = f.get(Main.class);
    111       f = dexCache.getClass().getDeclaredField("resolvedTypes");
    112       f.setAccessible(true);
    113       Object[] array = (Object[]) f.get(dexCache);
    114       for (int i = 0; i < array.length; i++) {
    115         array[i] = null;
    116       }
    117       restoreResolvedMethods(Main.class, savedResolvedMethods);
    118     } catch (Throwable t) { /* Ignore */ }
    119 
    120     // This will walk the stack, trying to resolve methods in it.
    121     // Because we cleared dex cache entries, we will have to find
    122     // classes again, which require to use the correct class loader
    123     // in the presence of inlining.
    124     new Exception().printStackTrace(System.out);
    125   }
    126   static Object savedResolvedMethods;
    127 
    128   static native Object cloneResolvedMethods(Class<?> cls);
    129   static native void restoreResolvedMethods(Class<?> cls, Object saved);
    130 }
    131