WebViewZygote.java revision f9bd606fbadb9a8595b0074aebf35f35d166aba8
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.ChildZygoteProcess; 24import android.os.Process; 25import android.os.ZygoteProcess; 26import android.text.TextUtils; 27import android.util.Log; 28 29import com.android.internal.annotations.GuardedBy; 30 31import java.io.File; 32import java.util.ArrayList; 33import java.util.List; 34 35/** @hide */ 36public class WebViewZygote { 37 private static final String LOGTAG = "WebViewZygote"; 38 39 /** 40 * Lock object that protects all other static members. 41 */ 42 private static final Object sLock = new Object(); 43 44 /** 45 * Instance that maintains the socket connection to the zygote. This is {@code null} if the 46 * zygote is not running or is not connected. 47 */ 48 @GuardedBy("sLock") 49 private static ChildZygoteProcess sZygote; 50 51 /** 52 * Information about the selected WebView package. This is set from #onWebViewProviderChanged(). 53 */ 54 @GuardedBy("sLock") 55 private static PackageInfo sPackage; 56 57 /** 58 * Original ApplicationInfo for the selected WebView package before stub fixup. This is set from 59 * #onWebViewProviderChanged(). 60 */ 61 @GuardedBy("sLock") 62 private static ApplicationInfo sPackageOriginalAppInfo; 63 64 /** 65 * Flag for whether multi-process WebView is enabled. If this is {@code false}, the zygote 66 * will not be started. 67 */ 68 @GuardedBy("sLock") 69 private static boolean sMultiprocessEnabled = false; 70 71 public static ZygoteProcess getProcess() { 72 synchronized (sLock) { 73 if (sZygote != null) return sZygote; 74 75 connectToZygoteIfNeededLocked(); 76 return sZygote; 77 } 78 } 79 80 public static String getPackageName() { 81 synchronized (sLock) { 82 return sPackage.packageName; 83 } 84 } 85 86 public static boolean isMultiprocessEnabled() { 87 synchronized (sLock) { 88 return sMultiprocessEnabled && sPackage != null; 89 } 90 } 91 92 public static void setMultiprocessEnabled(boolean enabled) { 93 synchronized (sLock) { 94 sMultiprocessEnabled = enabled; 95 96 // When toggling between multi-process being on/off, start or stop the 97 // zygote. If it is enabled and the zygote is not yet started, launch it. 98 // Otherwise, kill it. The name may be null if the package information has 99 // not yet been resolved. 100 if (enabled) { 101 connectToZygoteIfNeededLocked(); 102 } else { 103 stopZygoteLocked(); 104 } 105 } 106 } 107 108 public static void onWebViewProviderChanged(PackageInfo packageInfo, 109 ApplicationInfo originalAppInfo) { 110 synchronized (sLock) { 111 sPackage = packageInfo; 112 sPackageOriginalAppInfo = originalAppInfo; 113 114 // If multi-process is not enabled, then do not start the zygote service. 115 if (!sMultiprocessEnabled) { 116 return; 117 } 118 119 stopZygoteLocked(); 120 } 121 } 122 123 @GuardedBy("sLock") 124 private static void stopZygoteLocked() { 125 if (sZygote != null) { 126 // Close the connection and kill the zygote process. This will not cause 127 // child processes to be killed by itself. But if this is called in response to 128 // setMultiprocessEnabled() or onWebViewProviderChanged(), the WebViewUpdater 129 // will kill all processes that depend on the WebView package. 130 sZygote.close(); 131 Process.killProcess(sZygote.getPid()); 132 sZygote = null; 133 } 134 } 135 136 @GuardedBy("sLock") 137 private static void connectToZygoteIfNeededLocked() { 138 if (sZygote != null) { 139 return; 140 } 141 142 if (sPackage == null) { 143 Log.e(LOGTAG, "Cannot connect to zygote, no package specified"); 144 return; 145 } 146 147 try { 148 sZygote = Process.zygoteProcess.startChildZygote( 149 "com.android.internal.os.WebViewZygoteInit", 150 "webview_zygote", 151 Process.WEBVIEW_ZYGOTE_UID, 152 Process.WEBVIEW_ZYGOTE_UID, 153 null, // gids 154 0, // runtimeFlags 155 "webview_zygote", // seInfo 156 sPackage.applicationInfo.primaryCpuAbi, // abi 157 null); // instructionSet 158 159 // All the work below is usually done by LoadedApk, but the zygote can't talk to 160 // PackageManager or construct a LoadedApk since it's single-threaded pre-fork, so 161 // doesn't have an ActivityThread and can't use Binder. 162 // Instead, figure out the paths here, in the system server where we have access to 163 // the package manager. Reuse the logic from LoadedApk to determine the correct 164 // paths and pass them to the zygote as strings. 165 final List<String> zipPaths = new ArrayList<>(10); 166 final List<String> libPaths = new ArrayList<>(10); 167 LoadedApk.makePaths(null, false, sPackage.applicationInfo, zipPaths, libPaths); 168 final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths); 169 final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) : 170 TextUtils.join(File.pathSeparator, zipPaths); 171 172 // In the case where the ApplicationInfo has been modified by the stub WebView, 173 // we need to use the original ApplicationInfo to determine what the original classpath 174 // would have been to use as a cache key. 175 LoadedApk.makePaths(null, false, sPackageOriginalAppInfo, zipPaths, null); 176 final String cacheKey = (zipPaths.size() == 1) ? zipPaths.get(0) : 177 TextUtils.join(File.pathSeparator, zipPaths); 178 179 ZygoteProcess.waitForConnectionToZygote(sZygote.getPrimarySocketAddress()); 180 181 Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath); 182 sZygote.preloadPackageForAbi(zip, librarySearchPath, cacheKey, 183 Build.SUPPORTED_ABIS[0]); 184 } catch (Exception e) { 185 Log.e(LOGTAG, "Error connecting to webview zygote", e); 186 stopZygoteLocked(); 187 } 188 } 189} 190