1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.webkit; 18 19 import android.content.pm.PackageInfo; 20 import android.os.Build; 21 import android.os.ChildZygoteProcess; 22 import android.os.Process; 23 import android.os.ZygoteProcess; 24 import android.text.TextUtils; 25 import android.util.Log; 26 27 import com.android.internal.annotations.GuardedBy; 28 import com.android.internal.os.Zygote; 29 30 /** @hide */ 31 public class WebViewZygote { 32 private static final String LOGTAG = "WebViewZygote"; 33 34 /** 35 * Lock object that protects all other static members. 36 */ 37 private static final Object sLock = new Object(); 38 39 /** 40 * Instance that maintains the socket connection to the zygote. This is {@code null} if the 41 * zygote is not running or is not connected. 42 */ 43 @GuardedBy("sLock") 44 private static ChildZygoteProcess sZygote; 45 46 /** 47 * Information about the selected WebView package. This is set from #onWebViewProviderChanged(). 48 */ 49 @GuardedBy("sLock") 50 private static PackageInfo sPackage; 51 52 /** 53 * Flag for whether multi-process WebView is enabled. If this is {@code false}, the zygote 54 * will not be started. 55 */ 56 @GuardedBy("sLock") 57 private static boolean sMultiprocessEnabled = false; 58 getProcess()59 public static ZygoteProcess getProcess() { 60 synchronized (sLock) { 61 if (sZygote != null) return sZygote; 62 63 connectToZygoteIfNeededLocked(); 64 return sZygote; 65 } 66 } 67 getPackageName()68 public static String getPackageName() { 69 synchronized (sLock) { 70 return sPackage.packageName; 71 } 72 } 73 isMultiprocessEnabled()74 public static boolean isMultiprocessEnabled() { 75 synchronized (sLock) { 76 return sMultiprocessEnabled && sPackage != null; 77 } 78 } 79 setMultiprocessEnabled(boolean enabled)80 public static void setMultiprocessEnabled(boolean enabled) { 81 synchronized (sLock) { 82 sMultiprocessEnabled = enabled; 83 84 // When multi-process is disabled, kill the zygote. When it is enabled, 85 // the zygote will be started when it is first needed in getProcess(). 86 if (!enabled) { 87 stopZygoteLocked(); 88 } 89 } 90 } 91 onWebViewProviderChanged(PackageInfo packageInfo)92 static void onWebViewProviderChanged(PackageInfo packageInfo) { 93 synchronized (sLock) { 94 sPackage = packageInfo; 95 96 // If multi-process is not enabled, then do not start the zygote service. 97 if (!sMultiprocessEnabled) { 98 return; 99 } 100 101 stopZygoteLocked(); 102 } 103 } 104 105 @GuardedBy("sLock") stopZygoteLocked()106 private static void stopZygoteLocked() { 107 if (sZygote != null) { 108 // Close the connection and kill the zygote process. This will not cause 109 // child processes to be killed by itself. But if this is called in response to 110 // setMultiprocessEnabled() or onWebViewProviderChanged(), the WebViewUpdater 111 // will kill all processes that depend on the WebView package. 112 sZygote.close(); 113 Process.killProcess(sZygote.getPid()); 114 sZygote = null; 115 } 116 } 117 118 @GuardedBy("sLock") connectToZygoteIfNeededLocked()119 private static void connectToZygoteIfNeededLocked() { 120 if (sZygote != null) { 121 return; 122 } 123 124 if (sPackage == null) { 125 Log.e(LOGTAG, "Cannot connect to zygote, no package specified"); 126 return; 127 } 128 129 try { 130 String abi = sPackage.applicationInfo.primaryCpuAbi; 131 int runtimeFlags = Zygote.getMemorySafetyRuntimeFlagsForSecondaryZygote( 132 sPackage.applicationInfo, null); 133 sZygote = Process.ZYGOTE_PROCESS.startChildZygote( 134 "com.android.internal.os.WebViewZygoteInit", 135 "webview_zygote", 136 Process.WEBVIEW_ZYGOTE_UID, 137 Process.WEBVIEW_ZYGOTE_UID, 138 null, // gids 139 runtimeFlags, 140 "webview_zygote", // seInfo 141 abi, // abi 142 TextUtils.join(",", Build.SUPPORTED_ABIS), 143 null, // instructionSet 144 Process.FIRST_ISOLATED_UID, 145 Integer.MAX_VALUE); // TODO(b/123615476) deal with user-id ranges properly 146 ZygoteProcess.waitForConnectionToZygote(sZygote.getPrimarySocketAddress()); 147 sZygote.preloadApp(sPackage.applicationInfo, abi); 148 } catch (Exception e) { 149 Log.e(LOGTAG, "Error connecting to webview zygote", e); 150 stopZygoteLocked(); 151 } 152 } 153 } 154