diff options
| author | 2016-11-21 17:13:45 +0000 | |
|---|---|---|
| committer | 2016-11-21 17:13:45 +0000 | |
| commit | 7e17c6b0f13e059d7c93f12d893d4d6db5feac4f (patch) | |
| tree | 657eca05bef28f22a961530415f52ac3d2e3cc10 | |
| parent | 059022bc1ba2f2bf2ba7f46d79f69444fcf81a78 (diff) | |
| parent | 9565860b0870861e75c3fae8b41f0272b0642017 (diff) | |
Merge "Precreate the classloader for the WebView." am: 566b1c80e4 am: 1feb782f01
am: 9565860b08
Change-Id: I070288a127cbaa08f05b49f66e23ea0ec298040b
| -rw-r--r-- | core/java/android/app/ApplicationLoaders.java | 19 | ||||
| -rw-r--r-- | core/java/android/app/LoadedApk.java | 60 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewFactory.java | 4 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewZygote.java | 25 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/WebViewZygoteInit.java | 25 |
5 files changed, 96 insertions, 37 deletions
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java index 6a73829da154..ef2db4a0d795 100644 --- a/core/java/android/app/ApplicationLoaders.java +++ b/core/java/android/app/ApplicationLoaders.java @@ -16,17 +16,19 @@ package android.app; +import android.os.Build; import android.os.Trace; import android.util.ArrayMap; import com.android.internal.os.PathClassLoaderFactory; import dalvik.system.PathClassLoader; -class ApplicationLoaders { +/** @hide */ +public class ApplicationLoaders { public static ApplicationLoaders getDefault() { return gApplicationLoaders; } - public ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled, + ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled, String librarySearchPath, String libraryPermittedPath, ClassLoader parent) { /* @@ -80,6 +82,19 @@ class ApplicationLoaders { } } + /** + * 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) { + // 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. + return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null); + } + private static native void setupVulkanLayerPath(ClassLoader classLoader, String librarySearchPath); /** diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 7754244cae8e..9ea16a719ceb 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -339,39 +339,43 @@ public final class LoadedApk { * concatenation of both apps' shared library lists. */ - String instrumentationPackageName = activityThread.mInstrumentationPackageName; - String instrumentationAppDir = activityThread.mInstrumentationAppDir; - String[] instrumentationSplitAppDirs = activityThread.mInstrumentationSplitAppDirs; - String instrumentationLibDir = activityThread.mInstrumentationLibDir; - - String instrumentedAppDir = activityThread.mInstrumentedAppDir; - String[] instrumentedSplitAppDirs = activityThread.mInstrumentedSplitAppDirs; - String instrumentedLibDir = activityThread.mInstrumentedLibDir; String[] instrumentationLibs = null; - - if (appDir.equals(instrumentationAppDir) - || appDir.equals(instrumentedAppDir)) { - outZipPaths.clear(); - outZipPaths.add(instrumentationAppDir); - if (instrumentationSplitAppDirs != null) { - Collections.addAll(outZipPaths, instrumentationSplitAppDirs); - } - if (!instrumentationAppDir.equals(instrumentedAppDir)) { - outZipPaths.add(instrumentedAppDir); - if (instrumentedSplitAppDirs != null) { - Collections.addAll(outZipPaths, instrumentedSplitAppDirs); + // activityThread will be null when called from the WebView zygote; just assume + // no instrumentation applies in this case. + if (activityThread != null) { + String instrumentationPackageName = activityThread.mInstrumentationPackageName; + String instrumentationAppDir = activityThread.mInstrumentationAppDir; + String[] instrumentationSplitAppDirs = activityThread.mInstrumentationSplitAppDirs; + String instrumentationLibDir = activityThread.mInstrumentationLibDir; + + String instrumentedAppDir = activityThread.mInstrumentedAppDir; + String[] instrumentedSplitAppDirs = activityThread.mInstrumentedSplitAppDirs; + String instrumentedLibDir = activityThread.mInstrumentedLibDir; + + if (appDir.equals(instrumentationAppDir) + || appDir.equals(instrumentedAppDir)) { + outZipPaths.clear(); + outZipPaths.add(instrumentationAppDir); + if (instrumentationSplitAppDirs != null) { + Collections.addAll(outZipPaths, instrumentationSplitAppDirs); + } + if (!instrumentationAppDir.equals(instrumentedAppDir)) { + outZipPaths.add(instrumentedAppDir); + if (instrumentedSplitAppDirs != null) { + Collections.addAll(outZipPaths, instrumentedSplitAppDirs); + } } - } - if (outLibPaths != null) { - outLibPaths.add(instrumentationLibDir); - if (!instrumentationLibDir.equals(instrumentedLibDir)) { - outLibPaths.add(instrumentedLibDir); + if (outLibPaths != null) { + outLibPaths.add(instrumentationLibDir); + if (!instrumentationLibDir.equals(instrumentedLibDir)) { + outLibPaths.add(instrumentedLibDir); + } } - } - if (!instrumentedAppDir.equals(instrumentationAppDir)) { - instrumentationLibs = getLibrariesFor(instrumentationPackageName); + if (!instrumentedAppDir.equals(instrumentationAppDir)) { + instrumentationLibs = getLibrariesFor(instrumentationPackageName); + } } } diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index f41a8380beab..f1e8fc2d310b 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -56,7 +56,9 @@ import java.util.zip.ZipFile; @SystemApi public final class WebViewFactory { - private static final String CHROMIUM_WEBVIEW_FACTORY = + // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote. + /** @hide */ + public static final String CHROMIUM_WEBVIEW_FACTORY = "com.android.webview.chromium.WebViewChromiumFactoryProvider"; private static final String NULL_WEBVIEW_FACTORY = diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java index bc6e7b4a9dd3..c2069741d6cd 100644 --- a/core/java/android/webkit/WebViewZygote.java +++ b/core/java/android/webkit/WebViewZygote.java @@ -16,14 +16,19 @@ package android.webkit; +import android.app.LoadedApk; import android.content.pm.PackageInfo; import android.os.Build; import android.os.SystemService; import android.os.ZygoteProcess; +import android.text.TextUtils; import android.util.Log; +import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.concurrent.TimeoutException; /** @hide */ @@ -122,11 +127,21 @@ public class WebViewZygote { try { sZygote = new ZygoteProcess("webview_zygote", null); - String packagePath = sPackage.applicationInfo.sourceDir; - String libsPath = sPackage.applicationInfo.nativeLibraryDir; - - Log.d(LOGTAG, "Preloading package " + packagePath + " " + libsPath); - sZygote.preloadPackageForAbi(packagePath, libsPath, Build.SUPPORTED_ABIS[0]); + // All the work below is usually done by LoadedApk, but the zygote can't talk to + // PackageManager or construct a LoadedApk since it's single-threaded pre-fork, so + // doesn't have an ActivityThread and can't use Binder. + // Instead, figure out the paths here, in the system server where we have access to + // the package manager. Reuse the logic from LoadedApk to determine the correct + // paths and pass them to the zygote as strings. + final List<String> zipPaths = new ArrayList<>(10); + final List<String> libPaths = new ArrayList<>(10); + LoadedApk.makePaths(null, sPackage.applicationInfo, zipPaths, libPaths); + final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths); + final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) : + TextUtils.join(File.pathSeparator, zipPaths); + + Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath); + sZygote.preloadPackageForAbi(zip, librarySearchPath, Build.SUPPORTED_ABIS[0]); } catch (Exception e) { Log.e(LOGTAG, "Error connecting to " + serviceName, e); sZygote = null; diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java index 11dd0e8771a7..d968e3c939ab 100644 --- a/core/java/com/android/internal/os/WebViewZygoteInit.java +++ b/core/java/com/android/internal/os/WebViewZygoteInit.java @@ -16,14 +16,17 @@ package com.android.internal.os; +import android.app.ApplicationLoaders; import android.net.LocalSocket; import android.os.Build; import android.system.ErrnoException; import android.system.Os; import android.text.TextUtils; import android.util.Log; +import android.webkit.WebViewFactory; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; /** * Startup class for the WebView zygote process. @@ -52,7 +55,27 @@ class WebViewZygoteInit { @Override protected boolean handlePreloadPackage(String packagePath, String libsPath) { - // TODO: Use preload information to setup the ClassLoader. + // Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that + // our children will reuse the same classloader instead of creating their own. + // This enables us to preload Java and native code in the webview zygote process and + // have the preloaded versions actually be used post-fork. + ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader( + packagePath, libsPath); + + // Once we have the classloader, look up the WebViewFactoryProvider implementation and + // call preloadInZygote() on it to give it the opportunity to preload the native library + // and perform any other initialisation work that should be shared among the children. + try { + Class providerClass = Class.forName(WebViewFactory.CHROMIUM_WEBVIEW_FACTORY, true, + loader); + Object result = providerClass.getMethod("preloadInZygote").invoke(null); + if (!((Boolean)result).booleanValue()) { + Log.e(TAG, "preloadInZygote returned false"); + } + } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | + IllegalAccessException | InvocationTargetException e) { + Log.e(TAG, "Exception while preloading package", e); + } return false; } } |