320 lines
13 KiB
Java
320 lines
13 KiB
Java
/*
|
|
* 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;
|
|
}
|