Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [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 | package com.android.server.webkit; |
| 17 | |
| 18 | import android.content.Context; |
| 19 | import android.content.pm.ApplicationInfo; |
| 20 | import android.content.pm.PackageInfo; |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 21 | import android.content.pm.Signature; |
Gustav Sennton | 0df2c55 | 2016-06-14 15:32:19 +0100 | [diff] [blame] | 22 | import android.os.UserHandle; |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 23 | import android.util.Slog; |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 24 | import android.webkit.UserPackage; |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 25 | import android.webkit.WebViewProviderInfo; |
| 26 | import android.webkit.WebViewProviderResponse; |
| 27 | |
Gustav Sennton | 1eb3820 | 2016-10-21 13:40:10 +0100 | [diff] [blame] | 28 | import java.io.PrintWriter; |
Gustav Sennton | b265016 | 2017-04-07 14:41:38 +0100 | [diff] [blame] | 29 | import java.lang.Integer; |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 30 | import java.util.List; |
| 31 | |
| 32 | /** |
| 33 | * Implementation of the WebViewUpdateService. |
| 34 | * This class doesn't depend on the android system like the actual Service does and can be used |
| 35 | * directly by tests (as long as they implement a SystemInterface). |
Gustav Sennton | 47d7ec8 | 2016-06-07 15:02:58 +0100 | [diff] [blame] | 36 | * |
| 37 | * This class implements two main features - handling WebView fallback packages and keeping track |
| 38 | * of, and preparing, the current WebView implementation. The fallback mechanism is meant to be |
| 39 | * uncoupled from the rest of the WebView preparation and does not store any state. The code for |
| 40 | * choosing and preparing a WebView implementation needs to keep track of a couple of different |
| 41 | * things such as what package is used as WebView implementation. |
| 42 | * |
| 43 | * The public methods in this class are accessed from WebViewUpdateService either on the UI thread |
| 44 | * or on one of multiple Binder threads. This means that the code in this class needs to be |
| 45 | * thread-safe. The fallback mechanism shares (almost) no information between threads which makes |
| 46 | * it easier to argue about thread-safety (in theory, if timed badly, the fallback mechanism can |
| 47 | * incorrectly enable/disable a fallback package but that fault will be corrected when we later |
| 48 | * receive an intent for that enabling/disabling). On the other hand, the WebView preparation code |
| 49 | * shares state between threads meaning that code that chooses a new WebView implementation or |
| 50 | * checks which implementation is being used needs to hold a lock. |
| 51 | * |
| 52 | * The WebViewUpdateService can be accessed in a couple of different ways. |
| 53 | * 1. It is started from the SystemServer at boot - at that point we just initiate some state such |
| 54 | * as the WebView preparation class. |
| 55 | * 2. The SystemServer calls WebViewUpdateService.prepareWebViewInSystemServer. This happens at boot |
| 56 | * and the WebViewUpdateService should not have been accessed before this call. In this call we |
| 57 | * enable/disable fallback packages and then choose WebView implementation for the first time. |
| 58 | * 3. The update service listens for Intents related to package installs and removals. These intents |
| 59 | * are received and processed on the UI thread. Each intent can result in enabling/disabling |
| 60 | * fallback packages and changing WebView implementation. |
| 61 | * 4. The update service can be reached through Binder calls which are handled on specific binder |
| 62 | * threads. These calls can be made from any process. Generally they are used for changing WebView |
| 63 | * implementation (from Settings), getting information about the current WebView implementation (for |
| 64 | * loading WebView into an app process), or notifying the service about Relro creation being |
| 65 | * completed. |
| 66 | * |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 67 | * @hide |
| 68 | */ |
| 69 | public class WebViewUpdateServiceImpl { |
| 70 | private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName(); |
| 71 | |
| 72 | private SystemInterface mSystemInterface; |
| 73 | private WebViewUpdater mWebViewUpdater; |
Gustav Sennton | 86f7bbe | 2016-10-24 16:49:32 +0100 | [diff] [blame] | 74 | final private Context mContext; |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 75 | |
Gustav Sennton | b265016 | 2017-04-07 14:41:38 +0100 | [diff] [blame] | 76 | private final static int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE; |
| 77 | private final static int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE; |
| 78 | |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 79 | public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) { |
| 80 | mContext = context; |
| 81 | mSystemInterface = systemInterface; |
| 82 | mWebViewUpdater = new WebViewUpdater(mContext, mSystemInterface); |
| 83 | } |
| 84 | |
Gustav Sennton | 0df2c55 | 2016-06-14 15:32:19 +0100 | [diff] [blame] | 85 | void packageStateChanged(String packageName, int changedState, int userId) { |
| 86 | // We don't early out here in different cases where we could potentially early-out (e.g. if |
| 87 | // we receive PACKAGE_CHANGED for another user than the system user) since that would |
| 88 | // complicate this logic further and open up for more edge cases. |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 89 | updateFallbackStateOnPackageChange(packageName, changedState); |
| 90 | mWebViewUpdater.packageStateChanged(packageName, changedState); |
| 91 | } |
| 92 | |
| 93 | void prepareWebViewInSystemServer() { |
| 94 | updateFallbackStateOnBoot(); |
| 95 | mWebViewUpdater.prepareWebViewInSystemServer(); |
Torne (Richard Coles) | 1a4c4e3 | 2017-01-10 15:57:41 +0000 | [diff] [blame] | 96 | mSystemInterface.notifyZygote(isMultiProcessEnabled()); |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 97 | } |
| 98 | |
| 99 | private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) { |
| 100 | for (WebViewProviderInfo provider : providers) { |
| 101 | if (provider.availableByDefault && !provider.isFallback) { |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 102 | // userPackages can contain null objects. |
| 103 | List<UserPackage> userPackages = |
| 104 | mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider); |
Gustav Sennton | 924dacd | 2017-03-10 14:37:15 +0000 | [diff] [blame] | 105 | if (WebViewUpdater.isInstalledAndEnabledForAllUsers(userPackages) && |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 106 | // Checking validity of the package for the system user (rather than all |
| 107 | // users) since package validity depends not on the user but on the package |
| 108 | // itself. |
| 109 | mWebViewUpdater.isValidProvider(provider, |
| 110 | userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo())) { |
| 111 | return true; |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 112 | } |
| 113 | } |
| 114 | } |
| 115 | return false; |
| 116 | } |
| 117 | |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 118 | void handleNewUser(int userId) { |
Gustav Sennton | a43f144 | 2017-03-23 17:04:23 +0000 | [diff] [blame] | 119 | // The system user is always started at boot, and by that point we have already run one |
| 120 | // round of the package-changing logic (through prepareWebViewInSystemServer()), so early |
| 121 | // out here. |
| 122 | if (userId == UserHandle.USER_SYSTEM) return; |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 123 | handleUserChange(); |
| 124 | } |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 125 | |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 126 | void handleUserRemoved(int userId) { |
| 127 | handleUserChange(); |
| 128 | } |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 129 | |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 130 | /** |
| 131 | * Called when a user was added or removed to ensure fallback logic and WebView preparation are |
| 132 | * triggered. This has to be done since the WebView package we use depends on the enabled-state |
| 133 | * of packages for all users (so adding or removing a user might cause us to change package). |
| 134 | */ |
| 135 | private void handleUserChange() { |
| 136 | if (mSystemInterface.isFallbackLogicEnabled()) { |
| 137 | updateFallbackState(mSystemInterface.getWebViewPackages()); |
| 138 | } |
| 139 | // Potentially trigger package-changing logic. |
| 140 | mWebViewUpdater.updateCurrentWebViewPackage(null); |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 141 | } |
| 142 | |
| 143 | void notifyRelroCreationCompleted() { |
| 144 | mWebViewUpdater.notifyRelroCreationCompleted(); |
| 145 | } |
| 146 | |
| 147 | WebViewProviderResponse waitForAndGetProvider() { |
| 148 | return mWebViewUpdater.waitForAndGetProvider(); |
| 149 | } |
| 150 | |
| 151 | String changeProviderAndSetting(String newProvider) { |
| 152 | return mWebViewUpdater.changeProviderAndSetting(newProvider); |
| 153 | } |
| 154 | |
| 155 | WebViewProviderInfo[] getValidWebViewPackages() { |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 156 | return mWebViewUpdater.getValidWebViewPackages(); |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 157 | } |
| 158 | |
| 159 | WebViewProviderInfo[] getWebViewPackages() { |
| 160 | return mSystemInterface.getWebViewPackages(); |
| 161 | } |
| 162 | |
Gustav Sennton | bf683e0 | 2016-09-15 14:42:50 +0100 | [diff] [blame] | 163 | PackageInfo getCurrentWebViewPackage() { |
| 164 | return mWebViewUpdater.getCurrentWebViewPackage(); |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 165 | } |
| 166 | |
| 167 | void enableFallbackLogic(boolean enable) { |
| 168 | mSystemInterface.enableFallbackLogic(enable); |
| 169 | } |
| 170 | |
| 171 | private void updateFallbackStateOnBoot() { |
Gustav Sennton | 53b7824 | 2016-04-07 15:56:10 +0100 | [diff] [blame] | 172 | if (!mSystemInterface.isFallbackLogicEnabled()) return; |
| 173 | |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 174 | WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 175 | updateFallbackState(webviewProviders); |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 176 | } |
| 177 | |
| 178 | /** |
| 179 | * Handle the enabled-state of our fallback package, i.e. if there exists some non-fallback |
| 180 | * package that is valid (and available by default) then disable the fallback package, |
| 181 | * otherwise, enable the fallback package. |
| 182 | */ |
| 183 | private void updateFallbackStateOnPackageChange(String changedPackage, int changedState) { |
| 184 | if (!mSystemInterface.isFallbackLogicEnabled()) return; |
| 185 | |
| 186 | WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); |
| 187 | |
| 188 | // A package was changed / updated / downgraded, early out if it is not one of the |
| 189 | // webview packages that are available by default. |
| 190 | boolean changedPackageAvailableByDefault = false; |
| 191 | for (WebViewProviderInfo provider : webviewProviders) { |
| 192 | if (provider.packageName.equals(changedPackage)) { |
| 193 | if (provider.availableByDefault) { |
| 194 | changedPackageAvailableByDefault = true; |
| 195 | } |
| 196 | break; |
| 197 | } |
| 198 | } |
| 199 | if (!changedPackageAvailableByDefault) return; |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 200 | updateFallbackState(webviewProviders); |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 201 | } |
| 202 | |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 203 | private void updateFallbackState(WebViewProviderInfo[] webviewProviders) { |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 204 | // If there exists a valid and enabled non-fallback package - disable the fallback |
| 205 | // package, otherwise, enable it. |
| 206 | WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders); |
| 207 | if (fallbackProvider == null) return; |
| 208 | boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders); |
| 209 | |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 210 | List<UserPackage> userPackages = |
| 211 | mSystemInterface.getPackageInfoForProviderAllUsers(mContext, fallbackProvider); |
| 212 | if (existsValidNonFallbackProvider && !isDisabledForAllUsers(userPackages)) { |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 213 | mSystemInterface.uninstallAndDisablePackageForAllUsers(mContext, |
| 214 | fallbackProvider.packageName); |
| 215 | } else if (!existsValidNonFallbackProvider |
Gustav Sennton | 924dacd | 2017-03-10 14:37:15 +0000 | [diff] [blame] | 216 | && !WebViewUpdater.isInstalledAndEnabledForAllUsers(userPackages)) { |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 217 | // Enable the fallback package for all users. |
| 218 | mSystemInterface.enablePackageForAllUsers(mContext, |
| 219 | fallbackProvider.packageName, true); |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | /** |
| 224 | * Returns the only fallback provider in the set of given packages, or null if there is none. |
| 225 | */ |
| 226 | private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) { |
| 227 | for (WebViewProviderInfo provider : webviewPackages) { |
| 228 | if (provider.isFallback) { |
| 229 | return provider; |
| 230 | } |
| 231 | } |
| 232 | return null; |
| 233 | } |
| 234 | |
| 235 | boolean isFallbackPackage(String packageName) { |
| 236 | if (packageName == null || !mSystemInterface.isFallbackLogicEnabled()) return false; |
| 237 | |
| 238 | WebViewProviderInfo[] webviewPackages = mSystemInterface.getWebViewPackages(); |
| 239 | WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages); |
| 240 | return (fallbackProvider != null |
| 241 | && packageName.equals(fallbackProvider.packageName)); |
| 242 | } |
| 243 | |
Torne (Richard Coles) | 1a4c4e3 | 2017-01-10 15:57:41 +0000 | [diff] [blame] | 244 | boolean isMultiProcessEnabled() { |
Torne (Richard Coles) | dc37507 | 2017-01-11 15:48:13 +0000 | [diff] [blame] | 245 | int settingValue = mSystemInterface.getMultiProcessSetting(mContext); |
| 246 | if (mSystemInterface.isMultiProcessDefaultEnabled()) { |
Gustav Sennton | b265016 | 2017-04-07 14:41:38 +0100 | [diff] [blame] | 247 | // Multiprocess should be enabled unless the user has turned it off manually. |
| 248 | return settingValue > MULTIPROCESS_SETTING_OFF_VALUE; |
Torne (Richard Coles) | dc37507 | 2017-01-11 15:48:13 +0000 | [diff] [blame] | 249 | } else { |
Gustav Sennton | b265016 | 2017-04-07 14:41:38 +0100 | [diff] [blame] | 250 | // Multiprocess should not be enabled, unless the user has turned it on manually. |
| 251 | return settingValue >= MULTIPROCESS_SETTING_ON_VALUE; |
Torne (Richard Coles) | dc37507 | 2017-01-11 15:48:13 +0000 | [diff] [blame] | 252 | } |
Torne (Richard Coles) | 1a4c4e3 | 2017-01-10 15:57:41 +0000 | [diff] [blame] | 253 | } |
| 254 | |
| 255 | void enableMultiProcess(boolean enable) { |
| 256 | PackageInfo current = getCurrentWebViewPackage(); |
Torne (Richard Coles) | dc37507 | 2017-01-11 15:48:13 +0000 | [diff] [blame] | 257 | mSystemInterface.setMultiProcessSetting(mContext, |
Gustav Sennton | b265016 | 2017-04-07 14:41:38 +0100 | [diff] [blame] | 258 | enable ? MULTIPROCESS_SETTING_ON_VALUE : MULTIPROCESS_SETTING_OFF_VALUE); |
Torne (Richard Coles) | 1a4c4e3 | 2017-01-10 15:57:41 +0000 | [diff] [blame] | 259 | mSystemInterface.notifyZygote(enable); |
| 260 | if (current != null) { |
| 261 | mSystemInterface.killPackageDependents(current.packageName); |
| 262 | } |
| 263 | } |
| 264 | |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 265 | private static boolean isDisabledForAllUsers(List<UserPackage> userPackages) { |
| 266 | for (UserPackage userPackage : userPackages) { |
| 267 | if (userPackage.getPackageInfo() != null && userPackage.isEnabledPackage()) { |
| 268 | return false; |
| 269 | } |
| 270 | } |
| 271 | return true; |
Gustav Sennton | 0df2c55 | 2016-06-14 15:32:19 +0100 | [diff] [blame] | 272 | } |
| 273 | |
Robert Sesek | ded2098 | 2016-08-15 13:59:13 -0400 | [diff] [blame] | 274 | /** |
Gustav Sennton | 1eb3820 | 2016-10-21 13:40:10 +0100 | [diff] [blame] | 275 | * Dump the state of this Service. |
| 276 | */ |
| 277 | void dumpState(PrintWriter pw) { |
| 278 | pw.println("Current WebView Update Service state"); |
| 279 | pw.println(String.format(" Fallback logic enabled: %b", |
| 280 | mSystemInterface.isFallbackLogicEnabled())); |
Gustav Sennton | b0c715b | 2017-06-02 11:20:18 +0100 | [diff] [blame] | 281 | pw.println(String.format(" Multiprocess enabled: %b", isMultiProcessEnabled())); |
Gustav Sennton | 1eb3820 | 2016-10-21 13:40:10 +0100 | [diff] [blame] | 282 | mWebViewUpdater.dumpState(pw); |
| 283 | } |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 284 | } |