|  | /* | 
|  | * 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.ChildZygoteProcess; | 
|  | import android.os.Process; | 
|  | import android.os.ZygoteProcess; | 
|  | import android.text.TextUtils; | 
|  | import android.util.Log; | 
|  |  | 
|  | import com.android.internal.annotations.GuardedBy; | 
|  |  | 
|  | /** @hide */ | 
|  | public class WebViewZygote { | 
|  | private static final String LOGTAG = "WebViewZygote"; | 
|  |  | 
|  | /** | 
|  | * Lock object that protects all other static members. | 
|  | */ | 
|  | private static final Object sLock = new Object(); | 
|  |  | 
|  | /** | 
|  | * Instance that maintains the socket connection to the zygote. This is {@code null} if the | 
|  | * zygote is not running or is not connected. | 
|  | */ | 
|  | @GuardedBy("sLock") | 
|  | private static ChildZygoteProcess sZygote; | 
|  |  | 
|  | /** | 
|  | * Information about the selected WebView package. This is set from #onWebViewProviderChanged(). | 
|  | */ | 
|  | @GuardedBy("sLock") | 
|  | private static PackageInfo sPackage; | 
|  |  | 
|  | /** | 
|  | * Flag for whether multi-process WebView is enabled. If this is {@code false}, the zygote | 
|  | * will not be started. | 
|  | */ | 
|  | @GuardedBy("sLock") | 
|  | private static boolean sMultiprocessEnabled = false; | 
|  |  | 
|  | public static ZygoteProcess getProcess() { | 
|  | synchronized (sLock) { | 
|  | if (sZygote != null) return sZygote; | 
|  |  | 
|  | connectToZygoteIfNeededLocked(); | 
|  | return sZygote; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static String getPackageName() { | 
|  | synchronized (sLock) { | 
|  | return sPackage.packageName; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static boolean isMultiprocessEnabled() { | 
|  | synchronized (sLock) { | 
|  | return sMultiprocessEnabled && sPackage != null; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static void setMultiprocessEnabled(boolean enabled) { | 
|  | synchronized (sLock) { | 
|  | sMultiprocessEnabled = enabled; | 
|  |  | 
|  | // When multi-process is disabled, kill the zygote. When it is enabled, | 
|  | // the zygote will be started when it is first needed in getProcess(). | 
|  | if (!enabled) { | 
|  | stopZygoteLocked(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void onWebViewProviderChanged(PackageInfo packageInfo) { | 
|  | synchronized (sLock) { | 
|  | sPackage = packageInfo; | 
|  |  | 
|  | // If multi-process is not enabled, then do not start the zygote service. | 
|  | if (!sMultiprocessEnabled) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | stopZygoteLocked(); | 
|  | } | 
|  | } | 
|  |  | 
|  | @GuardedBy("sLock") | 
|  | private static void stopZygoteLocked() { | 
|  | if (sZygote != null) { | 
|  | // Close the connection and kill the zygote process. This will not cause | 
|  | // child processes to be killed by itself. But if this is called in response to | 
|  | // setMultiprocessEnabled() or onWebViewProviderChanged(), the WebViewUpdater | 
|  | // will kill all processes that depend on the WebView package. | 
|  | sZygote.close(); | 
|  | Process.killProcess(sZygote.getPid()); | 
|  | sZygote = null; | 
|  | } | 
|  | } | 
|  |  | 
|  | @GuardedBy("sLock") | 
|  | private static void connectToZygoteIfNeededLocked() { | 
|  | if (sZygote != null) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (sPackage == null) { | 
|  | Log.e(LOGTAG, "Cannot connect to zygote, no package specified"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | try { | 
|  | String abi = sPackage.applicationInfo.primaryCpuAbi; | 
|  | sZygote = Process.ZYGOTE_PROCESS.startChildZygote( | 
|  | "com.android.internal.os.WebViewZygoteInit", | 
|  | "webview_zygote", | 
|  | Process.WEBVIEW_ZYGOTE_UID, | 
|  | Process.WEBVIEW_ZYGOTE_UID, | 
|  | null,  // gids | 
|  | 0,  // runtimeFlags | 
|  | "webview_zygote",  // seInfo | 
|  | abi,  // abi | 
|  | TextUtils.join(",", Build.SUPPORTED_ABIS), | 
|  | null, // instructionSet | 
|  | Process.FIRST_ISOLATED_UID, | 
|  | Integer.MAX_VALUE); // TODO(b/123615476) deal with user-id ranges properly | 
|  | ZygoteProcess.waitForConnectionToZygote(sZygote.getPrimarySocketAddress()); | 
|  | sZygote.preloadApp(sPackage.applicationInfo, abi); | 
|  | } catch (Exception e) { | 
|  | Log.e(LOGTAG, "Error connecting to webview zygote", e); | 
|  | stopZygoteLocked(); | 
|  | } | 
|  | } | 
|  | } |