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.util.ArrayList;
22 import java.util.Enumeration;
23 import java.util.List;
24 
25 /**
26  * Base class for common functionality between various dex-based
27  * {@link ClassLoader} implementations.
28  */
29 public class BaseDexClassLoader extends ClassLoader {
30     private final DexPathList pathList;
31 
32     /**
33      * Constructs an instance.
34      *
35      * @param dexPath the list of jar/apk files containing classes and
36      * resources, delimited by {@code File.pathSeparator}, which
37      * defaults to {@code ":"} on Android
38      * @param optimizedDirectory directory where optimized dex files
39      * should be written; may be {@code null}
40      * @param librarySearchPath the list of directories containing native
41      * libraries, delimited by {@code File.pathSeparator}; may be
42      * {@code null}
43      * @param parent the parent class loader
44      */
BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent)45     public BaseDexClassLoader(String dexPath, File optimizedDirectory,
46             String librarySearchPath, ClassLoader parent) {
47         super(parent);
48         this.pathList = new DexPathList(this, dexPath, librarySearchPath, optimizedDirectory);
49     }
50 
51     @Override
findClass(String name)52     protected Class<?> findClass(String name) throws ClassNotFoundException {
53         List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
54         Class c = pathList.findClass(name, suppressedExceptions);
55         if (c == null) {
56             ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
57             for (Throwable t : suppressedExceptions) {
58                 cnfe.addSuppressed(t);
59             }
60             throw cnfe;
61         }
62         return c;
63     }
64 
65     /**
66      * @hide
67      */
addDexPath(String dexPath)68     public void addDexPath(String dexPath) {
69         pathList.addDexPath(dexPath, null /*optimizedDirectory*/);
70     }
71 
72     @Override
findResource(String name)73     protected URL findResource(String name) {
74         return pathList.findResource(name);
75     }
76 
77     @Override
findResources(String name)78     protected Enumeration<URL> findResources(String name) {
79         return pathList.findResources(name);
80     }
81 
82     @Override
findLibrary(String name)83     public String findLibrary(String name) {
84         return pathList.findLibrary(name);
85     }
86 
87     /**
88      * Returns package information for the given package.
89      * Unfortunately, instances of this class don't really have this
90      * information, and as a non-secure {@code ClassLoader}, it isn't
91      * even required to, according to the spec. Yet, we want to
92      * provide it, in order to make all those hopeful callers of
93      * {@code myClass.getPackage().getName()} happy. Thus we construct
94      * a {@code Package} object the first time it is being requested
95      * and fill most of the fields with dummy values. The {@code
96      * Package} object is then put into the {@code ClassLoader}'s
97      * package cache, so we see the same one next time. We don't
98      * create {@code Package} objects for {@code null} arguments or
99      * for the default package.
100      *
101      * <p>There is a limited chance that we end up with multiple
102      * {@code Package} objects representing the same package: It can
103      * happen when when a package is scattered across different JAR
104      * files which were loaded by different {@code ClassLoader}
105      * instances. This is rather unlikely, and given that this whole
106      * thing is more or less a workaround, probably not worth the
107      * effort to address.
108      *
109      * @param name the name of the class
110      * @return the package information for the class, or {@code null}
111      * if there is no package information available for it
112      */
113     @Override
getPackage(String name)114     protected synchronized Package getPackage(String name) {
115         if (name != null && !name.isEmpty()) {
116             Package pack = super.getPackage(name);
117 
118             if (pack == null) {
119                 pack = definePackage(name, "Unknown", "0.0", "Unknown",
120                         "Unknown", "0.0", "Unknown", null);
121             }
122 
123             return pack;
124         }
125 
126         return null;
127     }
128 
129     /**
130      * @hide
131      */
getLdLibraryPath()132     public String getLdLibraryPath() {
133         StringBuilder result = new StringBuilder();
134         for (File directory : pathList.getNativeLibraryDirectories()) {
135             if (result.length() > 0) {
136                 result.append(':');
137             }
138             result.append(directory);
139         }
140 
141         return result.toString();
142     }
143 
toString()144     @Override public String toString() {
145         return getClass().getName() + "[" + pathList + "]";
146     }
147 }
148