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;
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);
/**

View File

@ -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;
// 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;
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);
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 (outLibPaths != null) {
outLibPaths.add(instrumentationLibDir);
if (!instrumentationLibDir.equals(instrumentedLibDir)) {
outLibPaths.add(instrumentedLibDir);
if (!instrumentationAppDir.equals(instrumentedAppDir)) {
outZipPaths.add(instrumentedAppDir);
if (instrumentedSplitAppDirs != null) {
Collections.addAll(outZipPaths, instrumentedSplitAppDirs);
}
}
}
if (!instrumentedAppDir.equals(instrumentationAppDir)) {
instrumentationLibs = getLibrariesFor(instrumentationPackageName);
if (outLibPaths != null) {
outLibPaths.add(instrumentationLibDir);
if (!instrumentationLibDir.equals(instrumentedLibDir)) {
outLibPaths.add(instrumentedLibDir);
}
}
if (!instrumentedAppDir.equals(instrumentationAppDir)) {
instrumentationLibs = getLibrariesFor(instrumentationPackageName);
}
}
}

View File

@ -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 =

View File

@ -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;
// 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 " + packagePath + " " + libsPath);
sZygote.preloadPackageForAbi(packagePath, libsPath, Build.SUPPORTED_ABIS[0]);
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;

View File

@ -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;
}
}