diff options
-rw-r--r-- | core/java/android/app/ApplicationLoaders.java | 151 | ||||
-rw-r--r-- | core/java/android/app/LoadedApk.java | 2 | ||||
-rw-r--r-- | core/java/com/android/internal/os/ZygoteInit.java | 31 |
3 files changed, 182 insertions, 2 deletions
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java index 9ef24c6c2aeb..faa30f3a98b8 100644 --- a/core/java/android/app/ApplicationLoaders.java +++ b/core/java/android/app/ApplicationLoaders.java @@ -17,20 +17,27 @@ package android.app; import android.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; @@ -54,6 +61,26 @@ public class ApplicationLoaders { libraryPermittedPath, parent, zip, classLoaderName, sharedLibraries); } + /** + * 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) { + ClassLoader loader = getCachedNonBootclasspathSystemLib(zip, parent, classLoaderName, + sharedLibraries); + if (loader != null) { + return loader; + } + + return getClassLoaderWithSharedLibraries(zip, targetSdkVersion, isBundled, + librarySearchPath, libraryPermittedPath, parent, classLoaderName, sharedLibraries); + } + private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled, String librarySearchPath, String libraryPermittedPath, ClassLoader parent, String cacheKey, @@ -95,7 +122,9 @@ public class ApplicationLoaders { classloader, librarySearchPath, libraryPermittedPath); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - mLoaders.put(cacheKey, classloader); + if (cacheKey != null) { + mLoaders.put(cacheKey, classloader); + } return classloader; } @@ -108,6 +137,112 @@ public class ApplicationLoaders { } /** + * 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(SharedLibraryInfo[] libs) { + if (mSystemLibsCacheMap != null) { + Log.wtf(TAG, "Already cached."); + return; + } + + mSystemLibsCacheMap = new HashMap<String, CachedClassLoader>(); + + for (SharedLibraryInfo lib : libs) { + createAndCacheNonBootclasspathSystemClassLoader(lib); + } + } + + /** + * Caches a single non-bootclasspath class loader. + * + * All of this library's dependencies must have previously been cached. + */ + 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) { + Log.e(TAG, "Failed to find dependency " + dependencyPath + + " of cached library " + path); + return; + } + + 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*/); + + if (classLoader == null) { + Log.e(TAG, "Failed to cache " + path); + return; + } + + 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. + */ + private 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 for cached library: (" + 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. @@ -151,4 +286,18 @@ public class ApplicationLoaders { 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; } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 0f1ba390cd7a..53849ba92339 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -675,7 +675,7 @@ public final class LoadedApk { // Shared libraries get a null parent: this has the side effect of having canonicalized // shared libraries using ApplicationLoaders cache, which is the behavior we want. - return ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(jars, + return ApplicationLoaders.getDefault().getSharedLibraryClassLoaderWithSharedLibraries(jars, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath, libraryPermittedPath, /* parent */ null, /* classLoaderName */ null, sharedLibraries); diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index ce1ee3dc957a..2abc8c080ac1 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -22,6 +22,8 @@ import static android.system.OsConstants.S_IRWXO; import android.annotation.UnsupportedAppUsage; import android.content.res.Resources; import android.content.res.TypedArray; +import android.app.ApplicationLoaders; +import android.content.pm.SharedLibraryInfo; import android.os.Build; import android.os.Environment; import android.os.IInstalld; @@ -141,6 +143,9 @@ public class ZygoteInit { bootTimingsTraceLog.traceBegin("PreloadClasses"); preloadClasses(); bootTimingsTraceLog.traceEnd(); // PreloadClasses + bootTimingsTraceLog.traceBegin("CacheNonBootClasspathClassLoaders"); + cacheNonBootClasspathClassLoaders(); + bootTimingsTraceLog.traceEnd(); // CacheNonBootClasspathClassLoaders bootTimingsTraceLog.traceBegin("PreloadResources"); preloadResources(); bootTimingsTraceLog.traceEnd(); // PreloadResources @@ -347,6 +352,32 @@ public class ZygoteInit { } /** + * Load in things which are used by many apps but which cannot be put in the boot + * classpath. + */ + private static void cacheNonBootClasspathClassLoaders() { + // These libraries used to be part of the bootclasspath, but had to be removed. + // Old system applications still get them for backwards compatibility reasons, + // so they are cached here in order to preserve performance characteristics. + SharedLibraryInfo hidlBase = new SharedLibraryInfo( + "/system/framework/android.hidl.base-V1.0-java.jar", null /*packageName*/, + null /*codePaths*/, null /*name*/, 0 /*version*/, SharedLibraryInfo.TYPE_BUILTIN, + null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/); + SharedLibraryInfo hidlManager = new SharedLibraryInfo( + "/system/framework/android.hidl.manager-V1.0-java.jar", null /*packageName*/, + null /*codePaths*/, null /*name*/, 0 /*version*/, SharedLibraryInfo.TYPE_BUILTIN, + null /*declaringPackage*/, null /*dependentPackages*/, null /*dependencies*/); + hidlManager.addDependency(hidlBase); + + ApplicationLoaders.getDefault().createAndCacheNonBootclasspathSystemClassLoaders( + new SharedLibraryInfo[]{ + // ordered dependencies first + hidlBase, + hidlManager, + }); + } + + /** * Load in commonly used resources, so they can be shared across processes. * * These tend to be a few Kbytes, but are frequently in the 20-40K range, and occasionally even |