1 /*
2  * Copyright (C) 2006 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 android.app;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.content.pm.SharedLibraryInfo;
21 import android.os.Build;
22 import android.os.GraphicsEnvironment;
23 import android.os.Trace;
24 import android.util.ArrayMap;
25 import android.util.Log;
26 
27 import com.android.internal.os.ClassLoaderFactory;
28 
29 import dalvik.system.PathClassLoader;
30 
31 import java.util.ArrayList;
32 import java.util.Collection;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Map;
36 
37 /** @hide */
38 public class ApplicationLoaders {
39     private static final String TAG = "ApplicationLoaders";
40 
41     @UnsupportedAppUsage
getDefault()42     public static ApplicationLoaders getDefault() {
43         return gApplicationLoaders;
44     }
45 
getClassLoader(String zip, int targetSdkVersion, boolean isBundled, String librarySearchPath, String libraryPermittedPath, ClassLoader parent, String classLoaderName)46     ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
47                                String librarySearchPath, String libraryPermittedPath,
48                                ClassLoader parent, String classLoaderName) {
49         return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
50                               librarySearchPath, libraryPermittedPath, parent, classLoaderName,
51                               null);
52     }
53 
getClassLoaderWithSharedLibraries( String zip, int targetSdkVersion, boolean isBundled, String librarySearchPath, String libraryPermittedPath, ClassLoader parent, String classLoaderName, List<ClassLoader> sharedLibraries)54     ClassLoader getClassLoaderWithSharedLibraries(
55             String zip, int targetSdkVersion, boolean isBundled,
56             String librarySearchPath, String libraryPermittedPath,
57             ClassLoader parent, String classLoaderName,
58             List<ClassLoader> sharedLibraries) {
59         // For normal usage the cache key used is the same as the zip path.
60         return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
61                               libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries);
62     }
63 
64     /**
65      * Gets a class loader for a shared library. Additional dependent shared libraries are allowed
66      * to be specified (sharedLibraries).
67      *
68      * Additionally, as an optimization, this will return a pre-created ClassLoader if one has
69      * been cached by createAndCacheNonBootclasspathSystemClassLoaders.
70      */
getSharedLibraryClassLoaderWithSharedLibraries(String zip, int targetSdkVersion, boolean isBundled, String librarySearchPath, String libraryPermittedPath, ClassLoader parent, String classLoaderName, List<ClassLoader> sharedLibraries)71     ClassLoader getSharedLibraryClassLoaderWithSharedLibraries(String zip, int targetSdkVersion,
72             boolean isBundled, String librarySearchPath, String libraryPermittedPath,
73             ClassLoader parent, String classLoaderName, List<ClassLoader> sharedLibraries) {
74         ClassLoader loader = getCachedNonBootclasspathSystemLib(zip, parent, classLoaderName,
75                 sharedLibraries);
76         if (loader != null) {
77             return loader;
78         }
79 
80         return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled,
81               librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries);
82     }
83 
getClassLoader(String zip, int targetSdkVersion, boolean isBundled, String librarySearchPath, String libraryPermittedPath, ClassLoader parent, String cacheKey, String classLoaderName, List<ClassLoader> sharedLibraries)84     private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
85                                        String librarySearchPath, String libraryPermittedPath,
86                                        ClassLoader parent, String cacheKey,
87                                        String classLoaderName, List<ClassLoader> sharedLibraries) {
88         /*
89          * This is the parent we use if they pass "null" in.  In theory
90          * this should be the "system" class loader; in practice we
91          * don't use that and can happily (and more efficiently) use the
92          * bootstrap class loader.
93          */
94         ClassLoader baseParent = ClassLoader.getSystemClassLoader().getParent();
95 
96         synchronized (mLoaders) {
97             if (parent == null) {
98                 parent = baseParent;
99             }
100 
101             /*
102              * If we're one step up from the base class loader, find
103              * something in our cache.  Otherwise, we create a whole
104              * new ClassLoader for the zip archive.
105              */
106             if (parent == baseParent) {
107                 ClassLoader loader = mLoaders.get(cacheKey);
108                 if (loader != null) {
109                     return loader;
110                 }
111 
112                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
113 
114                 ClassLoader classloader = ClassLoaderFactory.createClassLoader(
115                         zip,  librarySearchPath, libraryPermittedPath, parent,
116                         targetSdkVersion, isBundled, classLoaderName, sharedLibraries);
117 
118                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
119 
120                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setLayerPaths");
121                 GraphicsEnvironment.getInstance().setLayerPaths(
122                         classloader, librarySearchPath, libraryPermittedPath);
123                 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
124 
125                 if (cacheKey != null) {
126                     mLoaders.put(cacheKey, classloader);
127                 }
128                 return classloader;
129             }
130 
131             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip);
132             ClassLoader loader = ClassLoaderFactory.createClassLoader(
133                     zip, null, parent, classLoaderName, sharedLibraries);
134             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
135             return loader;
136         }
137     }
138 
139     /**
140      * Caches system library class loaders which are not on the bootclasspath but are still used
141      * by many system apps.
142      *
143      * All libraries in the closure of libraries to be loaded must be in libs. A library can
144      * only depend on libraries that come before it in the list.
145      */
createAndCacheNonBootclasspathSystemClassLoaders(SharedLibraryInfo[] libs)146     public void createAndCacheNonBootclasspathSystemClassLoaders(SharedLibraryInfo[] libs) {
147         if (mSystemLibsCacheMap != null) {
148             throw new IllegalStateException("Already cached.");
149         }
150 
151         mSystemLibsCacheMap = new HashMap<String, CachedClassLoader>();
152 
153         for (SharedLibraryInfo lib : libs) {
154             createAndCacheNonBootclasspathSystemClassLoader(lib);
155         }
156     }
157 
158     /**
159      * Caches a single non-bootclasspath class loader.
160      *
161      * All of this library's dependencies must have previously been cached. Otherwise, an exception
162      * is thrown.
163      */
createAndCacheNonBootclasspathSystemClassLoader(SharedLibraryInfo lib)164     private void createAndCacheNonBootclasspathSystemClassLoader(SharedLibraryInfo lib) {
165         String path = lib.getPath();
166         List<SharedLibraryInfo> dependencies = lib.getDependencies();
167 
168         // get cached classloaders for dependencies
169         ArrayList<ClassLoader> sharedLibraries = null;
170         if (dependencies != null) {
171             sharedLibraries = new ArrayList<ClassLoader>(dependencies.size());
172             for (SharedLibraryInfo dependency : dependencies) {
173                 String dependencyPath = dependency.getPath();
174                 CachedClassLoader cached = mSystemLibsCacheMap.get(dependencyPath);
175 
176                 if (cached == null) {
177                     throw new IllegalStateException("Failed to find dependency " + dependencyPath
178                             + " of cachedlibrary " + path);
179                 }
180 
181                 sharedLibraries.add(cached.loader);
182             }
183         }
184 
185         // assume cached libraries work with current sdk since they are built-in
186         ClassLoader classLoader = getClassLoader(path, Build.VERSION.SDK_INT, true /*isBundled*/,
187                 null /*librarySearchPath*/, null /*libraryPermittedPath*/, null /*parent*/,
188                 null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/);
189 
190         if (classLoader == null) {
191             // bad configuration or break in classloading code
192             throw new IllegalStateException("Failed to cache " + path);
193         }
194 
195         CachedClassLoader cached = new CachedClassLoader();
196         cached.loader = classLoader;
197         cached.sharedLibraries = sharedLibraries;
198 
199         Log.d(TAG, "Created zygote-cached class loader: " + path);
200         mSystemLibsCacheMap.put(path, cached);
201     }
202 
sharedLibrariesEquals(List<ClassLoader> lhs, List<ClassLoader> rhs)203     private static boolean sharedLibrariesEquals(List<ClassLoader> lhs, List<ClassLoader> rhs) {
204         if (lhs == null) {
205             return rhs == null;
206         }
207 
208         return lhs.equals(rhs);
209     }
210 
211     /**
212      * Returns lib cached with createAndCacheNonBootclasspathSystemClassLoader. This is called by
213      * the zygote during caching.
214      *
215      * If there is an error or the cache is not available, this returns null.
216      */
getCachedNonBootclasspathSystemLib(String zip, ClassLoader parent, String classLoaderName, List<ClassLoader> sharedLibraries)217     public ClassLoader getCachedNonBootclasspathSystemLib(String zip, ClassLoader parent,
218             String classLoaderName, List<ClassLoader> sharedLibraries) {
219         if (mSystemLibsCacheMap == null) {
220             return null;
221         }
222 
223         // we only cache top-level libs with the default class loader
224         if (parent != null || classLoaderName != null) {
225             return null;
226         }
227 
228         CachedClassLoader cached = mSystemLibsCacheMap.get(zip);
229         if (cached == null) {
230             return null;
231         }
232 
233         // cached must be built and loaded in the same environment
234         if (!sharedLibrariesEquals(sharedLibraries, cached.sharedLibraries)) {
235             Log.w(TAG, "Unexpected environment for cached library: (" + sharedLibraries + "|"
236                     + cached.sharedLibraries + ")");
237             return null;
238         }
239 
240         Log.d(TAG, "Returning zygote-cached class loader: " + zip);
241         return cached.loader;
242     }
243 
244     /**
245      * Creates a classloader for the WebView APK and places it in the cache of loaders maintained
246      * by this class. This is used in the WebView zygote, where its presence in the cache speeds up
247      * startup and enables memory sharing.
248      */
createAndCacheWebViewClassLoader(String packagePath, String libsPath, String cacheKey)249     public ClassLoader createAndCacheWebViewClassLoader(String packagePath, String libsPath,
250                                                         String cacheKey) {
251         // The correct paths are calculated by WebViewZygote in the system server and passed to
252         // us here. We hardcode the other parameters: WebView always targets the current SDK,
253         // does not need to use non-public system libraries, and uses the base classloader as its
254         // parent to permit usage of the cache.
255         // The cache key is passed separately to enable the stub WebView to be cached under the
256         // stub's APK path, when the actual package path is the donor APK.
257         return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null,
258                               cacheKey, null /* classLoaderName */, null /* sharedLibraries */);
259     }
260 
261     /**
262      * Adds a new path the classpath of the given loader.
263      * @throws IllegalStateException if the provided class loader is not a {@link PathClassLoader}.
264      */
addPath(ClassLoader classLoader, String dexPath)265     void addPath(ClassLoader classLoader, String dexPath) {
266         if (!(classLoader instanceof PathClassLoader)) {
267             throw new IllegalStateException("class loader is not a PathClassLoader");
268         }
269         final PathClassLoader baseDexClassLoader = (PathClassLoader) classLoader;
270         baseDexClassLoader.addDexPath(dexPath);
271     }
272 
273     /**
274      * @hide
275      */
addNative(ClassLoader classLoader, Collection<String> libPaths)276     void addNative(ClassLoader classLoader, Collection<String> libPaths) {
277         if (!(classLoader instanceof PathClassLoader)) {
278             throw new IllegalStateException("class loader is not a PathClassLoader");
279         }
280         final PathClassLoader baseDexClassLoader = (PathClassLoader) classLoader;
281         baseDexClassLoader.addNativePath(libPaths);
282     }
283 
284     @UnsupportedAppUsage
285     private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<>();
286 
287     private static final ApplicationLoaders gApplicationLoaders = new ApplicationLoaders();
288 
289     private static class CachedClassLoader {
290         ClassLoader loader;
291 
292         /**
293          * The shared libraries used when constructing loader for verification.
294          */
295         List<ClassLoader> sharedLibraries;
296     }
297 
298     /**
299      * This is a map of zip to associated class loader.
300      */
301     private Map<String, CachedClassLoader> mSystemLibsCacheMap = null;
302 }
303