blob: 2db6b5d4bf9047ec71a29674c766fead66e2aa69 [file] [log] [blame]
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +01001/*
2 * Copyright (C) 2012 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
17package com.android.server.webkit;
18
Gustav Sennton6258dcd2015-10-30 19:25:37 +000019import android.app.ActivityManagerNative;
20import android.app.AppGlobals;
Ben Murdochdc00a842014-07-17 14:55:00 +010021import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
Gustav Sennton6258dcd2015-10-30 19:25:37 +000025import android.content.pm.ApplicationInfo;
Gustav Senntonc83e3fa2016-02-18 12:19:13 +000026import android.content.pm.IPackageDeleteObserver;
Gustav Sennton6258dcd2015-10-30 19:25:37 +000027import android.content.pm.PackageInfo;
28import android.content.pm.PackageManager;
29import android.content.pm.Signature;
Gustav Senntonc83e3fa2016-02-18 12:19:13 +000030import android.content.pm.UserInfo;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010031import android.os.Binder;
Gustav Senntonc83e3fa2016-02-18 12:19:13 +000032import android.os.PatternMatcher;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010033import android.os.Process;
Gustav Sennton6258dcd2015-10-30 19:25:37 +000034import android.os.RemoteException;
Gustav Senntonc83e3fa2016-02-18 12:19:13 +000035import android.os.ResultReceiver;
Gustav Sennton23875b22016-02-09 14:11:33 +000036import android.os.UserHandle;
Gustav Senntonc83e3fa2016-02-18 12:19:13 +000037import android.os.UserManager;
Gustav Sennton6258dcd2015-10-30 19:25:37 +000038import android.provider.Settings;
Gustav Sennton14c033c2016-02-11 12:51:27 +000039import android.provider.Settings.Global;
Gustav Sennton6258dcd2015-10-30 19:25:37 +000040import android.util.AndroidRuntimeException;
Primiano Tucci810c0522014-07-25 18:03:16 +010041import android.util.Slog;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010042import android.webkit.IWebViewUpdateService;
Gustav Sennton6258dcd2015-10-30 19:25:37 +000043import android.webkit.WebViewProviderInfo;
44import android.webkit.WebViewProviderResponse;
Ben Murdochdc00a842014-07-17 14:55:00 +010045import android.webkit.WebViewFactory;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010046
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +010047import com.android.server.SystemService;
48
Gustav Senntonc83e3fa2016-02-18 12:19:13 +000049import java.io.FileDescriptor;
Gustav Sennton6258dcd2015-10-30 19:25:37 +000050import java.util.ArrayList;
51import java.util.Arrays;
52import java.util.Iterator;
53import java.util.List;
54
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010055/**
56 * Private service to wait for the updatable WebView to be ready for use.
57 * @hide
58 */
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +010059public class WebViewUpdateService extends SystemService {
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010060
61 private static final String TAG = "WebViewUpdateService";
Gustav Sennton6258dcd2015-10-30 19:25:37 +000062 private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000.
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010063
Gustav Sennton6258dcd2015-10-30 19:25:37 +000064 // Keeps track of the number of running relro creations
65 private int mNumRelroCreationsStarted = 0;
66 private int mNumRelroCreationsFinished = 0;
67 // Implies that we need to rerun relro creation because we are using an out-of-date package
68 private boolean mWebViewPackageDirty = false;
69 // Set to true when the current provider is being replaced
70 private boolean mCurrentProviderBeingReplaced = false;
71 private boolean mAnyWebViewInstalled = false;
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +010072
Gustav Sennton6258dcd2015-10-30 19:25:37 +000073 private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
74
75 // The WebView package currently in use (or the one we are preparing).
76 private PackageInfo mCurrentWebViewPackage = null;
77 // The WebView providers that are currently available.
78 private WebViewProviderInfo[] mCurrentValidWebViewPackages = null;
Gustav Sennton6ce92c92015-10-23 11:10:39 +010079
Ben Murdochdc00a842014-07-17 14:55:00 +010080 private BroadcastReceiver mWebViewUpdatedReceiver;
81
82 public WebViewUpdateService(Context context) {
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +010083 super(context);
84 }
85
86 @Override
87 public void onStart() {
Ben Murdochdc00a842014-07-17 14:55:00 +010088 mWebViewUpdatedReceiver = new BroadcastReceiver() {
89 @Override
90 public void onReceive(Context context, Intent intent) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +000091 // When a package is replaced we will receive two intents, one representing
92 // the removal of the old package and one representing the addition of the
93 // new package.
94 // In the case where we receive an intent to remove the old version of the
95 // package that is being replaced we set a flag here and early-out so that we
96 // don't change provider while replacing the current package (we will instead
97 // change provider when the new version of the package is being installed).
Gustav Sennton3098cf22015-11-10 03:33:09 +000098 if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)
Gustav Sennton6258dcd2015-10-30 19:25:37 +000099 && intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) {
Gustav Sennton14c033c2016-02-11 12:51:27 +0000100 synchronized(WebViewUpdateService.this) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000101 if (mCurrentWebViewPackage == null) return;
102
103 String webViewPackage = "package:" + mCurrentWebViewPackage.packageName;
104 if (webViewPackage.equals(intent.getDataString()))
105 mCurrentProviderBeingReplaced = true;
106 }
107
Gustav Sennton3098cf22015-11-10 03:33:09 +0000108 return;
109 }
110
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000111 // Ensure that we only heed PACKAGE_CHANGED intents if they change an entire
112 // package, not just a component
113 if (intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED)) {
114 if (!WebViewFactory.entirePackageChanged(intent)) {
115 return;
116 }
117 }
118
119 if (intent.getAction().equals(Intent.ACTION_USER_ADDED)) {
120 int userId =
121 intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
122 handleNewUser(userId);
123 return;
124 }
125
126 updateFallbackState(context, intent);
127
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000128 for (WebViewProviderInfo provider : WebViewFactory.getWebViewPackages()) {
129 String webviewPackage = "package:" + provider.packageName;
Gustav Sennton6ce92c92015-10-23 11:10:39 +0100130
131 if (webviewPackage.equals(intent.getDataString())) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000132 boolean updateWebView = false;
Gustav Sennton1e5d8032016-02-25 12:26:36 +0000133 boolean removedOrChangedOldPackage = false;
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000134 String oldProviderName = null;
135 PackageInfo newPackage = null;
136 synchronized(WebViewUpdateService.this) {
137 try {
138 updateValidWebViewPackages();
139 newPackage = findPreferredWebViewPackage();
140 if (mCurrentWebViewPackage != null)
141 oldProviderName = mCurrentWebViewPackage.packageName;
142 // Only trigger update actions if the updated package is the one
143 // that will be used, or the one that was in use before the
144 // update, or if we haven't seen a valid WebView package before.
145 updateWebView =
146 provider.packageName.equals(newPackage.packageName)
147 || provider.packageName.equals(oldProviderName)
148 || mCurrentWebViewPackage == null;
149 // We removed the old package if we received an intent to remove
150 // or replace the old package.
Gustav Sennton1e5d8032016-02-25 12:26:36 +0000151 removedOrChangedOldPackage =
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000152 provider.packageName.equals(oldProviderName);
153 if (updateWebView) {
154 onWebViewProviderChanged(newPackage);
155 }
156 } catch (WebViewFactory.MissingWebViewPackageException e) {
157 Slog.e(TAG, "Could not find valid WebView package to create " +
158 "relro with " + e);
159 }
160 }
Gustav Sennton1e5d8032016-02-25 12:26:36 +0000161 if(updateWebView && !removedOrChangedOldPackage
162 && oldProviderName != null) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000163 // If the provider change is the result of adding or replacing a
164 // package that was not the previous provider then we must kill
165 // packages dependent on the old package ourselves. The framework
166 // only kills dependents of packages that are being removed.
167 try {
168 ActivityManagerNative.getDefault().killPackageDependents(
Gustav Sennton23875b22016-02-09 14:11:33 +0000169 oldProviderName, UserHandle.USER_ALL);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000170 } catch (RemoteException e) {
171 }
Gustav Sennton6ce92c92015-10-23 11:10:39 +0100172 }
173 return;
174 }
Ben Murdochdc00a842014-07-17 14:55:00 +0100175 }
176 }
177 };
178 IntentFilter filter = new IntentFilter();
Gustav Sennton3098cf22015-11-10 03:33:09 +0000179 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
180 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000181 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Ben Murdochdc00a842014-07-17 14:55:00 +0100182 filter.addDataScheme("package");
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000183 // Make sure we only receive intents for WebView packages from our config file.
184 for (WebViewProviderInfo provider : WebViewFactory.getWebViewPackages()) {
185 filter.addDataSchemeSpecificPart(provider.packageName, PatternMatcher.PATTERN_LITERAL);
186 }
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100187 getContext().registerReceiver(mWebViewUpdatedReceiver, filter);
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100188
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000189 IntentFilter userAddedFilter = new IntentFilter();
190 userAddedFilter.addAction(Intent.ACTION_USER_ADDED);
191 getContext().registerReceiver(mWebViewUpdatedReceiver, userAddedFilter);
192
Torne (Richard Coles)fc19b0a2016-02-01 16:16:57 +0000193 publishBinderService("webviewupdate", new BinderService(), true /*allowIsolated*/);
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100194 }
Ben Murdochdc00a842014-07-17 14:55:00 +0100195
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000196 private static boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
197 for (WebViewProviderInfo provider : providers) {
198 if (provider.isAvailableByDefault() && provider.isEnabled()
199 && provider.isValidProvider() && !provider.isFallbackPackage()) {
200 return true;
201 }
202 }
203 return false;
204 }
205
206 private static void enablePackageForUser(String packageName, boolean enable, int userId) {
207 try {
208 AppGlobals.getPackageManager().setApplicationEnabledSetting(
209 packageName,
210 enable ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT :
211 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0,
212 userId, null);
213 } catch (RemoteException e) {
214 Slog.w(TAG, "Tried to disable " + packageName + " for user " + userId + ": " + e);
215 }
216 }
217
218 /**
219 * Called when a new user has been added to update the state of its fallback package.
220 */
221 void handleNewUser(int userId) {
222 if (!isFallbackLogicEnabled()) return;
223
224 WebViewProviderInfo[] webviewProviders = WebViewFactory.getWebViewPackages();
225 WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
226 if (fallbackProvider == null) return;
227 boolean existsValidNonFallbackProvider =
228 existsValidNonFallbackProvider(webviewProviders);
229
230 enablePackageForUser(fallbackProvider.packageName, !existsValidNonFallbackProvider,
231 userId);
232 }
233
234 /**
235 * Handle the enabled-state of our fallback package, i.e. if there exists some non-fallback
236 * package that is valid (and available by default) then disable the fallback package,
237 * otherwise, enable the fallback package.
238 */
239 void updateFallbackState(final Context context, final Intent intent) {
240 if (!isFallbackLogicEnabled()) return;
241
242 WebViewProviderInfo[] webviewProviders = WebViewFactory.getWebViewPackages();
243
244 if (intent != null && (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)
245 || intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED))) {
246 // A package was changed / updated / downgraded, early out if it is not one of the
247 // webview packages that are available by default.
248 String changedPackage = null;
249 for (WebViewProviderInfo provider : webviewProviders) {
250 String webviewPackage = "package:" + provider.packageName;
251 if (webviewPackage.equals(intent.getDataString())) {
252 if (provider.isAvailableByDefault()) {
253 changedPackage = provider.packageName;
254 }
255 break;
256 }
257 }
258 if (changedPackage == null) return;
259 }
260
261 // If there exists a valid and enabled non-fallback package - disable the fallback
262 // package, otherwise, enable it.
263 WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
264 if (fallbackProvider == null) return;
265 boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders);
266
267 if (existsValidNonFallbackProvider
268 // During an OTA the primary user's WebView state might differ from other users', so
269 // ignore the state of that user during boot.
270 && (fallbackProvider.isEnabled() || intent == null)) {
271 // Uninstall and disable fallback package for all users.
272 context.getPackageManager().deletePackage(fallbackProvider.packageName,
273 new IPackageDeleteObserver.Stub() {
274 public void packageDeleted(String packageName, int returnCode) {
275 // Ignore returnCode since the deletion could fail, e.g. we might be trying
276 // to delete a non-updated system-package (and we should still disable the
277 // package)
278 UserManager userManager =
279 (UserManager)context.getSystemService(Context.USER_SERVICE);
280 // Disable the fallback package for all users.
281 for(UserInfo userInfo : userManager.getUsers()) {
282 enablePackageForUser(packageName, false, userInfo.id);
283 }
284 }
285 }, PackageManager.DELETE_SYSTEM_APP | PackageManager.DELETE_ALL_USERS);
286 } else if (!existsValidNonFallbackProvider
287 // During an OTA the primary user's WebView state might differ from other users', so
288 // ignore the state of that user during boot.
289 && (!fallbackProvider.isEnabled() || intent==null)) {
290 // Enable the fallback package for all users.
291 UserManager userManager =
292 (UserManager)context.getSystemService(Context.USER_SERVICE);
293 for(UserInfo userInfo : userManager.getUsers()) {
294 enablePackageForUser(fallbackProvider.packageName, true, userInfo.id);
295 }
296 }
297 }
298
299 private static boolean isFallbackLogicEnabled() {
300 // Note that this is enabled by default (i.e. if the setting hasn't been set).
301 return Settings.Global.getInt(AppGlobals.getInitialApplication().getContentResolver(),
302 Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, 1) == 1;
303 }
304
305 private static void enableFallbackLogic(boolean enable) {
306 Settings.Global.putInt(AppGlobals.getInitialApplication().getContentResolver(),
307 Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, enable ? 1 : 0);
308 }
309
310 /**
311 * Returns the only fallback provider, or null if there is none.
312 */
313 private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
314 for (WebViewProviderInfo provider : webviewPackages) {
315 if (provider.isFallbackPackage()) {
316 return provider;
317 }
318 }
319 return null;
320 }
321
322 private static boolean containsAvailableNonFallbackProvider(
323 WebViewProviderInfo[] webviewPackages) {
324 for (WebViewProviderInfo provider : webviewPackages) {
325 if (provider.isAvailableByDefault() && provider.isEnabled()
326 && provider.isValidProvider() && !provider.isFallbackPackage()) {
327 return true;
328 }
329 }
330 return false;
331 }
332
333 private static boolean isFallbackPackage(String packageName) {
334 if (packageName == null || !isFallbackLogicEnabled()) return false;
335
336 WebViewProviderInfo[] webviewPackages = WebViewFactory.getWebViewPackages();
337 WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages);
338 return (fallbackProvider != null
339 && packageName.equals(fallbackProvider.packageName));
340 }
341
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000342 /**
343 * Perform any WebView loading preparations that must happen at boot from the system server,
344 * after the package manager has started or after an update to the webview is installed.
345 * This must be called in the system server.
346 * Currently, this means spawning the child processes which will create the relro files.
347 */
348 public void prepareWebViewInSystemServer() {
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000349 updateFallbackState(getContext(), null);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000350 try {
351 synchronized(this) {
352 updateValidWebViewPackages();
353 mCurrentWebViewPackage = findPreferredWebViewPackage();
354 onWebViewProviderChanged(mCurrentWebViewPackage);
355 }
356 } catch (Throwable t) {
357 // Log and discard errors at this stage as we must not crash the system server.
358 Slog.e(TAG, "error preparing webview provider from system server", t);
Ben Murdochdc00a842014-07-17 14:55:00 +0100359 }
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000360 }
361
362
363 /**
364 * Change WebView provider and provider setting and kill packages using the old provider.
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000365 * Return the new provider (in case we are in the middle of creating relro files this new
366 * provider will not be in use directly, but will when the relros are done).
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000367 */
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000368 private String changeProviderAndSetting(String newProviderName) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000369 PackageInfo oldPackage = null;
370 PackageInfo newPackage = null;
371 synchronized(this) {
372 oldPackage = mCurrentWebViewPackage;
373 updateUserSetting(newProviderName);
374
375 try {
376 newPackage = findPreferredWebViewPackage();
377 if (oldPackage != null && newPackage.packageName.equals(oldPackage.packageName)) {
378 // If we don't perform the user change, revert the settings change.
379 updateUserSetting(newPackage.packageName);
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000380 return newPackage.packageName;
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000381 }
382 } catch (WebViewFactory.MissingWebViewPackageException e) {
383 Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView package "
384 + e);
385 // If we don't perform the user change but don't have an installed WebView package,
386 // we will have changed the setting and it will be used when a package is available.
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000387 return newProviderName;
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000388 }
389 onWebViewProviderChanged(newPackage);
390 }
391 // Kill apps using the old provider
392 try {
393 if (oldPackage != null) {
394 ActivityManagerNative.getDefault().killPackageDependents(
Gustav Sennton23875b22016-02-09 14:11:33 +0000395 oldPackage.packageName, UserHandle.USER_ALL);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000396 }
397 } catch (RemoteException e) {
398 }
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000399 return newPackage.packageName;
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000400 }
401
402 /**
403 * This is called when we change WebView provider, either when the current provider is updated
404 * or a new provider is chosen / takes precedence.
405 */
406 private void onWebViewProviderChanged(PackageInfo newPackage) {
407 synchronized(this) {
408 mAnyWebViewInstalled = true;
409 // If we have changed provider then the replacement of the old provider is
410 // irrelevant - we can only have chosen a new provider if its package is available.
411 mCurrentProviderBeingReplaced = false;
412 if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
413 mCurrentWebViewPackage = newPackage;
414 updateUserSetting(newPackage.packageName);
415
416 // The relro creations might 'finish' (not start at all) before
417 // WebViewFactory.onWebViewProviderChanged which means we might not know the number
418 // of started creations before they finish.
419 mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
420 mNumRelroCreationsFinished = 0;
421 mNumRelroCreationsStarted = WebViewFactory.onWebViewProviderChanged(newPackage);
422 // If the relro creations finish before we know the number of started creations we
423 // will have to do any cleanup/notifying here.
424 checkIfRelrosDoneLocked();
425 } else {
426 mWebViewPackageDirty = true;
427 }
428 }
429 }
430
431 /**
432 * Updates the currently valid WebView provider packages.
433 * Should be used when a provider has been installed or removed.
434 * @hide
435 * */
436 private void updateValidWebViewPackages() {
437 List<WebViewProviderInfo> webViewProviders =
438 new ArrayList<WebViewProviderInfo>(Arrays.asList(WebViewFactory.getWebViewPackages()));
439 Iterator<WebViewProviderInfo> it = webViewProviders.iterator();
440 // remove non-valid packages
441 while(it.hasNext()) {
442 WebViewProviderInfo current = it.next();
443 if (!current.isValidProvider())
444 it.remove();
445 }
446 synchronized(this) {
447 mCurrentValidWebViewPackages =
448 webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
449 }
450 }
451
452 private static String getUserChosenWebViewProvider() {
Gustav Sennton14c033c2016-02-11 12:51:27 +0000453 return Settings.Global.getString(AppGlobals.getInitialApplication().getContentResolver(),
454 Settings.Global.WEBVIEW_PROVIDER);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000455 }
456
457 private void updateUserSetting(String newProviderName) {
Gustav Sennton14c033c2016-02-11 12:51:27 +0000458 Settings.Global.putString(getContext().getContentResolver(),
459 Settings.Global.WEBVIEW_PROVIDER,
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000460 newProviderName == null ? "" : newProviderName);
461 }
462
463 /**
464 * Returns either the package info of the WebView provider determined in the following way:
465 * If the user has chosen a provider then use that if it is valid,
466 * otherwise use the first package in the webview priority list that is valid.
467 *
468 * @hide
469 */
470 private PackageInfo findPreferredWebViewPackage() {
471 WebViewProviderInfo[] providers = mCurrentValidWebViewPackages;
472
473 String userChosenProvider = getUserChosenWebViewProvider();
474
475 // If the user has chosen provider, use that
476 for (WebViewProviderInfo provider : providers) {
Gustav Sennton27f13de2016-01-05 20:22:02 +0000477 if (provider.packageName.equals(userChosenProvider) && provider.isEnabled()) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000478 return provider.getPackageInfo();
479 }
480 }
481
Gustav Sennton27f13de2016-01-05 20:22:02 +0000482 // User did not choose, or the choice failed; use the most stable provider that is
483 // enabled and available by default (not through user choice).
484 for (WebViewProviderInfo provider : providers) {
485 if (provider.isAvailableByDefault() && provider.isEnabled()) {
486 return provider.getPackageInfo();
487 }
488 }
489
490 // Could not find any enabled package either, use the most stable provider.
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000491 for (WebViewProviderInfo provider : providers) {
492 return provider.getPackageInfo();
493 }
Gustav Sennton27f13de2016-01-05 20:22:02 +0000494
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000495 mAnyWebViewInstalled = false;
496 throw new WebViewFactory.MissingWebViewPackageException(
497 "Could not find a loadable WebView package");
498 }
499
500 /**
501 * Returns whether WebView is ready and is not going to go through its preparation phase again
502 * directly.
503 */
504 private boolean webViewIsReadyLocked() {
505 return !mWebViewPackageDirty
506 && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
507 && !mCurrentProviderBeingReplaced
508 // The current package might be replaced though we haven't received an intent declaring
509 // this yet, the following flag makes anyone loading WebView to wait in this case.
510 && mAnyWebViewInstalled;
511 }
512
513 private void checkIfRelrosDoneLocked() {
514 if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
515 if (mWebViewPackageDirty) {
516 mWebViewPackageDirty = false;
517 // If we have changed provider since we started the relro creation we need to
518 // redo the whole process using the new package instead.
519 // Though, if the current provider package is being replaced we don't want to change
520 // provider here since we will perform the change either when the package is added
521 // again or when we switch to another provider (whichever comes first).
522 if (!mCurrentProviderBeingReplaced) {
523 PackageInfo newPackage = findPreferredWebViewPackage();
524 onWebViewProviderChanged(newPackage);
525 }
526 } else {
527 this.notifyAll();
528 }
529 }
Ben Murdochdc00a842014-07-17 14:55:00 +0100530 }
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100531
532 private class BinderService extends IWebViewUpdateService.Stub {
533
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000534 @Override
535 public void onShellCommand(FileDescriptor in, FileDescriptor out,
536 FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
537 (new WebViewUpdateServiceShellCommand(this)).exec(
538 this, in, out, err, args, resultReceiver);
539 }
540
541
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100542 /**
543 * The shared relro process calls this to notify us that it's done trying to create a relro
544 * file. This method gets called even if the relro creation has failed or the process
545 * crashed.
546 */
547 @Override // Binder call
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000548 public void notifyRelroCreationCompleted() {
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100549 // Verify that the caller is either the shared relro process (nominal case) or the
550 // system server (only in the case the relro process crashes and we get here via the
551 // crashHandler).
552 if (Binder.getCallingUid() != Process.SHARED_RELRO_UID &&
553 Binder.getCallingUid() != Process.SYSTEM_UID) {
554 return;
555 }
556
Gustav Sennton275d13c2016-02-24 10:58:09 +0000557 long callingId = Binder.clearCallingIdentity();
558 try {
559 synchronized (WebViewUpdateService.this) {
560 mNumRelroCreationsFinished++;
561 checkIfRelrosDoneLocked();
562 }
563 } finally {
564 Binder.restoreCallingIdentity(callingId);
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100565 }
566 }
567
568 /**
569 * WebViewFactory calls this to block WebView loading until the relro file is created.
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000570 * Returns the WebView provider for which we create relro files.
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100571 */
572 @Override // Binder call
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000573 public WebViewProviderResponse waitForAndGetProvider() {
Primiano Tuccie76e81a2014-07-29 16:38:33 +0100574 // The WebViewUpdateService depends on the prepareWebViewInSystemServer call, which
575 // happens later (during the PHASE_ACTIVITY_MANAGER_READY) in SystemServer.java. If
576 // another service there tries to bring up a WebView in the between, the wait below
577 // would deadlock without the check below.
578 if (Binder.getCallingPid() == Process.myPid()) {
579 throw new IllegalStateException("Cannot create a WebView from the SystemServer");
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100580 }
581
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000582 PackageInfo webViewPackage = null;
Primiano Tuccie76e81a2014-07-29 16:38:33 +0100583 final long NS_PER_MS = 1000000;
584 final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000585 boolean webViewReady = false;
586 int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100587 synchronized (WebViewUpdateService.this) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000588 webViewReady = WebViewUpdateService.this.webViewIsReadyLocked();
589 while (!webViewReady) {
Primiano Tuccie76e81a2014-07-29 16:38:33 +0100590 final long timeNowMs = System.nanoTime() / NS_PER_MS;
591 if (timeNowMs >= timeoutTimeMs) break;
592 try {
593 WebViewUpdateService.this.wait(timeoutTimeMs - timeNowMs);
594 } catch (InterruptedException e) {}
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000595 webViewReady = WebViewUpdateService.this.webViewIsReadyLocked();
596 }
597 // Make sure we return the provider that was used to create the relro file
598 webViewPackage = WebViewUpdateService.this.mCurrentWebViewPackage;
599 if (webViewReady) {
600 } else if (mCurrentProviderBeingReplaced) {
601 // It is important that we check this flag before the one representing WebView
602 // being installed, otherwise we might think there is no WebView though the
603 // current one is just being replaced.
604 webViewStatus = WebViewFactory.LIBLOAD_WEBVIEW_BEING_REPLACED;
605 } else if (!mAnyWebViewInstalled) {
606 webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
607 } else {
608 // Either the current relro creation isn't done yet, or the new relro creatioin
609 // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
610 webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100611 }
612 }
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000613 if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
614 return new WebViewProviderResponse(webViewPackage, webViewStatus);
615 }
616
617 /**
618 * This is called from DeveloperSettings when the user changes WebView provider.
619 */
620 @Override // Binder call
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000621 public String changeProviderAndSetting(String newProvider) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000622 if (getContext().checkCallingPermission(
623 android.Manifest.permission.WRITE_SECURE_SETTINGS)
624 != PackageManager.PERMISSION_GRANTED) {
625 String msg = "Permission Denial: changeProviderAndSetting() from pid="
626 + Binder.getCallingPid()
627 + ", uid=" + Binder.getCallingUid()
628 + " requires " + android.Manifest.permission.WRITE_SECURE_SETTINGS;
629 Slog.w(TAG, msg);
630 throw new SecurityException(msg);
631 }
632
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000633 return WebViewUpdateService.this.changeProviderAndSetting(newProvider);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000634 }
635
636 @Override // Binder call
637 public WebViewProviderInfo[] getValidWebViewPackages() {
638 synchronized(WebViewUpdateService.this) {
639 return mCurrentValidWebViewPackages;
640 }
641 }
642
643 @Override // Binder call
644 public String getCurrentWebViewPackageName() {
645 synchronized(WebViewUpdateService.this) {
646 if (WebViewUpdateService.this.mCurrentWebViewPackage == null)
647 return null;
648 return WebViewUpdateService.this.mCurrentWebViewPackage.packageName;
649 }
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100650 }
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000651
652 @Override // Binder call
653 public boolean isFallbackPackage(String packageName) {
654 return WebViewUpdateService.isFallbackPackage(packageName);
655 }
656
657 @Override // Binder call
658 public void enableFallbackLogic(boolean enable) {
659 if (getContext().checkCallingPermission(
660 android.Manifest.permission.WRITE_SECURE_SETTINGS)
661 != PackageManager.PERMISSION_GRANTED) {
662 String msg = "Permission Denial: enableFallbackLogic() from pid="
663 + Binder.getCallingPid()
664 + ", uid=" + Binder.getCallingUid()
665 + " requires " + android.Manifest.permission.WRITE_SECURE_SETTINGS;
666 Slog.w(TAG, msg);
667 throw new SecurityException(msg);
668 }
669
670 WebViewUpdateService.enableFallbackLogic(enable);
671 }
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100672 }
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100673}