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