Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 758f80ea authored by Torne (Richard Coles)'s avatar Torne (Richard Coles) Committed by android-build-merger
Browse files

Merge "Support loading a stub WebView using a donor package." into oc-dev am: 7ec7ef2b

am: 07cb93dc

Change-Id: Ieadf02a034f991c3f7c6f745067eaa316a8dcbcf
parents 3868b389 07cb93dc
Loading
Loading
Loading
Loading
+23 −10
Original line number Diff line number Diff line
@@ -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;
@@ -31,6 +32,14 @@ public class ApplicationLoaders {
    ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
                               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) {
    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.
      return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null);
        // 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);
+6 −3
Original line number Diff line number Diff line
@@ -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();
        }
    }
+49 −3
Original line number Diff line number Diff line
@@ -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);
    }
+11 −2
Original line number Diff line number Diff line
@@ -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;
+8 −3
Original line number Diff line number Diff line
@@ -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
Loading