Merge "Support loading a stub WebView using a donor package." into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
7ec7ef2ba3
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user