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

Commit 08cfaf67 authored by Torne (Richard Coles)'s avatar Torne (Richard Coles)
Browse files

Use the WebView's loader library to load the real library.

Load libwebviewchromiumloader and use it to load the real WebView
library, to enable sharing of the relro segment between different
application processes without requiring that the library be preloaded in
the zygote. A system service is added to track whether the relro segment
file has been prepared, and block loading of the library until it has
been.

Bug: 13005501
Change-Id: I846b37c7b8e2a4eb8a39e4fd455bccbb2048c173
parent b5de924f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -241,6 +241,7 @@ LOCAL_SRC_FILES += \
	core/java/android/view/IWindowManager.aidl \
	core/java/android/view/IWindowSession.aidl \
	core/java/android/view/IWindowSessionCallback.aidl \
	core/java/android/webkit/IWebViewUpdateService.aidl \
	core/java/android/speech/IRecognitionListener.aidl \
	core/java/android/speech/IRecognitionService.aidl \
	core/java/android/speech/tts/ITextToSpeechCallback.aidl \
+6 −0
Original line number Diff line number Diff line
@@ -130,6 +130,12 @@ public class Process {
     */
    public static final int PACKAGE_INFO_GID = 1032;

    /**
     * Defines the UID/GID for the shared RELRO file updater process.
     * @hide
     */
    public static final int SHARED_RELRO_UID = 1037;

    /**
     * Defines the start of a range of UIDs (and GIDs), going from this
     * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
+36 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.webkit;

/**
 * Private service to wait for the updatable WebView to be ready for use.
 * @hide
 */
interface IWebViewUpdateService {

    /**
     * Used by the relro file creator to notify the service that it's done.
     */
    void notifyRelroCreationCompleted(boolean is64Bit, boolean success);

    /**
     * Used by WebViewFactory to block loading of WebView code until
     * preparations are complete.
     */
    void waitForRelroCreationCompleted(boolean is64Bit);

}
+140 −0
Original line number Diff line number Diff line
@@ -16,9 +16,18 @@

package android.webkit;

import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.util.AndroidRuntimeException;
import android.util.Log;
import dalvik.system.VMRuntime;

import java.io.File;

import com.android.internal.os.Zygote;

/**
 * Top level factory, used creating all the main WebView implementation classes.
@@ -33,6 +42,17 @@ public final class WebViewFactory {
    private static final String NULL_WEBVIEW_FACTORY =
            "com.android.webview.nullwebview.NullWebViewFactoryProvider";

    // TODO(torne): we need to use a system property instead of hardcoding the library paths to
    // enable it to be changed when a webview update apk is installed.
    private static final String CHROMIUM_WEBVIEW_NATIVE_LIB_32 =
            "/system/lib/libwebviewchromium.so";
    private static final String CHROMIUM_WEBVIEW_NATIVE_LIB_64 =
            "/system/lib64/libwebviewchromium.so";
    private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_32 =
            "/data/misc/shared_relro/libwebviewchromium32.relro";
    private static final String CHROMIUM_WEBVIEW_NATIVE_RELRO_64 =
            "/data/misc/shared_relro/libwebviewchromium64.relro";

    private static final String LOGTAG = "WebViewFactory";

    private static final boolean DEBUG = false;
@@ -41,6 +61,7 @@ public final class WebViewFactory {
    // same provider.
    private static WebViewFactoryProvider sProviderInstance;
    private static final Object sProviderLock = new Object();
    private static boolean sAddressSpaceReserved = false;

    static WebViewFactoryProvider getProvider() {
        synchronized (sProviderLock) {
@@ -48,6 +69,8 @@ public final class WebViewFactory {
            // us honest and minimize usage of WebView internals when binding the proxy.
            if (sProviderInstance != null) return sProviderInstance;

            loadNativeLibrary();

            Class<WebViewFactoryProvider> providerClass;
            try {
                providerClass = getFactoryClass();
@@ -78,4 +101,121 @@ public final class WebViewFactory {
            return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
        }
    }

    /**
     * Perform any WebView loading preparations that must happen in the zygote.
     * Currently, this means allocating address space to load the real JNI library later.
     */
    public static void prepareWebViewInZygote() {
        try {
            System.loadLibrary("webviewchromium_loader");
            sAddressSpaceReserved = nativeReserveAddressSpace(CHROMIUM_WEBVIEW_NATIVE_LIB_32,
                                                              CHROMIUM_WEBVIEW_NATIVE_LIB_64);
            if (sAddressSpaceReserved) {
                if (DEBUG) Log.v(LOGTAG, "address space reserved");
            } else {
                Log.e(LOGTAG, "reserving address space failed");
            }
        } catch (Throwable e) {
            // Log and discard errors at this stage as we must not crash the zygote.
            Log.e(LOGTAG, "error preparing native loader", e);
        }
    }

    /**
     * Perform any WebView loading preparations that must happen at boot from the system server,
     * after the package manager has started.
     * This must be called in the system server.
     * Currently, this means spawning the child processes which will create the relro files.
     */
    public static void prepareWebViewInSystemServer() {
        if (DEBUG) Log.v(LOGTAG, "creating relro files");
        if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_64).exists()) {
            createRelroFile(Build.SUPPORTED_64_BIT_ABIS[0]);
        }
        if (new File(CHROMIUM_WEBVIEW_NATIVE_LIB_32).exists()) {
            createRelroFile(Build.SUPPORTED_32_BIT_ABIS[0]);
        }
    }

    private static void createRelroFile(String abi) {
        try {
            Process.start("android.webkit.WebViewFactory$RelroFileCreator",
                          "WebViewLoader-" + abi,
                          Process.SHARED_RELRO_UID,
                          Process.SHARED_RELRO_UID,
                          null,
                          0,                 // TODO(torne): do we need to set debug flags?
                          Zygote.MOUNT_EXTERNAL_NONE,
                          Build.VERSION.SDK_INT,
                          null,
                          abi,
                          null);
        } catch (Throwable e) {
            // Log and discard errors as we must not crash the system server.
            Log.e(LOGTAG, "error starting relro file creator for abi " + abi, e);
        }
    }

    private static class RelroFileCreator {
        // Called in an unprivileged child process to create the relro file.
        public static void main(String[] args) {
            if (!sAddressSpaceReserved) {
                Log.e(LOGTAG, "can't create relro file; address space not reserved");
                return;
            }
            boolean result = nativeCreateRelroFile(CHROMIUM_WEBVIEW_NATIVE_LIB_32,
                                                   CHROMIUM_WEBVIEW_NATIVE_LIB_64,
                                                   CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
                                                   CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
            if (!result) {
                Log.e(LOGTAG, "failed to create relro file");
            } else if (DEBUG) {
                Log.v(LOGTAG, "created relro file");
            }
            try {
                getUpdateService().notifyRelroCreationCompleted(VMRuntime.getRuntime().is64Bit(),
                                                                result);
            } catch (RemoteException e) {
                Log.e(LOGTAG, "error notifying update service", e);
            }

            // Must explicitly exit or else this process will just sit around after we return.
            System.exit(0);
        }
    }

    private static void loadNativeLibrary() {
        if (!sAddressSpaceReserved) {
            Log.e(LOGTAG, "can't load with relro file; address space not reserved");
            return;
        }

        try {
            getUpdateService().waitForRelroCreationCompleted(VMRuntime.getRuntime().is64Bit());
        } catch (RemoteException e) {
            Log.e(LOGTAG, "error waiting for relro creation, proceeding without", e);
            return;
        }

        boolean result = nativeLoadWithRelroFile(CHROMIUM_WEBVIEW_NATIVE_LIB_32,
                                                 CHROMIUM_WEBVIEW_NATIVE_LIB_64,
                                                 CHROMIUM_WEBVIEW_NATIVE_RELRO_32,
                                                 CHROMIUM_WEBVIEW_NATIVE_RELRO_64);
        if (!result) {
            Log.w(LOGTAG, "failed to load with relro file, proceeding without");
        } else if (DEBUG) {
            Log.v(LOGTAG, "loaded with relro file");
        }
    }

    private static IWebViewUpdateService getUpdateService() {
        return IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate"));
    }

    private static native boolean nativeReserveAddressSpace(String lib32, String lib64);
    private static native boolean nativeCreateRelroFile(String lib32, String lib64,
                                                        String relro32, String relro64);
    private static native boolean nativeLoadWithRelroFile(String lib32, String lib64,
                                                          String relro32, String relro64);
}
+4 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.system.Os;
import android.system.OsConstants;
import android.util.EventLog;
import android.util.Log;
import android.webkit.WebViewFactory;

import dalvik.system.VMRuntime;

@@ -250,6 +251,9 @@ public class ZygoteInit {
        preloadClasses();
        preloadResources();
        preloadOpenGL();
        // Ask the WebViewFactory to do any initialization that must run in the zygote process,
        // for memory sharing purposes.
        WebViewFactory.prepareWebViewInZygote();
        Log.d(TAG, "end preload");
    }

Loading