The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 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 | |
Mathew Inwood | 4fb17d1 | 2018-08-14 14:25:44 +0100 | [diff] [blame] | 19 | import android.annotation.UnsupportedAppUsage; |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 20 | import android.content.pm.SharedLibraryInfo; |
Torne (Richard Coles) | 3b6ca99 | 2016-10-10 15:11:36 +0100 | [diff] [blame] | 21 | import android.os.Build; |
Cody Northrop | 86cedcb | 2017-10-20 09:03:13 -0600 | [diff] [blame] | 22 | import android.os.GraphicsEnvironment; |
Dianne Hackborn | f7be480 | 2013-04-12 14:52:58 -0700 | [diff] [blame] | 23 | import android.os.Trace; |
Dianne Hackborn | add005c | 2013-07-17 18:43:12 -0700 | [diff] [blame] | 24 | import android.util.ArrayMap; |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 25 | import android.util.Log; |
Cody Northrop | 86cedcb | 2017-10-20 09:03:13 -0600 | [diff] [blame] | 26 | |
Narayan Kamath | f9419f0 | 2017-06-15 11:35:38 +0100 | [diff] [blame] | 27 | import com.android.internal.os.ClassLoaderFactory; |
Cody Northrop | 86cedcb | 2017-10-20 09:03:13 -0600 | [diff] [blame] | 28 | |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 29 | import dalvik.system.PathClassLoader; |
| 30 | |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 31 | import java.util.ArrayList; |
Patrick Baumann | 1bea237 | 2018-03-13 14:26:58 -0700 | [diff] [blame] | 32 | import java.util.Collection; |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 33 | import java.util.HashMap; |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 34 | import java.util.List; |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 35 | import java.util.Map; |
Patrick Baumann | 1bea237 | 2018-03-13 14:26:58 -0700 | [diff] [blame] | 36 | |
Torne (Richard Coles) | 3b6ca99 | 2016-10-10 15:11:36 +0100 | [diff] [blame] | 37 | /** @hide */ |
| 38 | public class ApplicationLoaders { |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 39 | private static final String TAG = "ApplicationLoaders"; |
| 40 | |
Mathew Inwood | 4fb17d1 | 2018-08-14 14:25:44 +0100 | [diff] [blame] | 41 | @UnsupportedAppUsage |
Dimitry Ivanov | a55c7f1 | 2016-02-23 14:25:50 -0800 | [diff] [blame] | 42 | public static ApplicationLoaders getDefault() { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 43 | return gApplicationLoaders; |
| 44 | } |
| 45 | |
Torne (Richard Coles) | 3b6ca99 | 2016-10-10 15:11:36 +0100 | [diff] [blame] | 46 | ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled, |
Torne (Richard Coles) | 0452670 | 2017-01-13 14:19:39 +0000 | [diff] [blame] | 47 | String librarySearchPath, String libraryPermittedPath, |
Narayan Kamath | f9419f0 | 2017-06-15 11:35:38 +0100 | [diff] [blame] | 48 | ClassLoader parent, String classLoaderName) { |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 49 | return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled, |
| 50 | librarySearchPath, libraryPermittedPath, parent, classLoaderName, |
| 51 | null); |
| 52 | } |
| 53 | |
| 54 | ClassLoader getClassLoaderWithSharedLibraries( |
| 55 | String zip, int targetSdkVersion, boolean isBundled, |
| 56 | String librarySearchPath, String libraryPermittedPath, |
| 57 | ClassLoader parent, String classLoaderName, |
| 58 | List<ClassLoader> sharedLibraries) { |
Torne (Richard Coles) | 0452670 | 2017-01-13 14:19:39 +0000 | [diff] [blame] | 59 | // For normal usage the cache key used is the same as the zip path. |
| 60 | return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath, |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 61 | libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries); |
Torne (Richard Coles) | 0452670 | 2017-01-13 14:19:39 +0000 | [diff] [blame] | 62 | } |
| 63 | |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 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 | */ |
| 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 | |
Torne (Richard Coles) | 0452670 | 2017-01-13 14:19:39 +0000 | [diff] [blame] | 84 | private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled, |
| 85 | String librarySearchPath, String libraryPermittedPath, |
Narayan Kamath | f9419f0 | 2017-06-15 11:35:38 +0100 | [diff] [blame] | 86 | ClassLoader parent, String cacheKey, |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 87 | String classLoaderName, List<ClassLoader> sharedLibraries) { |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 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) { |
Torne (Richard Coles) | 0452670 | 2017-01-13 14:19:39 +0000 | [diff] [blame] | 107 | ClassLoader loader = mLoaders.get(cacheKey); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 108 | if (loader != null) { |
| 109 | return loader; |
| 110 | } |
Dimitry Ivanov | b1ef62b | 2016-04-20 14:09:32 -0700 | [diff] [blame] | 111 | |
Dianne Hackborn | f7be480 | 2013-04-12 14:52:58 -0700 | [diff] [blame] | 112 | Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip); |
Dimitry Ivanov | a55c7f1 | 2016-02-23 14:25:50 -0800 | [diff] [blame] | 113 | |
Narayan Kamath | f9419f0 | 2017-06-15 11:35:38 +0100 | [diff] [blame] | 114 | ClassLoader classloader = ClassLoaderFactory.createClassLoader( |
| 115 | zip, librarySearchPath, libraryPermittedPath, parent, |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 116 | targetSdkVersion, isBundled, classLoaderName, sharedLibraries); |
Dimitry Ivanov | a55c7f1 | 2016-02-23 14:25:50 -0800 | [diff] [blame] | 117 | |
Dianne Hackborn | f7be480 | 2013-04-12 14:52:58 -0700 | [diff] [blame] | 118 | Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| 119 | |
Cody Northrop | 86cedcb | 2017-10-20 09:03:13 -0600 | [diff] [blame] | 120 | Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setLayerPaths"); |
| 121 | GraphicsEnvironment.getInstance().setLayerPaths( |
| 122 | classloader, librarySearchPath, libraryPermittedPath); |
Andreas Gampe | 4c8e542 | 2016-05-18 11:58:47 -0700 | [diff] [blame] | 123 | Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
Dimitry Ivanov | 0997908 | 2016-04-19 11:11:01 -0700 | [diff] [blame] | 124 | |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 125 | if (cacheKey != null) { |
| 126 | mLoaders.put(cacheKey, classloader); |
| 127 | } |
Narayan Kamath | f9419f0 | 2017-06-15 11:35:38 +0100 | [diff] [blame] | 128 | return classloader; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 129 | } |
| 130 | |
Dianne Hackborn | f7be480 | 2013-04-12 14:52:58 -0700 | [diff] [blame] | 131 | Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip); |
Narayan Kamath | f9419f0 | 2017-06-15 11:35:38 +0100 | [diff] [blame] | 132 | ClassLoader loader = ClassLoaderFactory.createClassLoader( |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 133 | zip, null, parent, classLoaderName, sharedLibraries); |
Dianne Hackborn | f7be480 | 2013-04-12 14:52:58 -0700 | [diff] [blame] | 134 | Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
Narayan Kamath | f9419f0 | 2017-06-15 11:35:38 +0100 | [diff] [blame] | 135 | return loader; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 136 | } |
| 137 | } |
| 138 | |
Torne (Richard Coles) | 3b6ca99 | 2016-10-10 15:11:36 +0100 | [diff] [blame] | 139 | /** |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 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 | */ |
| 146 | public void createAndCacheNonBootclasspathSystemClassLoaders(SharedLibraryInfo[] libs) { |
| 147 | if (mSystemLibsCacheMap != null) { |
Steven Moreland | 94190e0 | 2019-03-27 06:38:42 -0700 | [diff] [blame] | 148 | throw new IllegalStateException("Already cached."); |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 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 | * |
Steven Moreland | 94190e0 | 2019-03-27 06:38:42 -0700 | [diff] [blame] | 161 | * All of this library's dependencies must have previously been cached. Otherwise, an exception |
| 162 | * is thrown. |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 163 | */ |
| 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) { |
Steven Moreland | 94190e0 | 2019-03-27 06:38:42 -0700 | [diff] [blame] | 177 | throw new IllegalStateException("Failed to find dependency " + dependencyPath |
| 178 | + " of cachedlibrary " + path); |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 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) { |
Steven Moreland | 94190e0 | 2019-03-27 06:38:42 -0700 | [diff] [blame] | 191 | // bad configuration or break in classloading code |
| 192 | throw new IllegalStateException("Failed to cache " + path); |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 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 | |
| 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 | */ |
Steven Moreland | 94190e0 | 2019-03-27 06:38:42 -0700 | [diff] [blame] | 217 | public ClassLoader getCachedNonBootclasspathSystemLib(String zip, ClassLoader parent, |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 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 | /** |
Torne (Richard Coles) | 3b6ca99 | 2016-10-10 15:11:36 +0100 | [diff] [blame] | 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 | */ |
Torne (Richard Coles) | 0452670 | 2017-01-13 14:19:39 +0000 | [diff] [blame] | 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, |
Nicolas Geoffray | 972b39e | 2018-11-15 12:59:52 +0000 | [diff] [blame] | 258 | cacheKey, null /* classLoaderName */, null /* sharedLibraries */); |
Torne (Richard Coles) | 3b6ca99 | 2016-10-10 15:11:36 +0100 | [diff] [blame] | 259 | } |
| 260 | |
Todd Kennedy | 39bfee5 | 2016-02-24 10:28:21 -0800 | [diff] [blame] | 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 | */ |
| 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 | |
Patrick Baumann | 1bea237 | 2018-03-13 14:26:58 -0700 | [diff] [blame] | 273 | /** |
| 274 | * @hide |
| 275 | */ |
| 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 | |
Mathew Inwood | 4fb17d1 | 2018-08-14 14:25:44 +0100 | [diff] [blame] | 284 | @UnsupportedAppUsage |
Narayan Kamath | f9419f0 | 2017-06-15 11:35:38 +0100 | [diff] [blame] | 285 | private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<>(); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 286 | |
Dimitry Ivanov | b1ef62b | 2016-04-20 14:09:32 -0700 | [diff] [blame] | 287 | private static final ApplicationLoaders gApplicationLoaders = new ApplicationLoaders(); |
Steven Moreland | bdc2702 | 2019-03-29 21:23:48 +0000 | [diff] [blame] | 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; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 302 | } |