1 // Copyright 2008 The Android Open Source Project 2 3 import java.io.File; 4 import java.io.FileNotFoundException; 5 import java.io.IOException; 6 import java.io.RandomAccessFile; 7 import java.lang.reflect.Constructor; 8 import java.lang.reflect.Method; 9 import java.lang.reflect.InvocationTargetException; 10 11 /** 12 * A class loader with atypical behavior: we try to load a private 13 * class implementation before asking the system or boot loader. This 14 * is used to create multiple classes with identical names in a single VM. 15 * 16 * If DexFile is available, we use that; if not, we assume we're not in 17 * Dalvik and instantiate the class with defineClass(). 18 * 19 * The location of the DEX files and class data is dependent upon the 20 * test framework. 21 */ 22 public class FancyLoader extends ClassLoader { 23 /* this is where the "alternate" .class files live */ 24 static final String CLASS_PATH = "classes-ex/"; 25 26 /* this is the "alternate" DEX/Jar file */ 27 static final String DEX_FILE = "test-ex.jar"; 28 29 /* on Dalvik, this is a DexFile; otherwise, it's null */ 30 private Class mDexClass; 31 32 /** 33 * Construct FancyLoader, grabbing a reference to the DexFile class 34 * if we're running under Dalvik. 35 */ FancyLoader(ClassLoader parent)36 public FancyLoader(ClassLoader parent) { 37 super(parent); 38 39 try { 40 mDexClass = parent.loadClass("dalvik/system/DexFile"); 41 } catch (ClassNotFoundException cnfe) { 42 // ignore -- not running Dalvik 43 } 44 } 45 46 /** 47 * Finds the class with the specified binary name. 48 * 49 * We search for a file in CLASS_PATH or pull an entry from DEX_FILE. 50 * If we don't find a match, we throw an exception. 51 */ findClass(String name)52 protected Class<?> findClass(String name) throws ClassNotFoundException 53 { 54 if (mDexClass != null) { 55 return findClassDalvik(name); 56 } else { 57 return findClassNonDalvik(name); 58 } 59 } 60 61 /** 62 * Finds the class with the specified binary name, from a DEX file. 63 */ findClassDalvik(String name)64 private Class<?> findClassDalvik(String name) 65 throws ClassNotFoundException { 66 67 Constructor ctor; 68 Object dexFile; 69 70 /* 71 * Construct a DexFile object through reflection. 72 */ 73 try { 74 ctor = mDexClass.getConstructor(new Class[] { String.class }); 75 } catch (NoSuchMethodException nsme) { 76 throw new ClassNotFoundException("getConstructor failed", nsme); 77 } 78 79 try { 80 dexFile = ctor.newInstance(DEX_FILE); 81 } catch (InstantiationException ie) { 82 throw new ClassNotFoundException("newInstance failed", ie); 83 } catch (IllegalAccessException iae) { 84 throw new ClassNotFoundException("newInstance failed", iae); 85 } catch (InvocationTargetException ite) { 86 throw new ClassNotFoundException("newInstance failed", ite); 87 } 88 89 /* 90 * Call DexFile.loadClass(String, ClassLoader). 91 */ 92 Method meth; 93 94 try { 95 meth = mDexClass.getMethod("loadClass", 96 new Class[] { String.class, ClassLoader.class }); 97 } catch (NoSuchMethodException nsme) { 98 throw new ClassNotFoundException("getMethod failed", nsme); 99 } 100 101 try { 102 meth.invoke(dexFile, name, this); 103 } catch (IllegalAccessException iae) { 104 throw new ClassNotFoundException("loadClass failed", iae); 105 } catch (InvocationTargetException ite) { 106 throw new ClassNotFoundException("loadClass failed", 107 ite.getCause()); 108 } 109 110 return null; 111 } 112 113 /** 114 * Finds the class with the specified binary name, from .class files. 115 */ findClassNonDalvik(String name)116 private Class<?> findClassNonDalvik(String name) 117 throws ClassNotFoundException { 118 119 String pathName = CLASS_PATH + name + ".class"; 120 //System.out.println("--- Fancy: looking for " + pathName); 121 122 File path = new File(pathName); 123 RandomAccessFile raf; 124 125 try { 126 raf = new RandomAccessFile(path, "r"); 127 } catch (FileNotFoundException fnfe) { 128 throw new ClassNotFoundException("Not found: " + pathName); 129 } 130 131 /* read the entire file in */ 132 byte[] fileData; 133 try { 134 fileData = new byte[(int) raf.length()]; 135 raf.readFully(fileData); 136 } catch (IOException ioe) { 137 throw new ClassNotFoundException("Read error: " + pathName); 138 } finally { 139 try { 140 raf.close(); 141 } catch (IOException ioe) { 142 // drop 143 } 144 } 145 146 /* create the class */ 147 //System.out.println("--- Fancy: defining " + name); 148 try { 149 return defineClass(name, fileData, 0, fileData.length); 150 } catch (Throwable th) { 151 throw new ClassNotFoundException("defineClass failed", th); 152 } 153 } 154 155 /** 156 * Load a class. 157 * 158 * Normally a class loader wouldn't override this, but we want our 159 * version of the class to take precedence over an already-loaded 160 * version. 161 * 162 * We still want the system classes (e.g. java.lang.Object) from the 163 * bootstrap class loader. 164 */ loadClass(String name, boolean resolve)165 protected Class<?> loadClass(String name, boolean resolve) 166 throws ClassNotFoundException 167 { 168 Class res; 169 170 /* 171 * 1. Invoke findLoadedClass(String) to check if the class has 172 * already been loaded. 173 * 174 * This doesn't change. 175 */ 176 res = findLoadedClass(name); 177 if (res != null) { 178 System.out.println("FancyLoader.loadClass: " 179 + name + " already loaded"); 180 if (resolve) 181 resolveClass(res); 182 return res; 183 } 184 185 /* 186 * 3. Invoke the findClass(String) method to find the class. 187 */ 188 try { 189 res = findClass(name); 190 if (resolve) 191 resolveClass(res); 192 } 193 catch (ClassNotFoundException e) { 194 // we couldn't find it, so eat the exception and keep going 195 } 196 197 /* 198 * 2. Invoke the loadClass method on the parent class loader. If 199 * the parent loader is null the class loader built-in to the 200 * virtual machine is used, instead. 201 * 202 * (Since we're not in java.lang, we can't actually invoke the 203 * parent's loadClass() method, but we passed our parent to the 204 * super-class which can take care of it for us.) 205 */ 206 res = super.loadClass(name, resolve); // returns class or throws 207 return res; 208 } 209 } 210 211