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) {}
}