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 libraryPath 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 libraryPath, ClassLoader parent)45     public BaseDexClassLoader(String dexPath, File optimizedDirectory,
46             String libraryPath, ClassLoader parent) {
47         super(parent);
48         this.pathList = new DexPathList(this, dexPath, libraryPath, 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     @Override
findResource(String name)66     protected URL findResource(String name) {
67         return pathList.findResource(name);
68     }
69 
70     @Override
findResources(String name)71     protected Enumeration<URL> findResources(String name) {
72         return pathList.findResources(name);
73     }
74 
75     @Override
findLibrary(String name)76     public String findLibrary(String name) {
77         return pathList.findLibrary(name);
78     }
79 
80     /**
81      * Returns package information for the given package.
82      * Unfortunately, instances of this class don't really have this
83      * information, and as a non-secure {@code ClassLoader}, it isn't
84      * even required to, according to the spec. Yet, we want to
85      * provide it, in order to make all those hopeful callers of
86      * {@code myClass.getPackage().getName()} happy. Thus we construct
87      * a {@code Package} object the first time it is being requested
88      * and fill most of the fields with dummy values. The {@code
89      * Package} object is then put into the {@code ClassLoader}'s
90      * package cache, so we see the same one next time. We don't
91      * create {@code Package} objects for {@code null} arguments or
92      * for the default package.
93      *
94      * <p>There is a limited chance that we end up with multiple
95      * {@code Package} objects representing the same package: It can
96      * happen when when a package is scattered across different JAR
97      * files which were loaded by different {@code ClassLoader}
98      * instances. This is rather unlikely, and given that this whole
99      * thing is more or less a workaround, probably not worth the
100      * effort to address.
101      *
102      * @param name the name of the class
103      * @return the package information for the class, or {@code null}
104      * if there is no package information available for it
105      */
106     @Override
getPackage(String name)107     protected synchronized Package getPackage(String name) {
108         if (name != null && !name.isEmpty()) {
109             Package pack = super.getPackage(name);
110 
111             if (pack == null) {
112                 pack = definePackage(name, "Unknown", "0.0", "Unknown",
113                         "Unknown", "0.0", "Unknown", null);
114             }
115 
116             return pack;
117         }
118 
119         return null;
120     }
121 
122     /**
123      * @hide
124      */
getLdLibraryPath()125     public String getLdLibraryPath() {
126         StringBuilder result = new StringBuilder();
127         for (File directory : pathList.getNativeLibraryDirectories()) {
128             if (result.length() > 0) {
129                 result.append(':');
130             }
131             result.append(directory);
132         }
133         return result.toString();
134     }
135 
toString()136     @Override public String toString() {
137         return getClass().getName() + "[" + pathList + "]";
138     }
139 }
140