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