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; 21 import android.content.pm.Signature; 22 import android.os.UserHandle; 23 import android.util.Slog; 24 import android.webkit.UserPackage; 25 import android.webkit.WebViewProviderInfo; 26 import android.webkit.WebViewProviderResponse; 27 28 import java.io.PrintWriter; 29 import java.lang.Integer; 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). 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 * 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; 74 final private Context mContext; 75 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 79 public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) { 80 mContext = context; 81 mSystemInterface = systemInterface; 82 mWebViewUpdater = new WebViewUpdater(mContext, mSystemInterface); 83 } 84 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. 89 updateFallbackStateOnPackageChange(packageName, changedState); 90 mWebViewUpdater.packageStateChanged(packageName, changedState); 91 } 92 93 void prepareWebViewInSystemServer() { 94 updateFallbackStateOnBoot(); 95 mWebViewUpdater.prepareWebViewInSystemServer(); 96 mSystemInterface.notifyZygote(isMultiProcessEnabled()); 97 } 98 99 private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) { 100 for (WebViewProviderInfo provider : providers) { 101 if (provider.availableByDefault && !provider.isFallback) { 102 // userPackages can contain null objects. 103 List<UserPackage> userPackages = 104 mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider); 105 if (WebViewUpdater.isInstalledAndEnabledForAllUsers(userPackages) && 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; 112 } 113 } 114 } 115 return false; 116 } 117 118 void handleNewUser(int userId) { 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; 123 handleUserChange(); 124 } 125 126 void handleUserRemoved(int userId) { 127 handleUserChange(); 128 } 129 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); 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() { 156 return mWebViewUpdater.getValidWebViewPackages(); 157 } 158 159 WebViewProviderInfo[] getWebViewPackages() { 160 return mSystemInterface.getWebViewPackages(); 161 } 162 163 PackageInfo getCurrentWebViewPackage() { 164 return mWebViewUpdater.getCurrentWebViewPackage(); 165 } 166 167 void enableFallbackLogic(boolean enable) { 168 mSystemInterface.enableFallbackLogic(enable); 169 } 170 171 private void updateFallbackStateOnBoot() { 172 if (!mSystemInterface.isFallbackLogicEnabled()) return; 173 174 WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); 175 updateFallbackState(webviewProviders); 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; 200 updateFallbackState(webviewProviders); 201 } 202 203 private void updateFallbackState(WebViewProviderInfo[] webviewProviders) { 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 210 List<UserPackage> userPackages = 211 mSystemInterface.getPackageInfoForProviderAllUsers(mContext, fallbackProvider); 212 if (existsValidNonFallbackProvider && !isDisabledForAllUsers(userPackages)) { 213 mSystemInterface.uninstallAndDisablePackageForAllUsers(mContext, 214 fallbackProvider.packageName); 215 } else if (!existsValidNonFallbackProvider 216 && !WebViewUpdater.isInstalledAndEnabledForAllUsers(userPackages)) { 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 244 boolean isMultiProcessEnabled() { 245 int settingValue = mSystemInterface.getMultiProcessSetting(mContext); 246 if (mSystemInterface.isMultiProcessDefaultEnabled()) { 247 // Multiprocess should be enabled unless the user has turned it off manually. 248 return settingValue > MULTIPROCESS_SETTING_OFF_VALUE; 249 } else { 250 // Multiprocess should not be enabled, unless the user has turned it on manually. 251 return settingValue >= MULTIPROCESS_SETTING_ON_VALUE; 252 } 253 } 254 255 void enableMultiProcess(boolean enable) { 256 PackageInfo current = getCurrentWebViewPackage(); 257 mSystemInterface.setMultiProcessSetting(mContext, 258 enable ? MULTIPROCESS_SETTING_ON_VALUE : MULTIPROCESS_SETTING_OFF_VALUE); 259 mSystemInterface.notifyZygote(enable); 260 if (current != null) { 261 mSystemInterface.killPackageDependents(current.packageName); 262 } 263 } 264 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; 272 } 273 274 /** 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())); 281 pw.println(String.format(" Multiprocess enabled: %b", isMultiProcessEnabled())); 282 mWebViewUpdater.dumpState(pw); 283 } 284 } 285