Merge "Support loading a stub WebView using a donor package." into oc-dev

This commit is contained in:
TreeHugger Robot
2017-03-28 17:11:21 +00:00
committed by Android (Google) Code Review
6 changed files with 101 additions and 23 deletions

View File

@@ -18,6 +18,7 @@ package android.app;
import android.os.Build;
import android.os.Trace;
import android.text.TextUtils;
import android.util.ArrayMap;
import com.android.internal.os.PathClassLoaderFactory;
import dalvik.system.PathClassLoader;
@@ -29,8 +30,16 @@ public class ApplicationLoaders {
}
ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent) {
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent) {
// For normal usage the cache key used is the same as the zip path.
return getClassLoader(zip, targetSdkVersion, isBundled, librarySearchPath,
libraryPermittedPath, parent, zip);
}
private ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent, String cacheKey) {
/*
* This is the parent we use if they pass "null" in. In theory
* this should be the "system" class loader; in practice we
@@ -50,7 +59,7 @@ public class ApplicationLoaders {
* new ClassLoader for the zip archive.
*/
if (parent == baseParent) {
ClassLoader loader = mLoaders.get(zip);
ClassLoader loader = mLoaders.get(cacheKey);
if (loader != null) {
return loader;
}
@@ -71,7 +80,7 @@ public class ApplicationLoaders {
setupVulkanLayerPath(pathClassloader, librarySearchPath);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
mLoaders.put(zip, pathClassloader);
mLoaders.put(cacheKey, pathClassloader);
return pathClassloader;
}
@@ -87,12 +96,16 @@ public class ApplicationLoaders {
* 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);
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);
}
private static native void setupVulkanLayerPath(ClassLoader classLoader, String librarySearchPath);

View File

@@ -487,11 +487,11 @@ public class ZygoteProcess {
* Instructs the zygote to pre-load the classes and native libraries at the given paths
* for the specified abi. Not all zygotes support this function.
*/
public void preloadPackageForAbi(String packagePath, String libsPath, String abi)
throws ZygoteStartFailedEx, IOException {
public void preloadPackageForAbi(String packagePath, String libsPath, String cacheKey,
String abi) throws ZygoteStartFailedEx, IOException {
synchronized(mLock) {
ZygoteState state = openZygoteSocketIfNeeded(abi);
state.writer.write("3");
state.writer.write("4");
state.writer.newLine();
state.writer.write("--preload-package");
@@ -503,6 +503,9 @@ public class ZygoteProcess {
state.writer.write(libsPath);
state.writer.newLine();
state.writer.write(cacheKey);
state.writer.newLine();
state.writer.flush();
}
}

View File

@@ -280,6 +280,44 @@ public final class WebViewFactory {
}
}
/**
* If the ApplicationInfo provided is for a stub WebView, fix up the object to include the
* required values from the donor package. If the ApplicationInfo is for a full WebView,
* leave it alone. Throws MissingWebViewPackageException if the donor is missing.
*/
private static void fixupStubApplicationInfo(ApplicationInfo ai, PackageManager pm) {
String donorPackageName = null;
if (ai.metaData != null) {
donorPackageName = ai.metaData.getString("com.android.webview.WebViewDonorPackage");
}
if (donorPackageName != null) {
PackageInfo donorPackage;
try {
donorPackage = pm.getPackageInfo(
donorPackageName,
PackageManager.GET_SHARED_LIBRARY_FILES
| PackageManager.MATCH_DEBUG_TRIAGED_MISSING
| PackageManager.MATCH_UNINSTALLED_PACKAGES
| PackageManager.MATCH_FACTORY_ONLY);
} catch (PackageManager.NameNotFoundException e) {
throw new MissingWebViewPackageException("Failed to find donor package: " +
donorPackageName);
}
ApplicationInfo donorInfo = donorPackage.applicationInfo;
// Replace the stub's code locations with the donor's.
ai.sourceDir = donorInfo.sourceDir;
ai.splitSourceDirs = donorInfo.splitSourceDirs;
ai.nativeLibraryDir = donorInfo.nativeLibraryDir;
ai.secondaryNativeLibraryDir = donorInfo.secondaryNativeLibraryDir;
// Copy the donor's primary and secondary ABIs, since the stub doesn't have native code
// and so they are unset.
ai.primaryCpuAbi = donorInfo.primaryCpuAbi;
ai.secondaryCpuAbi = donorInfo.secondaryCpuAbi;
}
}
private static Context getWebViewContextAndSetProvider() {
Application initialApplication = AppGlobals.getInitialApplication();
try {
@@ -307,9 +345,10 @@ public final class WebViewFactory {
}
// Fetch package info and verify it against the chosen package
PackageInfo newPackageInfo = null;
PackageManager pm = initialApplication.getPackageManager();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "PackageManager.getPackageInfo()");
try {
newPackageInfo = initialApplication.getPackageManager().getPackageInfo(
newPackageInfo = pm.getPackageInfo(
response.packageInfo.packageName,
PackageManager.GET_SHARED_LIBRARY_FILES
| PackageManager.MATCH_DEBUG_TRIAGED_MISSING
@@ -328,12 +367,15 @@ public final class WebViewFactory {
// failure
verifyPackageInfo(response.packageInfo, newPackageInfo);
ApplicationInfo ai = newPackageInfo.applicationInfo;
fixupStubApplicationInfo(ai, pm);
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
"initialApplication.createApplicationContext");
try {
// Construct an app context to load the Java code into the current app.
Context webViewContext = initialApplication.createApplicationContext(
newPackageInfo.applicationInfo,
ai,
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
sPackageInfo = newPackageInfo;
return webViewContext;
@@ -449,7 +491,11 @@ public final class WebViewFactory {
*/
public static int onWebViewProviderChanged(PackageInfo packageInfo) {
String[] nativeLibs = null;
String originalSourceDir = packageInfo.applicationInfo.sourceDir;
try {
fixupStubApplicationInfo(packageInfo.applicationInfo,
AppGlobals.getInitialApplication().getPackageManager());
nativeLibs = WebViewFactory.getWebViewNativeLibraryPaths(packageInfo);
if (nativeLibs != null) {
long newVmSize = 0L;
@@ -498,7 +544,7 @@ public final class WebViewFactory {
Log.e(LOGTAG, "error preparing webview native library", t);
}
WebViewZygote.onWebViewProviderChanged(packageInfo);
WebViewZygote.onWebViewProviderChanged(packageInfo, originalSourceDir);
return prepareWebViewInSystemServer(nativeLibs);
}

View File

@@ -66,6 +66,13 @@ public class WebViewZygote {
@GuardedBy("sLock")
private static PackageInfo sPackage;
/**
* Cache key for the selected WebView package's classloader. This is set from
* #onWebViewProviderChanged().
*/
@GuardedBy("sLock")
private static String sPackageCacheKey;
/**
* Flag for whether multi-process WebView is enabled. If this is false, the zygote
* will not be started.
@@ -118,9 +125,10 @@ public class WebViewZygote {
}
}
public static void onWebViewProviderChanged(PackageInfo packageInfo) {
public static void onWebViewProviderChanged(PackageInfo packageInfo, String cacheKey) {
synchronized (sLock) {
sPackage = packageInfo;
sPackageCacheKey = cacheKey;
// If multi-process is not enabled, then do not start the zygote service.
if (!sMultiprocessEnabled) {
@@ -210,7 +218,8 @@ public class WebViewZygote {
TextUtils.join(File.pathSeparator, zipPaths);
Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath);
sZygote.preloadPackageForAbi(zip, librarySearchPath, Build.SUPPORTED_ABIS[0]);
sZygote.preloadPackageForAbi(zip, librarySearchPath, sPackageCacheKey,
Build.SUPPORTED_ABIS[0]);
} catch (Exception e) {
Log.e(LOGTAG, "Error connecting to " + serviceName, e);
sZygote = null;

View File

@@ -26,6 +26,7 @@ import android.util.Log;
import android.webkit.WebViewFactory;
import android.webkit.WebViewFactoryProvider;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
@@ -67,16 +68,20 @@ class WebViewZygoteInit {
}
@Override
protected boolean handlePreloadPackage(String packagePath, String libsPath) {
protected boolean handlePreloadPackage(String packagePath, String libsPath,
String cacheKey) {
// 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);
packagePath, libsPath, cacheKey);
// Add the APK to the Zygote's list of allowed files for children.
Zygote.nativeAllowFileAcrossFork(packagePath);
String[] packageList = TextUtils.split(packagePath, File.pathSeparator);
for (String packageEntry : packageList) {
Zygote.nativeAllowFileAcrossFork(packageEntry);
}
// 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

View File

@@ -177,7 +177,7 @@ class ZygoteConnection {
if (parsedArgs.preloadPackage != null) {
return handlePreloadPackage(parsedArgs.preloadPackage,
parsedArgs.preloadPackageLibs);
parsedArgs.preloadPackageLibs, parsedArgs.preloadPackageCacheKey);
}
if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
@@ -314,7 +314,7 @@ class ZygoteConnection {
return ZygoteInit.isPreloadComplete();
}
protected boolean handlePreloadPackage(String packagePath, String libsPath) {
protected boolean handlePreloadPackage(String packagePath, String libsPath, String cacheKey) {
throw new RuntimeException("Zyogte does not support package preloading");
}
@@ -428,6 +428,7 @@ class ZygoteConnection {
*/
String preloadPackage;
String preloadPackageLibs;
String preloadPackageCacheKey;
/**
* Whether this is a request to start preloading the default resources and classes.
@@ -599,6 +600,7 @@ class ZygoteConnection {
} else if (arg.equals("--preload-package")) {
preloadPackage = args[++curArg];
preloadPackageLibs = args[++curArg];
preloadPackageCacheKey = args[++curArg];
} else if (arg.equals("--preload-default")) {
preloadDefault = true;
} else {