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; |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 19 | import android.content.pm.PackageInfo; |
Torne (Richard Coles) | 05a766e | 2019-04-10 17:55:53 -0400 | [diff] [blame] | 20 | import android.os.AsyncTask; |
Gustav Sennton | 0df2c55 | 2016-06-14 15:32:19 +0100 | [diff] [blame] | 21 | import android.os.UserHandle; |
Torne (Richard Coles) | 1ac3f11 | 2019-05-28 14:39:16 -0400 | [diff] [blame] | 22 | import android.util.Slog; |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 23 | import android.webkit.WebViewProviderInfo; |
| 24 | import android.webkit.WebViewProviderResponse; |
| 25 | |
Gustav Sennton | 1eb3820 | 2016-10-21 13:40:10 +0100 | [diff] [blame] | 26 | import java.io.PrintWriter; |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 27 | |
| 28 | /** |
| 29 | * Implementation of the WebViewUpdateService. |
| 30 | * This class doesn't depend on the android system like the actual Service does and can be used |
| 31 | * directly by tests (as long as they implement a SystemInterface). |
Gustav Sennton | 47d7ec8 | 2016-06-07 15:02:58 +0100 | [diff] [blame] | 32 | * |
Torne (Richard Coles) | ef47890 | 2019-03-28 15:03:21 -0400 | [diff] [blame] | 33 | * This class keeps track of and prepares the current WebView implementation, and needs to keep |
| 34 | * track of a couple of different things such as what package is used as WebView implementation. |
Gustav Sennton | 47d7ec8 | 2016-06-07 15:02:58 +0100 | [diff] [blame] | 35 | * |
| 36 | * The public methods in this class are accessed from WebViewUpdateService either on the UI thread |
Torne (Richard Coles) | ef47890 | 2019-03-28 15:03:21 -0400 | [diff] [blame] | 37 | * or on one of multiple Binder threads. The WebView preparation code shares state between threads |
| 38 | * meaning that code that chooses a new WebView implementation or checks which implementation is |
| 39 | * being used needs to hold a lock. |
Gustav Sennton | 47d7ec8 | 2016-06-07 15:02:58 +0100 | [diff] [blame] | 40 | * |
| 41 | * The WebViewUpdateService can be accessed in a couple of different ways. |
| 42 | * 1. It is started from the SystemServer at boot - at that point we just initiate some state such |
| 43 | * as the WebView preparation class. |
| 44 | * 2. The SystemServer calls WebViewUpdateService.prepareWebViewInSystemServer. This happens at boot |
| 45 | * and the WebViewUpdateService should not have been accessed before this call. In this call we |
Torne (Richard Coles) | ef47890 | 2019-03-28 15:03:21 -0400 | [diff] [blame] | 46 | * migrate away from the old fallback logic if necessary and then choose WebView implementation for |
| 47 | * the first time. |
Gustav Sennton | 47d7ec8 | 2016-06-07 15:02:58 +0100 | [diff] [blame] | 48 | * 3. The update service listens for Intents related to package installs and removals. These intents |
Torne (Richard Coles) | ef47890 | 2019-03-28 15:03:21 -0400 | [diff] [blame] | 49 | * are received and processed on the UI thread. Each intent can result in changing WebView |
| 50 | * implementation. |
Gustav Sennton | 47d7ec8 | 2016-06-07 15:02:58 +0100 | [diff] [blame] | 51 | * 4. The update service can be reached through Binder calls which are handled on specific binder |
| 52 | * threads. These calls can be made from any process. Generally they are used for changing WebView |
| 53 | * implementation (from Settings), getting information about the current WebView implementation (for |
| 54 | * loading WebView into an app process), or notifying the service about Relro creation being |
| 55 | * completed. |
| 56 | * |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 57 | * @hide |
| 58 | */ |
| 59 | public class WebViewUpdateServiceImpl { |
| 60 | private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName(); |
| 61 | |
| 62 | private SystemInterface mSystemInterface; |
| 63 | private WebViewUpdater mWebViewUpdater; |
Gustav Sennton | 86f7bbe | 2016-10-24 16:49:32 +0100 | [diff] [blame] | 64 | final private Context mContext; |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 65 | |
Gustav Sennton | b265016 | 2017-04-07 14:41:38 +0100 | [diff] [blame] | 66 | private final static int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE; |
| 67 | private final static int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE; |
| 68 | |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 69 | public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) { |
| 70 | mContext = context; |
| 71 | mSystemInterface = systemInterface; |
| 72 | mWebViewUpdater = new WebViewUpdater(mContext, mSystemInterface); |
| 73 | } |
| 74 | |
Gustav Sennton | 0df2c55 | 2016-06-14 15:32:19 +0100 | [diff] [blame] | 75 | void packageStateChanged(String packageName, int changedState, int userId) { |
| 76 | // We don't early out here in different cases where we could potentially early-out (e.g. if |
| 77 | // we receive PACKAGE_CHANGED for another user than the system user) since that would |
| 78 | // complicate this logic further and open up for more edge cases. |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 79 | mWebViewUpdater.packageStateChanged(packageName, changedState); |
| 80 | } |
| 81 | |
| 82 | void prepareWebViewInSystemServer() { |
Torne (Richard Coles) | ef47890 | 2019-03-28 15:03:21 -0400 | [diff] [blame] | 83 | migrateFallbackStateOnBoot(); |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 84 | mWebViewUpdater.prepareWebViewInSystemServer(); |
Torne (Richard Coles) | d6c5a30 | 2019-05-28 16:02:19 -0400 | [diff] [blame] | 85 | if (getCurrentWebViewPackage() == null) { |
| 86 | // We didn't find a valid WebView implementation. Try explicitly re-enabling the |
| 87 | // fallback package for all users in case it was disabled, even if we already did the |
| 88 | // one-time migration before. If this actually changes the state, WebViewUpdater will |
| 89 | // see the PackageManager broadcast shortly and try again. |
| 90 | WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); |
| 91 | WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders); |
| 92 | if (fallbackProvider != null) { |
| 93 | Slog.w(TAG, "No valid provider, trying to enable " + fallbackProvider.packageName); |
| 94 | mSystemInterface.enablePackageForAllUsers(mContext, fallbackProvider.packageName, |
| 95 | true); |
| 96 | } else { |
| 97 | Slog.e(TAG, "No valid provider and no fallback available."); |
| 98 | } |
| 99 | } |
| 100 | |
Torne (Richard Coles) | 1bc38ed | 2019-04-25 13:30:37 -0400 | [diff] [blame] | 101 | boolean multiProcessEnabled = isMultiProcessEnabled(); |
| 102 | mSystemInterface.notifyZygote(multiProcessEnabled); |
| 103 | if (multiProcessEnabled) { |
| 104 | AsyncTask.THREAD_POOL_EXECUTOR.execute(this::startZygoteWhenReady); |
| 105 | } |
Torne (Richard Coles) | 05a766e | 2019-04-10 17:55:53 -0400 | [diff] [blame] | 106 | } |
| 107 | |
| 108 | void startZygoteWhenReady() { |
| 109 | // Wait on a background thread for RELRO creation to be done. We ignore the return value |
| 110 | // because even if RELRO creation failed we still want to start the zygote. |
| 111 | waitForAndGetProvider(); |
| 112 | mSystemInterface.ensureZygoteStarted(); |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 113 | } |
| 114 | |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 115 | void handleNewUser(int userId) { |
Gustav Sennton | a43f144 | 2017-03-23 17:04:23 +0000 | [diff] [blame] | 116 | // The system user is always started at boot, and by that point we have already run one |
| 117 | // round of the package-changing logic (through prepareWebViewInSystemServer()), so early |
| 118 | // out here. |
| 119 | if (userId == UserHandle.USER_SYSTEM) return; |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 120 | handleUserChange(); |
| 121 | } |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 122 | |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 123 | void handleUserRemoved(int userId) { |
| 124 | handleUserChange(); |
| 125 | } |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 126 | |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 127 | /** |
Torne (Richard Coles) | ef47890 | 2019-03-28 15:03:21 -0400 | [diff] [blame] | 128 | * Called when a user was added or removed to ensure WebView preparation is triggered. |
| 129 | * This has to be done since the WebView package we use depends on the enabled-state |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 130 | * of packages for all users (so adding or removing a user might cause us to change package). |
| 131 | */ |
| 132 | private void handleUserChange() { |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 133 | // Potentially trigger package-changing logic. |
| 134 | mWebViewUpdater.updateCurrentWebViewPackage(null); |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 135 | } |
| 136 | |
| 137 | void notifyRelroCreationCompleted() { |
| 138 | mWebViewUpdater.notifyRelroCreationCompleted(); |
| 139 | } |
| 140 | |
| 141 | WebViewProviderResponse waitForAndGetProvider() { |
| 142 | return mWebViewUpdater.waitForAndGetProvider(); |
| 143 | } |
| 144 | |
| 145 | String changeProviderAndSetting(String newProvider) { |
| 146 | return mWebViewUpdater.changeProviderAndSetting(newProvider); |
| 147 | } |
| 148 | |
| 149 | WebViewProviderInfo[] getValidWebViewPackages() { |
Gustav Sennton | 364e160 | 2016-12-14 09:10:50 +0000 | [diff] [blame] | 150 | return mWebViewUpdater.getValidWebViewPackages(); |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 151 | } |
| 152 | |
| 153 | WebViewProviderInfo[] getWebViewPackages() { |
| 154 | return mSystemInterface.getWebViewPackages(); |
| 155 | } |
| 156 | |
Gustav Sennton | bf683e0 | 2016-09-15 14:42:50 +0100 | [diff] [blame] | 157 | PackageInfo getCurrentWebViewPackage() { |
| 158 | return mWebViewUpdater.getCurrentWebViewPackage(); |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 159 | } |
| 160 | |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 161 | /** |
Torne (Richard Coles) | ef47890 | 2019-03-28 15:03:21 -0400 | [diff] [blame] | 162 | * If the fallback logic is enabled, re-enable any fallback package for all users, then |
| 163 | * disable the fallback logic. |
| 164 | * |
| 165 | * This migrates away from the old fallback mechanism to the new state where packages are never |
| 166 | * automatically enableenableisabled. |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 167 | */ |
Torne (Richard Coles) | ef47890 | 2019-03-28 15:03:21 -0400 | [diff] [blame] | 168 | private void migrateFallbackStateOnBoot() { |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 169 | if (!mSystemInterface.isFallbackLogicEnabled()) return; |
| 170 | |
| 171 | WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 172 | WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders); |
Torne (Richard Coles) | ef47890 | 2019-03-28 15:03:21 -0400 | [diff] [blame] | 173 | if (fallbackProvider != null) { |
Torne (Richard Coles) | 1ac3f11 | 2019-05-28 14:39:16 -0400 | [diff] [blame] | 174 | Slog.i(TAG, "One-time migration: enabling " + fallbackProvider.packageName); |
Torne (Richard Coles) | ef47890 | 2019-03-28 15:03:21 -0400 | [diff] [blame] | 175 | mSystemInterface.enablePackageForAllUsers(mContext, fallbackProvider.packageName, true); |
Torne (Richard Coles) | 1ac3f11 | 2019-05-28 14:39:16 -0400 | [diff] [blame] | 176 | } else { |
| 177 | Slog.i(TAG, "Skipping one-time migration: no fallback provider"); |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 178 | } |
Torne (Richard Coles) | ef47890 | 2019-03-28 15:03:21 -0400 | [diff] [blame] | 179 | mSystemInterface.enableFallbackLogic(false); |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 180 | } |
| 181 | |
| 182 | /** |
| 183 | * Returns the only fallback provider in the set of given packages, or null if there is none. |
| 184 | */ |
| 185 | private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) { |
| 186 | for (WebViewProviderInfo provider : webviewPackages) { |
| 187 | if (provider.isFallback) { |
| 188 | return provider; |
| 189 | } |
| 190 | } |
| 191 | return null; |
| 192 | } |
| 193 | |
Torne (Richard Coles) | 1a4c4e3 | 2017-01-10 15:57:41 +0000 | [diff] [blame] | 194 | boolean isMultiProcessEnabled() { |
Torne (Richard Coles) | dc37507 | 2017-01-11 15:48:13 +0000 | [diff] [blame] | 195 | int settingValue = mSystemInterface.getMultiProcessSetting(mContext); |
| 196 | if (mSystemInterface.isMultiProcessDefaultEnabled()) { |
Gustav Sennton | b265016 | 2017-04-07 14:41:38 +0100 | [diff] [blame] | 197 | // Multiprocess should be enabled unless the user has turned it off manually. |
| 198 | return settingValue > MULTIPROCESS_SETTING_OFF_VALUE; |
Torne (Richard Coles) | dc37507 | 2017-01-11 15:48:13 +0000 | [diff] [blame] | 199 | } else { |
Gustav Sennton | b265016 | 2017-04-07 14:41:38 +0100 | [diff] [blame] | 200 | // Multiprocess should not be enabled, unless the user has turned it on manually. |
| 201 | return settingValue >= MULTIPROCESS_SETTING_ON_VALUE; |
Torne (Richard Coles) | dc37507 | 2017-01-11 15:48:13 +0000 | [diff] [blame] | 202 | } |
Torne (Richard Coles) | 1a4c4e3 | 2017-01-10 15:57:41 +0000 | [diff] [blame] | 203 | } |
| 204 | |
| 205 | void enableMultiProcess(boolean enable) { |
| 206 | PackageInfo current = getCurrentWebViewPackage(); |
Torne (Richard Coles) | dc37507 | 2017-01-11 15:48:13 +0000 | [diff] [blame] | 207 | mSystemInterface.setMultiProcessSetting(mContext, |
Gustav Sennton | b265016 | 2017-04-07 14:41:38 +0100 | [diff] [blame] | 208 | enable ? MULTIPROCESS_SETTING_ON_VALUE : MULTIPROCESS_SETTING_OFF_VALUE); |
Torne (Richard Coles) | 1a4c4e3 | 2017-01-10 15:57:41 +0000 | [diff] [blame] | 209 | mSystemInterface.notifyZygote(enable); |
| 210 | if (current != null) { |
| 211 | mSystemInterface.killPackageDependents(current.packageName); |
| 212 | } |
| 213 | } |
| 214 | |
Robert Sesek | ded2098 | 2016-08-15 13:59:13 -0400 | [diff] [blame] | 215 | /** |
Gustav Sennton | 1eb3820 | 2016-10-21 13:40:10 +0100 | [diff] [blame] | 216 | * Dump the state of this Service. |
| 217 | */ |
| 218 | void dumpState(PrintWriter pw) { |
| 219 | pw.println("Current WebView Update Service state"); |
| 220 | pw.println(String.format(" Fallback logic enabled: %b", |
| 221 | mSystemInterface.isFallbackLogicEnabled())); |
Gustav Sennton | b0c715b | 2017-06-02 11:20:18 +0100 | [diff] [blame] | 222 | pw.println(String.format(" Multiprocess enabled: %b", isMultiProcessEnabled())); |
Gustav Sennton | 1eb3820 | 2016-10-21 13:40:10 +0100 | [diff] [blame] | 223 | mWebViewUpdater.dumpState(pw); |
| 224 | } |
Gustav Sennton | 79fea48 | 2016-04-07 14:22:56 +0100 | [diff] [blame] | 225 | } |