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