diff options
| -rw-r--r-- | cmds/webview_zygote/Android.mk | 49 | ||||
| -rw-r--r-- | cmds/webview_zygote/webview_zygote.cpp | 76 | ||||
| -rw-r--r-- | cmds/webview_zygote/webview_zygote32.rc | 22 | ||||
| -rw-r--r-- | cmds/webview_zygote/webview_zygote64.rc | 22 | ||||
| -rw-r--r-- | core/java/android/os/Process.java | 23 | ||||
| -rw-r--r-- | core/java/android/os/ZygoteProcess.java | 195 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewFactory.java | 3 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewZygote.java | 135 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/WebViewZygoteInit.java | 52 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/ZygoteConnection.java | 24 | ||||
| -rw-r--r-- | core/java/com/android/internal/os/ZygoteServer.java | 8 | ||||
| -rw-r--r-- | services/core/java/com/android/server/webkit/SystemImpl.java | 6 | ||||
| -rw-r--r-- | services/core/java/com/android/server/webkit/SystemInterface.java | 2 | ||||
| -rw-r--r-- | services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java | 47 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java | 3 |
15 files changed, 588 insertions, 79 deletions
diff --git a/cmds/webview_zygote/Android.mk b/cmds/webview_zygote/Android.mk new file mode 100644 index 000000000000..66e762c0558b --- /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 000000000000..88fee645b3ee --- /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 000000000000..b7decc8eba3b --- /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 000000000000..2935b28cff55 --- /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 f9dee92fdf43..4eee8541f3bb 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -20,6 +20,7 @@ import android.annotation.TestApi; import android.system.Os; import android.system.OsConstants; import android.util.Log; +import android.webkit.WebViewZygote; import dalvik.system.VMRuntime; /** @@ -133,6 +134,12 @@ public class Process { 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. @@ -425,6 +432,22 @@ public class Process { 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 f050d7675562..c45fe5a61852 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -19,7 +19,9 @@ package android.os; 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 @@ public class ZygoteProcess { } 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 @@ public class ZygoteProcess { } /** + * 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 @@ public class ZygoteProcess { * * @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 @@ public class ZygoteProcess { * * @throws ZygoteStartFailedEx if process start failed for any reason */ + @GuardedBy("mLock") private static Process.ProcessStartResult zygoteSendArgsAndGetResult( ZygoteState zygoteState, ArrayList<String> args) throws ZygoteStartFailedEx { @@ -320,90 +332,90 @@ public class ZygoteProcess { String appDataDir, String[] extraArgs) throws ZygoteStartFailedEx { - synchronized(Process.class) { - 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); - - //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="); - - int sz = gids.length; - for (int i = 0; i < sz; i++) { - if (i != 0) { - sb.append(','); - } - sb.append(gids[i]); - } + 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); - argsForZygote.add(sb.toString()); - } + //TODO optionally enable debuger + //argsForZygote.add("--enable-debugger"); - if (niceName != null) { - argsForZygote.add("--nice-name=" + niceName); - } + // --setgroups is a comma-separated list + if (gids != null && gids.length > 0) { + StringBuilder sb = new StringBuilder(); + sb.append("--setgroups="); - if (seInfo != null) { - argsForZygote.add("--seinfo=" + seInfo); + int sz = gids.length; + for (int i = 0; i < sz; i++) { + if (i != 0) { + sb.append(','); + } + sb.append(gids[i]); } - if (instructionSet != null) { - argsForZygote.add("--instruction-set=" + instructionSet); - } + argsForZygote.add(sb.toString()); + } - if (appDataDir != null) { - argsForZygote.add("--app-data-dir=" + appDataDir); - } + if (niceName != null) { + argsForZygote.add("--nice-name=" + niceName); + } + + if (seInfo != null) { + argsForZygote.add("--seinfo=" + seInfo); + } - argsForZygote.add(processClass); + if (instructionSet != null) { + argsForZygote.add("--instruction-set=" + instructionSet); + } - if (extraArgs != null) { - for (String arg : extraArgs) { - argsForZygote.add(arg); - } + 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); } } @@ -415,7 +427,9 @@ public class ZygoteProcess { */ 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); } @@ -423,9 +437,12 @@ public class ZygoteProcess { /** * 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); @@ -453,4 +470,28 @@ public class ZygoteProcess { 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 15eb8de5614c..f41a8380beab 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -472,6 +472,9 @@ public final class WebViewFactory { // 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 000000000000..bc6e7b4a9dd3 --- /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 2ed7aa214ccd..11dd0e8771a7 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 @@ package com.android.internal.os; 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 132b02236ab9..66b294d4e46b 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.InputStreamReader; 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 @@ class ZygoteConnection { 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 @@ class ZygoteConnection { } } + 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 @@ class ZygoteConnection { 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 @@ class ZygoteConnection { 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 @@ class ZygoteConnection { 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 ab87641006a5..126d9e7db2ff 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -19,6 +19,7 @@ package com.android.internal.os; 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 @@ class ZygoteServer { */ 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 bb76449a8529..9f0f11a1443f 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.AndroidRuntimeException; 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 @@ public class SystemImpl implements SystemInterface { 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 7bde37a5a4fd..7c934fc8cdf4 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 interface SystemInterface { 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 2cf17229a5bf..863408c17f29 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.ApplicationInfo; 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 @@ public class WebViewUpdateServiceImpl { private SystemInterface mSystemInterface; private WebViewUpdater mWebViewUpdater; + private SettingsObserver mSettingsObserver; private Context mContext; public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) { @@ -61,6 +67,10 @@ public class WebViewUpdateServiceImpl { 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 @@ public class WebViewUpdateServiceImpl { & 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 e33be400a849..3a2a2dea66ad 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 class TestSystemImpl implements SystemInterface { public int getFactoryPackageVersion(String packageName) { return 0; } + + @Override + public void setMultiprocessEnabled(boolean enabled) {} } |