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