WebViewZygote.java revision 4fd8aa51e4de5a7c93841571e30ff10077d1b125
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 17package android.webkit; 18 19import android.app.LoadedApk; 20import android.content.pm.ApplicationInfo; 21import android.content.pm.PackageInfo; 22import android.os.Build; 23import android.os.SystemService; 24import android.os.ZygoteProcess; 25import android.text.TextUtils; 26import android.util.AndroidRuntimeException; 27import android.util.Log; 28 29import com.android.internal.annotations.GuardedBy; 30 31import java.io.File; 32import java.util.ArrayList; 33import java.util.Arrays; 34import java.util.List; 35import java.util.concurrent.TimeoutException; 36 37/** @hide */ 38public class WebViewZygote { 39 private static final String LOGTAG = "WebViewZygote"; 40 41 private static final String WEBVIEW_ZYGOTE_SERVICE_32 = "webview_zygote32"; 42 private static final String WEBVIEW_ZYGOTE_SERVICE_64 = "webview_zygote64"; 43 private static final String WEBVIEW_ZYGOTE_SOCKET = "webview_zygote"; 44 45 /** 46 * Lock object that protects all other static members. 47 */ 48 private static final Object sLock = new Object(); 49 50 /** 51 * Instance that maintains the socket connection to the zygote. This is {@code null} if the 52 * zygote is not running or is not connected. 53 */ 54 @GuardedBy("sLock") 55 private static ZygoteProcess sZygote; 56 57 /** 58 * Variable that allows us to determine whether the WebView zygote Service has already been 59 * started. 60 */ 61 @GuardedBy("sLock") 62 private static boolean sStartedService = false; 63 64 /** 65 * Information about the selected WebView package. This is set from #onWebViewProviderChanged(). 66 */ 67 @GuardedBy("sLock") 68 private static PackageInfo sPackage; 69 70 /** 71 * Original ApplicationInfo for the selected WebView package before stub fixup. This is set from 72 * #onWebViewProviderChanged(). 73 */ 74 @GuardedBy("sLock") 75 private static ApplicationInfo sPackageOriginalAppInfo; 76 77 /** 78 * Flag for whether multi-process WebView is enabled. If this is {@code false}, the zygote 79 * will not be started. 80 */ 81 @GuardedBy("sLock") 82 private static boolean sMultiprocessEnabled = false; 83 84 public static ZygoteProcess getProcess() { 85 synchronized (sLock) { 86 if (sZygote != null) return sZygote; 87 88 waitForServiceStartAndConnect(); 89 return sZygote; 90 } 91 } 92 93 public static String getPackageName() { 94 synchronized (sLock) { 95 return sPackage.packageName; 96 } 97 } 98 99 public static boolean isMultiprocessEnabled() { 100 synchronized (sLock) { 101 return sMultiprocessEnabled && sPackage != null; 102 } 103 } 104 105 public static void setMultiprocessEnabled(boolean enabled) { 106 synchronized (sLock) { 107 sMultiprocessEnabled = enabled; 108 109 // When toggling between multi-process being on/off, start or stop the 110 // service. If it is enabled and the zygote is not yet started, bring up the service. 111 // Otherwise, bring down the service. The name may be null if the package 112 // information has not yet been resolved. 113 final String serviceName = getServiceNameLocked(); 114 if (serviceName == null) return; 115 116 if (enabled) { 117 if (!sStartedService) { 118 SystemService.start(serviceName); 119 sStartedService = true; 120 } 121 } else { 122 SystemService.stop(serviceName); 123 sStartedService = false; 124 sZygote = null; 125 } 126 } 127 } 128 129 public static void onWebViewProviderChanged(PackageInfo packageInfo, 130 ApplicationInfo originalAppInfo) { 131 synchronized (sLock) { 132 sPackage = packageInfo; 133 sPackageOriginalAppInfo = originalAppInfo; 134 135 // If multi-process is not enabled, then do not start the zygote service. 136 if (!sMultiprocessEnabled) { 137 return; 138 } 139 140 final String serviceName = getServiceNameLocked(); 141 sZygote = null; 142 143 // The service may enter the RUNNING state before it opens the socket, 144 // so connectToZygoteIfNeededLocked() may still fail. 145 if (SystemService.isStopped(serviceName)) { 146 SystemService.start(serviceName); 147 } else { 148 SystemService.restart(serviceName); 149 } 150 sStartedService = true; 151 } 152 } 153 154 private static void waitForServiceStartAndConnect() { 155 if (!sStartedService) { 156 throw new AndroidRuntimeException("Tried waiting for the WebView Zygote Service to " + 157 "start running without first starting the service."); 158 } 159 160 String serviceName; 161 synchronized (sLock) { 162 serviceName = getServiceNameLocked(); 163 } 164 try { 165 SystemService.waitForState(serviceName, SystemService.State.RUNNING, 5000); 166 } catch (TimeoutException e) { 167 Log.e(LOGTAG, "Timed out waiting for " + serviceName); 168 return; 169 } 170 171 synchronized (sLock) { 172 connectToZygoteIfNeededLocked(); 173 } 174 } 175 176 @GuardedBy("sLock") 177 private static String getServiceNameLocked() { 178 if (sPackage == null) 179 return null; 180 181 if (Arrays.asList(Build.SUPPORTED_64_BIT_ABIS).contains( 182 sPackage.applicationInfo.primaryCpuAbi)) { 183 return WEBVIEW_ZYGOTE_SERVICE_64; 184 } 185 186 return WEBVIEW_ZYGOTE_SERVICE_32; 187 } 188 189 @GuardedBy("sLock") 190 private static void connectToZygoteIfNeededLocked() { 191 if (sZygote != null) { 192 return; 193 } 194 195 if (sPackage == null) { 196 Log.e(LOGTAG, "Cannot connect to zygote, no package specified"); 197 return; 198 } 199 200 final String serviceName = getServiceNameLocked(); 201 if (!SystemService.isRunning(serviceName)) { 202 Log.e(LOGTAG, serviceName + " is not running"); 203 return; 204 } 205 206 try { 207 sZygote = new ZygoteProcess(WEBVIEW_ZYGOTE_SOCKET, null); 208 209 // All the work below is usually done by LoadedApk, but the zygote can't talk to 210 // PackageManager or construct a LoadedApk since it's single-threaded pre-fork, so 211 // doesn't have an ActivityThread and can't use Binder. 212 // Instead, figure out the paths here, in the system server where we have access to 213 // the package manager. Reuse the logic from LoadedApk to determine the correct 214 // paths and pass them to the zygote as strings. 215 final List<String> zipPaths = new ArrayList<>(10); 216 final List<String> libPaths = new ArrayList<>(10); 217 LoadedApk.makePaths(null, false, sPackage.applicationInfo, zipPaths, libPaths); 218 final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths); 219 final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) : 220 TextUtils.join(File.pathSeparator, zipPaths); 221 222 // In the case where the ApplicationInfo has been modified by the stub WebView, 223 // we need to use the original ApplicationInfo to determine what the original classpath 224 // would have been to use as a cache key. 225 LoadedApk.makePaths(null, false, sPackageOriginalAppInfo, zipPaths, null); 226 final String cacheKey = (zipPaths.size() == 1) ? zipPaths.get(0) : 227 TextUtils.join(File.pathSeparator, zipPaths); 228 229 ZygoteProcess.waitForConnectionToZygote(WEBVIEW_ZYGOTE_SOCKET); 230 231 Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath); 232 sZygote.preloadPackageForAbi(zip, librarySearchPath, cacheKey, 233 Build.SUPPORTED_ABIS[0]); 234 } catch (Exception e) { 235 Log.e(LOGTAG, "Error connecting to " + serviceName, e); 236 sZygote = null; 237 } 238 } 239} 240