| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.app; |
| |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.content.pm.SharedLibraryInfo; |
| import android.os.Build; |
| import android.os.GraphicsEnvironment; |
| import android.os.Trace; |
| import android.util.ArrayMap; |
| import android.util.Log; |
| |
| import com.android.internal.os.ClassLoaderFactory; |
| |
| import dalvik.system.PathClassLoader; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** @hide */ |
| public class ApplicationLoaders { |
| private static final String TAG = "ApplicationLoaders"; |
| |
| @UnsupportedAppUsage |
| public static ApplicationLoaders getDefault() { |
| return gApplicationLoaders; |
| } |
| |
| ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled, |
| String librarySearchPath, String libraryPermittedPath, |
| ClassLoader parent, String classLoaderName) { |
| return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled, |
| librarySearchPath, libraryPermittedPath, parent, classLoaderName, |
| null, null, null); |
| } |
| |
| ClassLoader getClassLoaderWithSharedLibraries( |
| String zip, int targetSdkVersion, boolean isBundled, |
| String librarySearchPath, String libraryPermittedPath, |
| ClassLoader parent, String classLoaderName, |
| List<ClassLoader> sharedLibraries, List<String> nativeSharedLibraries, |
| List<ClassLoader> sharedLibrariesLoadedAfterApp) { |
| // For normal usage the cache key used is the same as the zip path. |
| return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath, |
| libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries, |
| nativeSharedLibraries, sharedLibrariesLoadedAfterApp); |
| } |
| |
| /** |
| * Gets a class loader for a shared library. Additional dependent shared libraries are allowed |
| * to be specified (sharedLibraries). |
| * |
| * Additionally, as an optimization, this will return a pre-created ClassLoader if one has |
| * been cached by createAndCacheNonBootclasspathSystemClassLoaders. |
| */ |
| ClassLoader getSharedLibraryClassLoaderWithSharedLibraries(String zip, int targetSdkVersion, |
| boolean isBundled, String librarySearchPath, String libraryPermittedPath, |
| ClassLoader parent, String classLoaderName, List<ClassLoader> sharedLibraries, |
| List<ClassLoader> sharedLibrariesAfter) { |
| ClassLoader loader = getCachedNonBootclasspathSystemLib(zip, parent, classLoaderName, |
| sharedLibraries); |
| if (loader != null) { |
| return loader; |
| } |
| |
| // TODO(b/142191088): allow (Java) shared libraries to have <uses-native-library> |
| // Until that is supported, assume that all native shared libraries are used. |
| // "ALL" is a magic string that libnativeloader uses to unconditionally add all available |
| // native shared libraries to the classloader. |
| List<String> nativeSharedLibraries = new ArrayList<>(); |
| nativeSharedLibraries.add("ALL"); |
| return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled, |
| librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries, |
| nativeSharedLibraries, sharedLibrariesAfter); |
| } |
| |
| private ClassLoader 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) { |
| /* |
| * This is the parent we use if they pass "null" in. In theory |
| * this should be the "system" class loader; in practice we |
| * don't use that and can happily (and more efficiently) use the |
| * bootstrap class loader. |
| */ |
| ClassLoader baseParent = ClassLoader.getSystemClassLoader().getParent(); |
| |
| synchronized (mLoaders) { |
| if (parent == null) { |
| parent = baseParent; |
| } |
| |
| /* |
| * If we're one step up from the base class loader, find |
| * something in our cache. Otherwise, we create a whole |
| * new ClassLoader for the zip archive. |
| */ |
| if (parent == baseParent) { |
| ClassLoader loader = mLoaders.get(cacheKey); |
| if (loader != null) { |
| return loader; |
| } |
| |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip); |
| |
| ClassLoader classloader = ClassLoaderFactory.createClassLoader( |
| zip, librarySearchPath, libraryPermittedPath, parent, |
| targetSdkVersion, isBundled, classLoaderName, sharedLibraries, |
| nativeSharedLibraries, sharedLibrariesLoadedAfterApp); |
| |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setLayerPaths"); |
| GraphicsEnvironment.getInstance().setLayerPaths( |
| classloader, librarySearchPath, libraryPermittedPath); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| |
| if (cacheKey != null) { |
| mLoaders.put(cacheKey, classloader); |
| } |
| return classloader; |
| } |
| |
| Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, zip); |
| ClassLoader loader = ClassLoaderFactory.createClassLoader( |
| zip, null, parent, classLoaderName, sharedLibraries, |
| null /*sharedLibrariesLoadedAfterApp*/); |
| Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); |
| return loader; |
| } |
| } |
| |
| /** |
| * Caches system library class loaders which are not on the bootclasspath but are still used |
| * by many system apps. |
| * |
| * All libraries in the closure of libraries to be loaded must be in libs. A library can |
| * only depend on libraries that come before it in the list. |
| */ |
| public void createAndCacheNonBootclasspathSystemClassLoaders(List<SharedLibraryInfo> libs) { |
| if (mSystemLibsCacheMap != null) { |
| throw new IllegalStateException("Already cached."); |
| } |
| |
| mSystemLibsCacheMap = new HashMap<>(); |
| |
| for (int i = 0; i < libs.size(); i++) { |
| createAndCacheNonBootclasspathSystemClassLoader(libs.get(i)); |
| } |
| } |
| |
| /** |
| * Caches a single non-bootclasspath class loader. |
| * |
| * All of this library's dependencies must have previously been cached. Otherwise, an exception |
| * is thrown. |
| */ |
| private void createAndCacheNonBootclasspathSystemClassLoader(SharedLibraryInfo lib) { |
| String path = lib.getPath(); |
| List<SharedLibraryInfo> dependencies = lib.getDependencies(); |
| |
| // get cached classloaders for dependencies |
| ArrayList<ClassLoader> sharedLibraries = null; |
| if (dependencies != null) { |
| sharedLibraries = new ArrayList<ClassLoader>(dependencies.size()); |
| for (SharedLibraryInfo dependency : dependencies) { |
| String dependencyPath = dependency.getPath(); |
| CachedClassLoader cached = mSystemLibsCacheMap.get(dependencyPath); |
| |
| if (cached == null) { |
| throw new IllegalStateException("Failed to find dependency " + dependencyPath |
| + " of cachedlibrary " + path); |
| } |
| |
| sharedLibraries.add(cached.loader); |
| } |
| } |
| |
| // assume cached libraries work with current sdk since they are built-in |
| ClassLoader classLoader = getClassLoader(path, Build.VERSION.SDK_INT, true /*isBundled*/, |
| null /*librarySearchPath*/, null /*libraryPermittedPath*/, null /*parent*/, |
| null /*cacheKey*/, null /*classLoaderName*/, sharedLibraries /*sharedLibraries*/, |
| null /* nativeSharedLibraries */, null /*sharedLibrariesLoadedAfterApp*/); |
| |
| if (classLoader == null) { |
| // bad configuration or break in classloading code |
| throw new IllegalStateException("Failed to cache " + path); |
| } |
| |
| CachedClassLoader cached = new CachedClassLoader(); |
| cached.loader = classLoader; |
| cached.sharedLibraries = sharedLibraries; |
| |
| Log.d(TAG, "Created zygote-cached class loader: " + path); |
| mSystemLibsCacheMap.put(path, cached); |
| } |
| |
| private static boolean sharedLibrariesEquals(List<ClassLoader> lhs, List<ClassLoader> rhs) { |
| if (lhs == null) { |
| return rhs == null; |
| } |
| |
| return lhs.equals(rhs); |
| } |
| |
| /** |
| * Returns lib cached with createAndCacheNonBootclasspathSystemClassLoader. This is called by |
| * the zygote during caching. |
| * |
| * If there is an error or the cache is not available, this returns null. |
| */ |
| public ClassLoader getCachedNonBootclasspathSystemLib(String zip, ClassLoader parent, |
| String classLoaderName, List<ClassLoader> sharedLibraries) { |
| if (mSystemLibsCacheMap == null) { |
| return null; |
| } |
| |
| // we only cache top-level libs with the default class loader |
| if (parent != null || classLoaderName != null) { |
| return null; |
| } |
| |
| CachedClassLoader cached = mSystemLibsCacheMap.get(zip); |
| if (cached == null) { |
| return null; |
| } |
| |
| // cached must be built and loaded in the same environment |
| if (!sharedLibrariesEquals(sharedLibraries, cached.sharedLibraries)) { |
| Log.w(TAG, "Unexpected environment loading cached library " + zip + " (real|cached): (" |
| + sharedLibraries + "|" + cached.sharedLibraries + ")"); |
| return null; |
| } |
| |
| Log.d(TAG, "Returning zygote-cached class loader: " + zip); |
| return cached.loader; |
| } |
| |
| /** |
| * Creates a classloader for the WebView APK and places it in the cache of loaders maintained |
| * by this class. This is used in the WebView zygote, where its presence in the cache speeds up |
| * startup and enables memory sharing. |
| */ |
| public ClassLoader createAndCacheWebViewClassLoader(String packagePath, String libsPath, |
| String cacheKey) { |
| // The correct paths are calculated by WebViewZygote in the system server and passed to |
| // us here. We hardcode the other parameters: WebView always targets the current SDK, |
| // does not need to use non-public system libraries, and uses the base classloader as its |
| // parent to permit usage of the cache. |
| // The cache key is passed separately to enable the stub WebView to be cached under the |
| // stub's APK path, when the actual package path is the donor APK. |
| return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null, |
| cacheKey, null /* classLoaderName */, null /* sharedLibraries */, |
| null /* nativeSharedLibraries */, |
| null /*sharedLibrariesLoadedAfterApp*/); |
| } |
| |
| /** |
| * Adds a new path the classpath of the given loader. |
| * @throws IllegalStateException if the provided class loader is not a {@link PathClassLoader}. |
| */ |
| void addPath(ClassLoader classLoader, String dexPath) { |
| if (!(classLoader instanceof PathClassLoader)) { |
| throw new IllegalStateException("class loader is not a PathClassLoader"); |
| } |
| final PathClassLoader baseDexClassLoader = (PathClassLoader) classLoader; |
| baseDexClassLoader.addDexPath(dexPath); |
| } |
| |
| /** |
| * @hide |
| */ |
| void addNative(ClassLoader classLoader, Collection<String> libPaths) { |
| if (!(classLoader instanceof PathClassLoader)) { |
| throw new IllegalStateException("class loader is not a PathClassLoader"); |
| } |
| final PathClassLoader baseDexClassLoader = (PathClassLoader) classLoader; |
| baseDexClassLoader.addNativePath(libPaths); |
| } |
| |
| @UnsupportedAppUsage |
| private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<>(); |
| |
| private static final ApplicationLoaders gApplicationLoaders = new ApplicationLoaders(); |
| |
| private static class CachedClassLoader { |
| ClassLoader loader; |
| |
| /** |
| * The shared libraries used when constructing loader for verification. |
| */ |
| List<ClassLoader> sharedLibraries; |
| } |
| |
| /** |
| * This is a map of zip to associated class loader. |
| */ |
| private Map<String, CachedClassLoader> mSystemLibsCacheMap = null; |
| } |