• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 package dalvik.system;
18 
19 import java.io.File;
20 import java.net.URL;
21 import java.nio.ByteBuffer;
22 import java.util.ArrayList;
23 import java.util.Enumeration;
24 import java.util.List;
25 
26 /**
27  * Base class for common functionality between various dex-based
28  * {@link ClassLoader} implementations.
29  */
30 public class BaseDexClassLoader extends ClassLoader {
31 
32     /**
33      * Hook for customizing how dex files loads are reported.
34      *
35      * This enables the framework to monitor the use of dex files. The
36      * goal is to simplify the mechanism for optimizing foreign dex files and
37      * enable further optimizations of secondary dex files.
38      *
39      * The reporting happens only when new instances of BaseDexClassLoader
40      * are constructed and will be active only after this field is set with
41      * {@link BaseDexClassLoader#setReporter}.
42      */
43     /* @NonNull */ private static volatile Reporter reporter = null;
44 
45     private final DexPathList pathList;
46 
47     /**
48      * Constructs an instance.
49      * Note that all the *.jar and *.apk files from {@code dexPath} might be
50      * first extracted in-memory before the code is loaded. This can be avoided
51      * by passing raw dex files (*.dex) in the {@code dexPath}.
52      *
53      * @param dexPath the list of jar/apk files containing classes and
54      * resources, delimited by {@code File.pathSeparator}, which
55      * defaults to {@code ":"} on Android.
56      * @param optimizedDirectory this parameter is deprecated and has no effect
57      * @param librarySearchPath the list of directories containing native
58      * libraries, delimited by {@code File.pathSeparator}; may be
59      * {@code null}
60      * @param parent the parent class loader
61      */
BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent)62     public BaseDexClassLoader(String dexPath, File optimizedDirectory,
63             String librarySearchPath, ClassLoader parent) {
64         super(parent);
65         this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);
66 
67         if (reporter != null) {
68             reporter.report(this.pathList.getDexPaths());
69         }
70     }
71 
72     /**
73      * Constructs an instance.
74      *
75      * dexFile must be an in-memory representation of a full dexFile.
76      *
77      * @param dexFiles the array of in-memory dex files containing classes.
78      * @param parent the parent class loader
79      *
80      * @hide
81      */
BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent)82     public BaseDexClassLoader(ByteBuffer[] dexFiles, ClassLoader parent) {
83         // TODO We should support giving this a library search path maybe.
84         super(parent);
85         this.pathList = new DexPathList(this, dexFiles);
86     }
87 
88     @Override
findClass(String name)89     protected Class<?> findClass(String name) throws ClassNotFoundException {
90         List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
91         Class c = pathList.findClass(name, suppressedExceptions);
92         if (c == null) {
93             ClassNotFoundException cnfe = new ClassNotFoundException(
94                     "Didn't find class \"" + name + "\" on path: " + pathList);
95             for (Throwable t : suppressedExceptions) {
96                 cnfe.addSuppressed(t);
97             }
98             throw cnfe;
99         }
100         return c;
101     }
102 
103     /**
104      * @hide
105      */
addDexPath(String dexPath)106     public void addDexPath(String dexPath) {
107         pathList.addDexPath(dexPath, null /*optimizedDirectory*/);
108     }
109 
110     @Override
findResource(String name)111     protected URL findResource(String name) {
112         return pathList.findResource(name);
113     }
114 
115     @Override
findResources(String name)116     protected Enumeration<URL> findResources(String name) {
117         return pathList.findResources(name);
118     }
119 
120     @Override
findLibrary(String name)121     public String findLibrary(String name) {
122         return pathList.findLibrary(name);
123     }
124 
125     /**
126      * Returns package information for the given package.
127      * Unfortunately, instances of this class don't really have this
128      * information, and as a non-secure {@code ClassLoader}, it isn't
129      * even required to, according to the spec. Yet, we want to
130      * provide it, in order to make all those hopeful callers of
131      * {@code myClass.getPackage().getName()} happy. Thus we construct
132      * a {@code Package} object the first time it is being requested
133      * and fill most of the fields with dummy values. The {@code
134      * Package} object is then put into the {@code ClassLoader}'s
135      * package cache, so we see the same one next time. We don't
136      * create {@code Package} objects for {@code null} arguments or
137      * for the default package.
138      *
139      * <p>There is a limited chance that we end up with multiple
140      * {@code Package} objects representing the same package: It can
141      * happen when when a package is scattered across different JAR
142      * files which were loaded by different {@code ClassLoader}
143      * instances. This is rather unlikely, and given that this whole
144      * thing is more or less a workaround, probably not worth the
145      * effort to address.
146      *
147      * @param name the name of the class
148      * @return the package information for the class, or {@code null}
149      * if there is no package information available for it
150      */
151     @Override
getPackage(String name)152     protected synchronized Package getPackage(String name) {
153         if (name != null && !name.isEmpty()) {
154             Package pack = super.getPackage(name);
155 
156             if (pack == null) {
157                 pack = definePackage(name, "Unknown", "0.0", "Unknown",
158                         "Unknown", "0.0", "Unknown", null);
159             }
160 
161             return pack;
162         }
163 
164         return null;
165     }
166 
167     /**
168      * @hide
169      */
getLdLibraryPath()170     public String getLdLibraryPath() {
171         StringBuilder result = new StringBuilder();
172         for (File directory : pathList.getNativeLibraryDirectories()) {
173             if (result.length() > 0) {
174                 result.append(':');
175             }
176             result.append(directory);
177         }
178 
179         return result.toString();
180     }
181 
toString()182     @Override public String toString() {
183         return getClass().getName() + "[" + pathList + "]";
184     }
185 
186     /**
187      * Sets the reporter for dex load notifications.
188      * Once set, all new instances of BaseDexClassLoader will report upon
189      * constructions the loaded dex files.
190      *
191      * @param newReporter the new Reporter. Setting null will cancel reporting.
192      * @hide
193      */
setReporter(Reporter newReporter)194     public static void setReporter(Reporter newReporter) {
195         reporter = newReporter;
196     }
197 
198     /**
199      * @hide
200      */
getReporter()201     public static Reporter getReporter() {
202         return reporter;
203     }
204 
205     /**
206      * @hide
207      */
208     public interface Reporter {
report(List<String> dexPaths)209         public void report(List<String> dexPaths);
210     }
211 }
212