Merge "Create the WebViewZygote and implement WebViewZygoteInit."
diff --git a/cmds/webview_zygote/Android.mk b/cmds/webview_zygote/Android.mk
new file mode 100644
index 0000000..66e762c
--- /dev/null
+++ b/cmds/webview_zygote/Android.mk
@@ -0,0 +1,49 @@
+#
+# Copyright (C) 2016 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := webview_zygote
+
+LOCAL_SRC_FILES := webview_zygote.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libandroid_runtime \
+	libbinder \
+	liblog \
+	libcutils \
+	libutils
+
+LOCAL_LDFLAGS_32 := -Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
+LOCAL_LDFLAGS_64 := -Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
+
+LOCAL_INIT_RC := webview_zygote32.rc
+
+# Always include the 32-bit version of webview_zygote. If the target is 64-bit,
+# also include the 64-bit webview_zygote.
+ifeq ($(TARGET_SUPPORTS_64_BIT_APPS),true)
+	LOCAL_INIT_RC += webview_zygote64.rc
+endif
+
+LOCAL_MULTILIB := both
+
+LOCAL_MODULE_STEM_32 := webview_zygote32
+LOCAL_MODULE_STEM_64 := webview_zygote64
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/webview_zygote/webview_zygote.cpp b/cmds/webview_zygote/webview_zygote.cpp
new file mode 100644
index 0000000..88fee64
--- /dev/null
+++ b/cmds/webview_zygote/webview_zygote.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+
+#define LOG_TAG "WebViewZygote"
+
+#include <sys/prctl.h>
+
+#include <android_runtime/AndroidRuntime.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class WebViewRuntime : public AndroidRuntime {
+public:
+    WebViewRuntime(char* argBlockStart, size_t argBlockSize)
+        : AndroidRuntime(argBlockStart, argBlockSize) {}
+
+    ~WebViewRuntime() override {}
+
+    void onStarted() override {
+        // Nothing to do since this is a zygote server.
+    }
+
+    void onVmCreated(JNIEnv*) override {
+        // Nothing to do when the VM is created in the zygote.
+    }
+
+    void onZygoteInit() override {
+        // Called after a new process is forked.
+        sp<ProcessState> proc = ProcessState::self();
+        proc->startThreadPool();
+    }
+
+    void onExit(int code) override {
+        IPCThreadState::self()->stopProcess();
+        AndroidRuntime::onExit(code);
+    }
+};
+
+}  // namespace android
+
+int main(int argc, char* const argv[]) {
+    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
+        LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));
+        return 12;
+    }
+
+    size_t argBlockSize = 0;
+    for (int i = 0; i < argc; ++i) {
+        argBlockSize += strlen(argv[i]) + 1;
+    }
+
+    android::WebViewRuntime runtime(argv[0], argBlockSize);
+    runtime.addOption("-Xzygote");
+
+    android::Vector<android::String8> args;
+    runtime.start("com.android.internal.os.WebViewZygoteInit", args, /*zygote=*/ true);
+}
diff --git a/cmds/webview_zygote/webview_zygote32.rc b/cmds/webview_zygote/webview_zygote32.rc
new file mode 100644
index 0000000..b7decc8
--- /dev/null
+++ b/cmds/webview_zygote/webview_zygote32.rc
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2016 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.
+#
+
+service webview_zygote32 /system/bin/webview_zygote32
+    user webview_zygote
+    socket webview_zygote stream 660 webview_zygote system
+
+on property:init.svc.zygote=stopped
+    stop webview_zygote32
diff --git a/cmds/webview_zygote/webview_zygote64.rc b/cmds/webview_zygote/webview_zygote64.rc
new file mode 100644
index 0000000..2935b28
--- /dev/null
+++ b/cmds/webview_zygote/webview_zygote64.rc
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2016 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.
+#
+
+service webview_zygote64 /system/bin/webview_zygote64
+    user webview_zygote
+    socket webview_zygote stream 660 webview_zygote system
+
+on property:init.svc.zygote=stopped
+    stop webview_zygote64
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 6aa9fac..3fccdb0 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -18,6 +18,7 @@
 
 import android.system.Os;
 import android.util.Log;
+import android.webkit.WebViewZygote;
 import dalvik.system.VMRuntime;
 
 /**
@@ -131,6 +132,12 @@
     public static final int CAMERASERVER_UID = 1047;
 
     /**
+     * Defines the UID/GID for the WebView zygote process.
+     * @hide
+     */
+    public static final int WEBVIEW_ZYGOTE_UID = 1051;
+
+    /**
      * Defines the start of a range of UIDs (and GIDs), going from this
      * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning
      * to applications.
@@ -417,6 +424,22 @@
                     abi, instructionSet, appDataDir, zygoteArgs);
     }
 
+    /** @hide */
+    public static final ProcessStartResult startWebView(final String processClass,
+                                  final String niceName,
+                                  int uid, int gid, int[] gids,
+                                  int debugFlags, int mountExternal,
+                                  int targetSdkVersion,
+                                  String seInfo,
+                                  String abi,
+                                  String instructionSet,
+                                  String appDataDir,
+                                  String[] zygoteArgs) {
+        return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
+                    debugFlags, mountExternal, targetSdkVersion, seInfo,
+                    abi, instructionSet, appDataDir, zygoteArgs);
+    }
+
     /**
      * Returns elapsed milliseconds of the time this process has run.
      * @return  Returns the number of milliseconds this process has return.
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index d7a7296..d5206d4 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -19,7 +19,9 @@
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.Zygote;
+import com.android.internal.util.Preconditions;
 import java.io.BufferedWriter;
 import java.io.DataInputStream;
 import java.io.IOException;
@@ -110,7 +112,8 @@
             }
 
             String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
-            Log.i("Zygote", "Process: zygote socket opened, supported ABIS: " + abiListString);
+            Log.i("Zygote", "Process: zygote socket " + socketAddress + " opened, supported ABIS: "
+                    + abiListString);
 
             return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
                     Arrays.asList(abiListString.split(",")));
@@ -136,6 +139,13 @@
     }
 
     /**
+     * Lock object to protect access to the two ZygoteStates below. This lock must be
+     * acquired while communicating over the ZygoteState's socket, to prevent
+     * interleaved access.
+     */
+    private final Object mLock = new Object();
+
+    /**
      * The state of the connection to the primary zygote.
      */
     private ZygoteState primaryZygoteState;
@@ -207,6 +217,7 @@
      *
      * @throws ZygoteStartFailedEx if the query failed.
      */
+    @GuardedBy("mLock")
     private static String getAbiList(BufferedWriter writer, DataInputStream inputStream)
             throws IOException {
         // Each query starts with the argument count (1 in this case)
@@ -233,6 +244,7 @@
      *
      * @throws ZygoteStartFailedEx if process start failed for any reason
      */
+    @GuardedBy("mLock")
     private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
             ZygoteState zygoteState, ArrayList<String> args)
             throws ZygoteStartFailedEx {
@@ -311,90 +323,90 @@
                                                       String appDataDir,
                                                       String[] extraArgs)
                                                       throws ZygoteStartFailedEx {
-        synchronized(Process.class) {
-            ArrayList<String> argsForZygote = new ArrayList<String>();
+        ArrayList<String> argsForZygote = new ArrayList<String>();
 
-            // --runtime-args, --setuid=, --setgid=,
-            // and --setgroups= must go first
-            argsForZygote.add("--runtime-args");
-            argsForZygote.add("--setuid=" + uid);
-            argsForZygote.add("--setgid=" + gid);
-            if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
-                argsForZygote.add("--enable-jni-logging");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
-                argsForZygote.add("--enable-safemode");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
-                argsForZygote.add("--enable-debugger");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
-                argsForZygote.add("--enable-checkjni");
-            }
-            if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
-                argsForZygote.add("--generate-debug-info");
-            }
-            if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
-                argsForZygote.add("--always-jit");
-            }
-            if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
-                argsForZygote.add("--native-debuggable");
-            }
-            if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
-                argsForZygote.add("--enable-assert");
-            }
-            if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
-                argsForZygote.add("--mount-external-default");
-            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
-                argsForZygote.add("--mount-external-read");
-            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
-                argsForZygote.add("--mount-external-write");
-            }
-            argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
+        // --runtime-args, --setuid=, --setgid=,
+        // and --setgroups= must go first
+        argsForZygote.add("--runtime-args");
+        argsForZygote.add("--setuid=" + uid);
+        argsForZygote.add("--setgid=" + gid);
+        if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
+            argsForZygote.add("--enable-jni-logging");
+        }
+        if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
+            argsForZygote.add("--enable-safemode");
+        }
+        if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
+            argsForZygote.add("--enable-debugger");
+        }
+        if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
+            argsForZygote.add("--enable-checkjni");
+        }
+        if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
+            argsForZygote.add("--generate-debug-info");
+        }
+        if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
+            argsForZygote.add("--always-jit");
+        }
+        if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
+            argsForZygote.add("--native-debuggable");
+        }
+        if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
+            argsForZygote.add("--enable-assert");
+        }
+        if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
+            argsForZygote.add("--mount-external-default");
+        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
+            argsForZygote.add("--mount-external-read");
+        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
+            argsForZygote.add("--mount-external-write");
+        }
+        argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
 
-            //TODO optionally enable debuger
-            //argsForZygote.add("--enable-debugger");
+        //TODO optionally enable debuger
+        //argsForZygote.add("--enable-debugger");
 
-            // --setgroups is a comma-separated list
-            if (gids != null && gids.length > 0) {
-                StringBuilder sb = new StringBuilder();
-                sb.append("--setgroups=");
+        // --setgroups is a comma-separated list
+        if (gids != null && gids.length > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("--setgroups=");
 
-                int sz = gids.length;
-                for (int i = 0; i < sz; i++) {
-                    if (i != 0) {
-                        sb.append(',');
-                    }
-                    sb.append(gids[i]);
+            int sz = gids.length;
+            for (int i = 0; i < sz; i++) {
+                if (i != 0) {
+                    sb.append(',');
                 }
-
-                argsForZygote.add(sb.toString());
+                sb.append(gids[i]);
             }
 
-            if (niceName != null) {
-                argsForZygote.add("--nice-name=" + niceName);
+            argsForZygote.add(sb.toString());
+        }
+
+        if (niceName != null) {
+            argsForZygote.add("--nice-name=" + niceName);
+        }
+
+        if (seInfo != null) {
+            argsForZygote.add("--seinfo=" + seInfo);
+        }
+
+        if (instructionSet != null) {
+            argsForZygote.add("--instruction-set=" + instructionSet);
+        }
+
+        if (appDataDir != null) {
+            argsForZygote.add("--app-data-dir=" + appDataDir);
+        }
+
+        argsForZygote.add(processClass);
+
+        if (extraArgs != null) {
+            for (String arg : extraArgs) {
+                argsForZygote.add(arg);
             }
+        }
 
-            if (seInfo != null) {
-                argsForZygote.add("--seinfo=" + seInfo);
-            }
-
-            if (instructionSet != null) {
-                argsForZygote.add("--instruction-set=" + instructionSet);
-            }
-
-            if (appDataDir != null) {
-                argsForZygote.add("--app-data-dir=" + appDataDir);
-            }
-
-            argsForZygote.add(processClass);
-
-            if (extraArgs != null) {
-                for (String arg : extraArgs) {
-                    argsForZygote.add(arg);
-                }
-            }
-
+        synchronized(mLock) {
             return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
         }
     }
@@ -406,7 +418,9 @@
      */
     public void establishZygoteConnectionForAbi(String abi) {
         try {
-            openZygoteSocketIfNeeded(abi);
+            synchronized(mLock) {
+                openZygoteSocketIfNeeded(abi);
+            }
         } catch (ZygoteStartFailedEx ex) {
             throw new RuntimeException("Unable to connect to zygote for abi: " + abi, ex);
         }
@@ -414,9 +428,12 @@
 
     /**
      * Tries to open socket to Zygote process if not already open. If
-     * already open, does nothing.  May block and retry.
+     * already open, does nothing.  May block and retry.  Requires that mLock be held.
      */
+    @GuardedBy("mLock")
     private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
+        Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
+
         if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
             try {
                 primaryZygoteState = ZygoteState.connect(mSocket);
@@ -444,4 +461,28 @@
 
         throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
     }
+
+    /**
+     * 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 {
+        synchronized(mLock) {
+            ZygoteState state = openZygoteSocketIfNeeded(abi);
+            state.writer.write("3");
+            state.writer.newLine();
+
+            state.writer.write("--preload-package");
+            state.writer.newLine();
+
+            state.writer.write(packagePath);
+            state.writer.newLine();
+
+            state.writer.write(libsPath);
+            state.writer.newLine();
+
+            state.writer.flush();
+        }
+    }
 }
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 15eb8de..f41a838 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -472,6 +472,9 @@
             // Log and discard errors at this stage as we must not crash the system server.
             Log.e(LOGTAG, "error preparing webview native library", t);
         }
+
+        WebViewZygote.onWebViewProviderChanged(packageInfo);
+
         return prepareWebViewInSystemServer(nativeLibs);
     }
 
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
new file mode 100644
index 0000000..bc6e7b4
--- /dev/null
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 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;
+
+import android.content.pm.PackageInfo;
+import android.os.Build;
+import android.os.SystemService;
+import android.os.ZygoteProcess;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.concurrent.TimeoutException;
+
+/** @hide */
+public class WebViewZygote {
+    private static final String LOGTAG = "WebViewZygote";
+
+    private static final String WEBVIEW_ZYGOTE_SERVICE_32 = "webview_zygote32";
+    private static final String WEBVIEW_ZYGOTE_SERVICE_64 = "webview_zygote64";
+
+    private static ZygoteProcess sZygote;
+
+    private static PackageInfo sPackage;
+
+    private static boolean sMultiprocessEnabled = false;
+
+    public static ZygoteProcess getProcess() {
+        connectToZygoteIfNeeded();
+        return sZygote;
+    }
+
+    public static String getPackageName() {
+        return sPackage.packageName;
+    }
+
+    public static void setMultiprocessEnabled(boolean enabled) {
+        sMultiprocessEnabled = enabled;
+
+        // When toggling between multi-process being on/off, start or stop the
+        // service. If it is enabled and the zygote is not yet started, bring up the service.
+        // Otherwise, bring down the service. The name may be null if the package
+        // information has not yet been resolved.
+        final String serviceName = getServiceName();
+        if (serviceName == null) return;
+
+        if (enabled && sZygote == null) {
+            SystemService.start(serviceName);
+        } else {
+            SystemService.stop(serviceName);
+            sZygote = null;
+        }
+    }
+
+    public static void onWebViewProviderChanged(PackageInfo packageInfo) {
+        sPackage = packageInfo;
+
+        // If multi-process is not enabled, then do not start the zygote service.
+        if (!sMultiprocessEnabled) {
+            return;
+        }
+
+        final String serviceName = getServiceName();
+
+        if (SystemService.isStopped(serviceName)) {
+            SystemService.start(serviceName);
+        } else if (sZygote != null) {
+            SystemService.restart(serviceName);
+        }
+
+        try {
+            SystemService.waitForState(serviceName, SystemService.State.RUNNING, 5000);
+        } catch (TimeoutException e) {
+            Log.e(LOGTAG, "Timed out waiting for " + serviceName);
+            return;
+        }
+
+        connectToZygoteIfNeeded();
+    }
+
+    private static String getServiceName() {
+        if (sPackage == null)
+            return null;
+
+        if (Arrays.asList(Build.SUPPORTED_64_BIT_ABIS).contains(
+                    sPackage.applicationInfo.primaryCpuAbi)) {
+            return WEBVIEW_ZYGOTE_SERVICE_64;
+        }
+
+        return WEBVIEW_ZYGOTE_SERVICE_32;
+    }
+
+    private static void connectToZygoteIfNeeded() {
+        if (sZygote != null)
+            return;
+
+        if (sPackage == null) {
+            Log.e(LOGTAG, "Cannot connect to zygote, no package specified");
+            return;
+        }
+
+        final String serviceName = getServiceName();
+        if (!SystemService.isRunning(serviceName)) {
+            Log.e(LOGTAG, serviceName + " is not running");
+            return;
+        }
+
+        try {
+            sZygote = new ZygoteProcess("webview_zygote", null);
+
+            String packagePath = sPackage.applicationInfo.sourceDir;
+            String libsPath = sPackage.applicationInfo.nativeLibraryDir;
+
+            Log.d(LOGTAG, "Preloading package " + packagePath + " " + libsPath);
+            sZygote.preloadPackageForAbi(packagePath, libsPath, Build.SUPPORTED_ABIS[0]);
+        } catch (Exception e) {
+            Log.e(LOGTAG, "Error connecting to " + serviceName, e);
+            sZygote = null;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index 2ed7aa2..11dd0e8 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -16,6 +16,15 @@
 
 package com.android.internal.os;
 
+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 java.io.IOException;
+
 /**
  * Startup class for the WebView zygote process.
  *
@@ -26,7 +35,48 @@
 class WebViewZygoteInit {
     public static final String TAG = "WebViewZygoteInit";
 
+    private static ZygoteServer sServer;
+
+    private static class WebViewZygoteServer extends ZygoteServer {
+        @Override
+        protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
+                throws IOException {
+            return new WebViewZygoteConnection(socket, abiList);
+        }
+    }
+
+    private static class WebViewZygoteConnection extends ZygoteConnection {
+        WebViewZygoteConnection(LocalSocket socket, String abiList) throws IOException {
+            super(socket, abiList);
+        }
+
+        @Override
+        protected boolean handlePreloadPackage(String packagePath, String libsPath) {
+            // TODO: Use preload information to setup the ClassLoader.
+            return false;
+        }
+    }
+
     public static void main(String argv[]) {
-        throw new RuntimeException("Not implemented yet");
+        sServer = new WebViewZygoteServer();
+
+        // Zygote goes into its own process group.
+        try {
+            Os.setpgid(0, 0);
+        } catch (ErrnoException ex) {
+            throw new RuntimeException("Failed to setpgid(0,0)", ex);
+        }
+
+        try {
+            sServer.registerServerSocket("webview_zygote");
+            sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
+            sServer.closeServerSocket();
+        } catch (Zygote.MethodAndArgsCaller caller) {
+            caller.run();
+        } catch (RuntimeException e) {
+            Log.e(TAG, "Fatal exception:", e);
+        }
+
+        System.exit(0);
     }
 }
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 132b022..66b294d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -43,6 +43,7 @@
 import java.io.PrintStream;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
 import libcore.io.IoUtils;
 
 /**
@@ -169,6 +170,11 @@
                 return handleAbiListQuery();
             }
 
+            if (parsedArgs.preloadPackage != null) {
+                return handlePreloadPackage(parsedArgs.preloadPackage,
+                        parsedArgs.preloadPackageLibs);
+            }
+
             if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
                 throw new ZygoteSecurityException("Client may not specify capabilities: " +
                         "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
@@ -270,6 +276,10 @@
         }
     }
 
+    protected boolean handlePreloadPackage(String packagePath, String libsPath) {
+        throw new RuntimeException("Zyogte does not support package preloading");
+    }
+
     /**
      * Closes socket associated with this connection.
      */
@@ -375,6 +385,12 @@
         String appDataDir;
 
         /**
+         * Whether to preload a package, with the package path in the remainingArgs.
+         */
+        String preloadPackage;
+        String preloadPackageLibs;
+
+        /**
          * Constructs instance and parses args
          * @param args zygote command-line args
          * @throws IllegalArgumentException
@@ -532,6 +548,9 @@
                     instructionSet = arg.substring(arg.indexOf('=') + 1);
                 } else if (arg.startsWith("--app-data-dir=")) {
                     appDataDir = arg.substring(arg.indexOf('=') + 1);
+                } else if (arg.equals("--preload-package")) {
+                    preloadPackage = args[++curArg];
+                    preloadPackageLibs = args[++curArg];
                 } else {
                     break;
                 }
@@ -541,6 +560,11 @@
                 if (args.length - curArg > 0) {
                     throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
                 }
+            } else if (preloadPackage != null) {
+                if (args.length - curArg > 0) {
+                    throw new IllegalArgumentException(
+                            "Unexpected arguments after --preload-package.");
+                }
             } else {
                 if (!seenRuntimeArgs) {
                     throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index ab876410..126d9e7 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -19,6 +19,7 @@
 import static android.system.OsConstants.POLLIN;
 
 import android.net.LocalServerSocket;
+import android.net.LocalSocket;
 import android.system.Os;
 import android.system.ErrnoException;
 import android.system.StructPollfd;
@@ -80,13 +81,18 @@
      */
     private ZygoteConnection acceptCommandPeer(String abiList) {
         try {
-            return new ZygoteConnection(mServerSocket.accept(), abiList);
+            return createNewConnection(mServerSocket.accept(), abiList);
         } catch (IOException ex) {
             throw new RuntimeException(
                     "IOException during accept()", ex);
         }
     }
 
+    protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
+            throws IOException {
+        return new ZygoteConnection(socket, abiList);
+    }
+
     /**
      * Close and clean up zygote sockets. Called on shutdown and on the
      * child's exit path.
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index bb76449..9f0f11a 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -36,6 +36,7 @@
 import android.util.Log;
 import android.webkit.WebViewFactory;
 import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewZygote;
 
 import com.android.internal.util.XmlUtils;
 
@@ -268,6 +269,11 @@
         return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS);
     }
 
+    @Override
+    public void setMultiprocessEnabled(boolean enabled) {
+        WebViewZygote.setMultiprocessEnabled(enabled);
+    }
+
     // flags declaring we want extra info from the package manager for webview providers
     private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
             | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 7bde37a..7c934fc 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -48,4 +48,6 @@
     public boolean systemIsDebuggable();
     public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
             throws NameNotFoundException;
+
+    public void setMultiprocessEnabled(boolean enabled);
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 2cf1722..863408c 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -20,7 +20,12 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.Signature;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.Base64;
 import android.util.Slog;
 import android.webkit.WebViewFactory;
@@ -42,6 +47,7 @@
 
     private SystemInterface mSystemInterface;
     private WebViewUpdater mWebViewUpdater;
+    private SettingsObserver mSettingsObserver;
     private Context mContext;
 
     public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
@@ -61,6 +67,10 @@
     void prepareWebViewInSystemServer() {
         updateFallbackStateOnBoot();
         mWebViewUpdater.prepareWebViewInSystemServer();
+
+        // Register for changes in the multiprocess developer option. This has to be done
+        // here, since the update service gets created before the ContentResolver service.
+        mSettingsObserver = new SettingsObserver();
     }
 
     private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
@@ -667,4 +677,41 @@
                         & ApplicationInfo.PRIVATE_FLAG_HIDDEN) == 0));
     }
 
+    /**
+     * Watches for changes in the WEBVIEW_MULTIPROCESS setting and lets
+     * the WebViewZygote know, so it can start or stop the zygote process
+     * appropriately.
+     */
+    private class SettingsObserver extends ContentObserver {
+        private final ContentResolver mResolver;
+
+        SettingsObserver() {
+            super(new Handler());
+
+            mResolver = mContext.getContentResolver();
+            mResolver.registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.WEBVIEW_MULTIPROCESS),
+                    false, this);
+
+            // Push the current value of the setting immediately.
+            notifyZygote();
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            notifyZygote();
+        }
+
+        private void notifyZygote() {
+            boolean enableMultiprocess = false;
+
+            try {
+                enableMultiprocess = Settings.Global.getInt(mResolver,
+                        Settings.Global.WEBVIEW_MULTIPROCESS) == 1;
+            } catch (Settings.SettingNotFoundException ex) {
+            }
+
+            mSystemInterface.setMultiprocessEnabled(enableMultiprocess);
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index e33be40..3a2a2de 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -113,4 +113,7 @@
     public int getFactoryPackageVersion(String packageName) {
         return 0;
     }
+
+    @Override
+    public void setMultiprocessEnabled(boolean enabled) {}
 }