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