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