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 static android.webkit.Flags.updateServiceV2; 20 21 import android.content.pm.PackageInfo; 22 import android.os.Build; 23 import android.os.ChildZygoteProcess; 24 import android.os.Process; 25 import android.os.ZygoteProcess; 26 import android.text.TextUtils; 27 import android.util.Log; 28 29 import com.android.internal.annotations.GuardedBy; 30 import com.android.internal.os.Zygote; 31 32 /** @hide */ 33 public class WebViewZygote { 34 private static final String LOGTAG = "WebViewZygote"; 35 36 /** 37 * Lock object that protects all other static members. 38 */ 39 private static final Object sLock = new Object(); 40 41 /** 42 * Instance that maintains the socket connection to the zygote. This is {@code null} if the 43 * zygote is not running or is not connected. 44 */ 45 @GuardedBy("sLock") 46 private static ChildZygoteProcess sZygote; 47 48 /** 49 * Information about the selected WebView package. This is set from #onWebViewProviderChanged(). 50 */ 51 @GuardedBy("sLock") 52 private static PackageInfo sPackage; 53 54 /** 55 * Flag for whether multi-process WebView is enabled. If this is {@code false}, the zygote will 56 * not be started. Should be removed entirely after we remove the updateServiceV2 flag. 57 */ 58 @GuardedBy("sLock") 59 private static boolean sMultiprocessEnabled = false; 60 getProcess()61 public static ZygoteProcess getProcess() { 62 synchronized (sLock) { 63 if (sZygote != null) return sZygote; 64 65 connectToZygoteIfNeededLocked(); 66 return sZygote; 67 } 68 } 69 getPackageName()70 public static String getPackageName() { 71 synchronized (sLock) { 72 return sPackage.packageName; 73 } 74 } 75 isMultiprocessEnabled()76 public static boolean isMultiprocessEnabled() { 77 synchronized (sLock) { 78 if (updateServiceV2()) { 79 return sPackage != null; 80 } else { 81 return sMultiprocessEnabled && sPackage != null; 82 } 83 } 84 } 85 setMultiprocessEnabled(boolean enabled)86 public static void setMultiprocessEnabled(boolean enabled) { 87 if (updateServiceV2()) { 88 throw new IllegalStateException( 89 "setMultiprocessEnabled shouldn't be called if update_service_v2 flag is set."); 90 } 91 synchronized (sLock) { 92 sMultiprocessEnabled = enabled; 93 94 // When multi-process is disabled, kill the zygote. When it is enabled, 95 // the zygote will be started when it is first needed in getProcess(). 96 if (!enabled) { 97 stopZygoteLocked(); 98 } 99 } 100 } 101 onWebViewProviderChanged(PackageInfo packageInfo)102 static void onWebViewProviderChanged(PackageInfo packageInfo) { 103 synchronized (sLock) { 104 sPackage = packageInfo; 105 106 // If multi-process is not enabled, then do not start the zygote service. 107 // Only check sMultiprocessEnabled if updateServiceV2 is not enabled. 108 if (!updateServiceV2() && !sMultiprocessEnabled) { 109 return; 110 } 111 112 stopZygoteLocked(); 113 } 114 } 115 116 @GuardedBy("sLock") stopZygoteLocked()117 private static void stopZygoteLocked() { 118 if (sZygote != null) { 119 // Close the connection and kill the zygote process. This will not cause 120 // child processes to be killed by itself. But if this is called in response to 121 // setMultiprocessEnabled() or onWebViewProviderChanged(), the WebViewUpdater 122 // will kill all processes that depend on the WebView package. 123 sZygote.close(); 124 Process.killProcess(sZygote.getPid()); 125 sZygote = null; 126 } 127 } 128 129 @GuardedBy("sLock") connectToZygoteIfNeededLocked()130 private static void connectToZygoteIfNeededLocked() { 131 if (sZygote != null) { 132 return; 133 } 134 135 if (sPackage == null) { 136 Log.e(LOGTAG, "Cannot connect to zygote, no package specified"); 137 return; 138 } 139 140 try { 141 String abi = sPackage.applicationInfo.primaryCpuAbi; 142 int runtimeFlags = Zygote.getMemorySafetyRuntimeFlagsForSecondaryZygote( 143 sPackage.applicationInfo, null); 144 sZygote = Process.ZYGOTE_PROCESS.startChildZygote( 145 "com.android.internal.os.WebViewZygoteInit", 146 "webview_zygote", 147 Process.WEBVIEW_ZYGOTE_UID, 148 Process.WEBVIEW_ZYGOTE_UID, 149 null, // gids 150 runtimeFlags, 151 "webview_zygote", // seInfo 152 abi, // abi 153 TextUtils.join(",", Build.SUPPORTED_ABIS), 154 null, // instructionSet 155 Process.FIRST_ISOLATED_UID, 156 Integer.MAX_VALUE); // TODO(b/123615476) deal with user-id ranges properly 157 ZygoteProcess.waitForConnectionToZygote(sZygote.getPrimarySocketAddress()); 158 sZygote.preloadApp(sPackage.applicationInfo, abi); 159 } catch (Exception e) { 160 Log.e(LOGTAG, "Error connecting to webview zygote", e); 161 stopZygoteLocked(); 162 } 163 } 164 } 165