Merge "Precreate the classloader for the WebView."

am: 566b1c80e4

Change-Id: Ic79079748dae515cb8d89ef99148755ac65f1d47
This commit is contained in:
Torne (Richard Coles)
2016-11-21 17:04:22 +00:00
committed by android-build-merger
5 changed files with 95 additions and 36 deletions

View File

@@ -16,17 +16,19 @@
package android.app; package android.app;
import android.os.Build;
import android.os.Trace; import android.os.Trace;
import android.util.ArrayMap; import android.util.ArrayMap;
import com.android.internal.os.PathClassLoaderFactory; import com.android.internal.os.PathClassLoaderFactory;
import dalvik.system.PathClassLoader; import dalvik.system.PathClassLoader;
class ApplicationLoaders { /** @hide */
public class ApplicationLoaders {
public static ApplicationLoaders getDefault() { public static ApplicationLoaders getDefault() {
return gApplicationLoaders; return gApplicationLoaders;
} }
public ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled, ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath, String librarySearchPath, String libraryPermittedPath,
ClassLoader parent) { 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); private static native void setupVulkanLayerPath(ClassLoader classLoader, String librarySearchPath);
/** /**

View File

@@ -339,6 +339,10 @@ public final class LoadedApk {
* concatenation of both apps' shared library lists. * concatenation of both apps' shared library lists.
*/ */
String[] instrumentationLibs = null;
// 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 instrumentationPackageName = activityThread.mInstrumentationPackageName;
String instrumentationAppDir = activityThread.mInstrumentationAppDir; String instrumentationAppDir = activityThread.mInstrumentationAppDir;
String[] instrumentationSplitAppDirs = activityThread.mInstrumentationSplitAppDirs; String[] instrumentationSplitAppDirs = activityThread.mInstrumentationSplitAppDirs;
@@ -347,7 +351,6 @@ public final class LoadedApk {
String instrumentedAppDir = activityThread.mInstrumentedAppDir; String instrumentedAppDir = activityThread.mInstrumentedAppDir;
String[] instrumentedSplitAppDirs = activityThread.mInstrumentedSplitAppDirs; String[] instrumentedSplitAppDirs = activityThread.mInstrumentedSplitAppDirs;
String instrumentedLibDir = activityThread.mInstrumentedLibDir; String instrumentedLibDir = activityThread.mInstrumentedLibDir;
String[] instrumentationLibs = null;
if (appDir.equals(instrumentationAppDir) if (appDir.equals(instrumentationAppDir)
|| appDir.equals(instrumentedAppDir)) { || appDir.equals(instrumentedAppDir)) {
@@ -374,6 +377,7 @@ public final class LoadedApk {
instrumentationLibs = getLibrariesFor(instrumentationPackageName); instrumentationLibs = getLibrariesFor(instrumentationPackageName);
} }
} }
}
if (outLibPaths != null) { if (outLibPaths != null) {
if (outLibPaths.isEmpty()) { if (outLibPaths.isEmpty()) {

View File

@@ -56,7 +56,9 @@ import java.util.zip.ZipFile;
@SystemApi @SystemApi
public final class WebViewFactory { 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"; "com.android.webview.chromium.WebViewChromiumFactoryProvider";
private static final String NULL_WEBVIEW_FACTORY = private static final String NULL_WEBVIEW_FACTORY =

View File

@@ -16,14 +16,19 @@
package android.webkit; package android.webkit;
import android.app.LoadedApk;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.os.Build; import android.os.Build;
import android.os.SystemService; import android.os.SystemService;
import android.os.ZygoteProcess; import android.os.ZygoteProcess;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
/** @hide */ /** @hide */
@@ -122,11 +127,21 @@ public class WebViewZygote {
try { try {
sZygote = new ZygoteProcess("webview_zygote", null); sZygote = new ZygoteProcess("webview_zygote", null);
String packagePath = sPackage.applicationInfo.sourceDir; // All the work below is usually done by LoadedApk, but the zygote can't talk to
String libsPath = sPackage.applicationInfo.nativeLibraryDir; // 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 " + packagePath + " " + libsPath); Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath);
sZygote.preloadPackageForAbi(packagePath, libsPath, Build.SUPPORTED_ABIS[0]); sZygote.preloadPackageForAbi(zip, librarySearchPath, Build.SUPPORTED_ABIS[0]);
} catch (Exception e) { } catch (Exception e) {
Log.e(LOGTAG, "Error connecting to " + serviceName, e); Log.e(LOGTAG, "Error connecting to " + serviceName, e);
sZygote = null; sZygote = null;

View File

@@ -16,14 +16,17 @@
package com.android.internal.os; package com.android.internal.os;
import android.app.ApplicationLoaders;
import android.net.LocalSocket; import android.net.LocalSocket;
import android.os.Build; import android.os.Build;
import android.system.ErrnoException; import android.system.ErrnoException;
import android.system.Os; import android.system.Os;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.webkit.WebViewFactory;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
/** /**
* Startup class for the WebView zygote process. * Startup class for the WebView zygote process.
@@ -52,7 +55,27 @@ class WebViewZygoteInit {
@Override @Override
protected boolean handlePreloadPackage(String packagePath, String libsPath) { 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; return false;
} }
} }