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