blob: 1a8f5060f5f1dab0ffbe2f35231dbc3758b2646d [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
128 // TODO(gsennton) for now don't update WebView on PACKAGE_CHANGED as this will
129 // change the current behaviour even more, instead do this in a follow-up.
130 if (intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED)) return;
131
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000132 for (WebViewProviderInfo provider : WebViewFactory.getWebViewPackages()) {
133 String webviewPackage = "package:" + provider.packageName;
Gustav Sennton6ce92c92015-10-23 11:10:39 +0100134
135 if (webviewPackage.equals(intent.getDataString())) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000136 boolean updateWebView = false;
137 boolean removedOldPackage = false;
138 String oldProviderName = null;
139 PackageInfo newPackage = null;
140 synchronized(WebViewUpdateService.this) {
141 try {
142 updateValidWebViewPackages();
143 newPackage = findPreferredWebViewPackage();
144 if (mCurrentWebViewPackage != null)
145 oldProviderName = mCurrentWebViewPackage.packageName;
146 // Only trigger update actions if the updated package is the one
147 // that will be used, or the one that was in use before the
148 // update, or if we haven't seen a valid WebView package before.
149 updateWebView =
150 provider.packageName.equals(newPackage.packageName)
151 || provider.packageName.equals(oldProviderName)
152 || mCurrentWebViewPackage == null;
153 // We removed the old package if we received an intent to remove
154 // or replace the old package.
155 removedOldPackage =
156 provider.packageName.equals(oldProviderName);
157 if (updateWebView) {
158 onWebViewProviderChanged(newPackage);
159 }
160 } catch (WebViewFactory.MissingWebViewPackageException e) {
161 Slog.e(TAG, "Could not find valid WebView package to create " +
162 "relro with " + e);
163 }
164 }
165 if(updateWebView && !removedOldPackage && oldProviderName != null) {
166 // If the provider change is the result of adding or replacing a
167 // package that was not the previous provider then we must kill
168 // packages dependent on the old package ourselves. The framework
169 // only kills dependents of packages that are being removed.
170 try {
171 ActivityManagerNative.getDefault().killPackageDependents(
Gustav Sennton23875b22016-02-09 14:11:33 +0000172 oldProviderName, UserHandle.USER_ALL);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000173 } catch (RemoteException e) {
174 }
Gustav Sennton6ce92c92015-10-23 11:10:39 +0100175 }
176 return;
177 }
Ben Murdochdc00a842014-07-17 14:55:00 +0100178 }
179 }
180 };
181 IntentFilter filter = new IntentFilter();
Gustav Sennton3098cf22015-11-10 03:33:09 +0000182 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
183 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000184 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Ben Murdochdc00a842014-07-17 14:55:00 +0100185 filter.addDataScheme("package");
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000186 // Make sure we only receive intents for WebView packages from our config file.
187 for (WebViewProviderInfo provider : WebViewFactory.getWebViewPackages()) {
188 filter.addDataSchemeSpecificPart(provider.packageName, PatternMatcher.PATTERN_LITERAL);
189 }
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100190 getContext().registerReceiver(mWebViewUpdatedReceiver, filter);
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100191
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000192 IntentFilter userAddedFilter = new IntentFilter();
193 userAddedFilter.addAction(Intent.ACTION_USER_ADDED);
194 getContext().registerReceiver(mWebViewUpdatedReceiver, userAddedFilter);
195
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100196 publishBinderService("webviewupdate", new BinderService());
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100197 }
Ben Murdochdc00a842014-07-17 14:55:00 +0100198
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000199 private static boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
200 for (WebViewProviderInfo provider : providers) {
201 if (provider.isAvailableByDefault() && provider.isEnabled()
202 && provider.isValidProvider() && !provider.isFallbackPackage()) {
203 return true;
204 }
205 }
206 return false;
207 }
208
209 private static void enablePackageForUser(String packageName, boolean enable, int userId) {
210 try {
211 AppGlobals.getPackageManager().setApplicationEnabledSetting(
212 packageName,
213 enable ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT :
214 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0,
215 userId, null);
216 } catch (RemoteException e) {
217 Slog.w(TAG, "Tried to disable " + packageName + " for user " + userId + ": " + e);
218 }
219 }
220
221 /**
222 * Called when a new user has been added to update the state of its fallback package.
223 */
224 void handleNewUser(int userId) {
225 if (!isFallbackLogicEnabled()) return;
226
227 WebViewProviderInfo[] webviewProviders = WebViewFactory.getWebViewPackages();
228 WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
229 if (fallbackProvider == null) return;
230 boolean existsValidNonFallbackProvider =
231 existsValidNonFallbackProvider(webviewProviders);
232
233 enablePackageForUser(fallbackProvider.packageName, !existsValidNonFallbackProvider,
234 userId);
235 }
236
237 /**
238 * Handle the enabled-state of our fallback package, i.e. if there exists some non-fallback
239 * package that is valid (and available by default) then disable the fallback package,
240 * otherwise, enable the fallback package.
241 */
242 void updateFallbackState(final Context context, final Intent intent) {
243 if (!isFallbackLogicEnabled()) return;
244
245 WebViewProviderInfo[] webviewProviders = WebViewFactory.getWebViewPackages();
246
247 if (intent != null && (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)
248 || intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED))) {
249 // A package was changed / updated / downgraded, early out if it is not one of the
250 // webview packages that are available by default.
251 String changedPackage = null;
252 for (WebViewProviderInfo provider : webviewProviders) {
253 String webviewPackage = "package:" + provider.packageName;
254 if (webviewPackage.equals(intent.getDataString())) {
255 if (provider.isAvailableByDefault()) {
256 changedPackage = provider.packageName;
257 }
258 break;
259 }
260 }
261 if (changedPackage == null) return;
262 }
263
264 // If there exists a valid and enabled non-fallback package - disable the fallback
265 // package, otherwise, enable it.
266 WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
267 if (fallbackProvider == null) return;
268 boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders);
269
270 if (existsValidNonFallbackProvider
271 // During an OTA the primary user's WebView state might differ from other users', so
272 // ignore the state of that user during boot.
273 && (fallbackProvider.isEnabled() || intent == null)) {
274 // Uninstall and disable fallback package for all users.
275 context.getPackageManager().deletePackage(fallbackProvider.packageName,
276 new IPackageDeleteObserver.Stub() {
277 public void packageDeleted(String packageName, int returnCode) {
278 // Ignore returnCode since the deletion could fail, e.g. we might be trying
279 // to delete a non-updated system-package (and we should still disable the
280 // package)
281 UserManager userManager =
282 (UserManager)context.getSystemService(Context.USER_SERVICE);
283 // Disable the fallback package for all users.
284 for(UserInfo userInfo : userManager.getUsers()) {
285 enablePackageForUser(packageName, false, userInfo.id);
286 }
287 }
288 }, PackageManager.DELETE_SYSTEM_APP | PackageManager.DELETE_ALL_USERS);
289 } else if (!existsValidNonFallbackProvider
290 // During an OTA the primary user's WebView state might differ from other users', so
291 // ignore the state of that user during boot.
292 && (!fallbackProvider.isEnabled() || intent==null)) {
293 // Enable the fallback package for all users.
294 UserManager userManager =
295 (UserManager)context.getSystemService(Context.USER_SERVICE);
296 for(UserInfo userInfo : userManager.getUsers()) {
297 enablePackageForUser(fallbackProvider.packageName, true, userInfo.id);
298 }
299 }
300 }
301
302 private static boolean isFallbackLogicEnabled() {
303 // Note that this is enabled by default (i.e. if the setting hasn't been set).
304 return Settings.Global.getInt(AppGlobals.getInitialApplication().getContentResolver(),
305 Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, 1) == 1;
306 }
307
308 private static void enableFallbackLogic(boolean enable) {
309 Settings.Global.putInt(AppGlobals.getInitialApplication().getContentResolver(),
310 Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, enable ? 1 : 0);
311 }
312
313 /**
314 * Returns the only fallback provider, or null if there is none.
315 */
316 private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
317 for (WebViewProviderInfo provider : webviewPackages) {
318 if (provider.isFallbackPackage()) {
319 return provider;
320 }
321 }
322 return null;
323 }
324
325 private static boolean containsAvailableNonFallbackProvider(
326 WebViewProviderInfo[] webviewPackages) {
327 for (WebViewProviderInfo provider : webviewPackages) {
328 if (provider.isAvailableByDefault() && provider.isEnabled()
329 && provider.isValidProvider() && !provider.isFallbackPackage()) {
330 return true;
331 }
332 }
333 return false;
334 }
335
336 private static boolean isFallbackPackage(String packageName) {
337 if (packageName == null || !isFallbackLogicEnabled()) return false;
338
339 WebViewProviderInfo[] webviewPackages = WebViewFactory.getWebViewPackages();
340 WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages);
341 return (fallbackProvider != null
342 && packageName.equals(fallbackProvider.packageName));
343 }
344
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000345 /**
346 * Perform any WebView loading preparations that must happen at boot from the system server,
347 * after the package manager has started or after an update to the webview is installed.
348 * This must be called in the system server.
349 * Currently, this means spawning the child processes which will create the relro files.
350 */
351 public void prepareWebViewInSystemServer() {
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000352 updateFallbackState(getContext(), null);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000353 try {
354 synchronized(this) {
355 updateValidWebViewPackages();
356 mCurrentWebViewPackage = findPreferredWebViewPackage();
357 onWebViewProviderChanged(mCurrentWebViewPackage);
358 }
359 } catch (Throwable t) {
360 // Log and discard errors at this stage as we must not crash the system server.
361 Slog.e(TAG, "error preparing webview provider from system server", t);
Ben Murdochdc00a842014-07-17 14:55:00 +0100362 }
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000363 }
364
365
366 /**
367 * Change WebView provider and provider setting and kill packages using the old provider.
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000368 * Return the new provider (in case we are in the middle of creating relro files this new
369 * provider will not be in use directly, but will when the relros are done).
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000370 */
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000371 private String changeProviderAndSetting(String newProviderName) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000372 PackageInfo oldPackage = null;
373 PackageInfo newPackage = null;
374 synchronized(this) {
375 oldPackage = mCurrentWebViewPackage;
376 updateUserSetting(newProviderName);
377
378 try {
379 newPackage = findPreferredWebViewPackage();
380 if (oldPackage != null && newPackage.packageName.equals(oldPackage.packageName)) {
381 // If we don't perform the user change, revert the settings change.
382 updateUserSetting(newPackage.packageName);
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000383 return newPackage.packageName;
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000384 }
385 } catch (WebViewFactory.MissingWebViewPackageException e) {
386 Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView package "
387 + e);
388 // If we don't perform the user change but don't have an installed WebView package,
389 // we will have changed the setting and it will be used when a package is available.
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000390 return newProviderName;
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000391 }
392 onWebViewProviderChanged(newPackage);
393 }
394 // Kill apps using the old provider
395 try {
396 if (oldPackage != null) {
397 ActivityManagerNative.getDefault().killPackageDependents(
Gustav Sennton23875b22016-02-09 14:11:33 +0000398 oldPackage.packageName, UserHandle.USER_ALL);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000399 }
400 } catch (RemoteException e) {
401 }
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000402 return newPackage.packageName;
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000403 }
404
405 /**
406 * This is called when we change WebView provider, either when the current provider is updated
407 * or a new provider is chosen / takes precedence.
408 */
409 private void onWebViewProviderChanged(PackageInfo newPackage) {
410 synchronized(this) {
411 mAnyWebViewInstalled = true;
412 // If we have changed provider then the replacement of the old provider is
413 // irrelevant - we can only have chosen a new provider if its package is available.
414 mCurrentProviderBeingReplaced = false;
415 if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
416 mCurrentWebViewPackage = newPackage;
417 updateUserSetting(newPackage.packageName);
418
419 // The relro creations might 'finish' (not start at all) before
420 // WebViewFactory.onWebViewProviderChanged which means we might not know the number
421 // of started creations before they finish.
422 mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
423 mNumRelroCreationsFinished = 0;
424 mNumRelroCreationsStarted = WebViewFactory.onWebViewProviderChanged(newPackage);
425 // If the relro creations finish before we know the number of started creations we
426 // will have to do any cleanup/notifying here.
427 checkIfRelrosDoneLocked();
428 } else {
429 mWebViewPackageDirty = true;
430 }
431 }
432 }
433
434 /**
435 * Updates the currently valid WebView provider packages.
436 * Should be used when a provider has been installed or removed.
437 * @hide
438 * */
439 private void updateValidWebViewPackages() {
440 List<WebViewProviderInfo> webViewProviders =
441 new ArrayList<WebViewProviderInfo>(Arrays.asList(WebViewFactory.getWebViewPackages()));
442 Iterator<WebViewProviderInfo> it = webViewProviders.iterator();
443 // remove non-valid packages
444 while(it.hasNext()) {
445 WebViewProviderInfo current = it.next();
446 if (!current.isValidProvider())
447 it.remove();
448 }
449 synchronized(this) {
450 mCurrentValidWebViewPackages =
451 webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
452 }
453 }
454
455 private static String getUserChosenWebViewProvider() {
Gustav Sennton14c033c2016-02-11 12:51:27 +0000456 return Settings.Global.getString(AppGlobals.getInitialApplication().getContentResolver(),
457 Settings.Global.WEBVIEW_PROVIDER);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000458 }
459
460 private void updateUserSetting(String newProviderName) {
Gustav Sennton14c033c2016-02-11 12:51:27 +0000461 Settings.Global.putString(getContext().getContentResolver(),
462 Settings.Global.WEBVIEW_PROVIDER,
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000463 newProviderName == null ? "" : newProviderName);
464 }
465
466 /**
467 * Returns either the package info of the WebView provider determined in the following way:
468 * If the user has chosen a provider then use that if it is valid,
469 * otherwise use the first package in the webview priority list that is valid.
470 *
471 * @hide
472 */
473 private PackageInfo findPreferredWebViewPackage() {
474 WebViewProviderInfo[] providers = mCurrentValidWebViewPackages;
475
476 String userChosenProvider = getUserChosenWebViewProvider();
477
478 // If the user has chosen provider, use that
479 for (WebViewProviderInfo provider : providers) {
Gustav Sennton27f13de2016-01-05 20:22:02 +0000480 if (provider.packageName.equals(userChosenProvider) && provider.isEnabled()) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000481 return provider.getPackageInfo();
482 }
483 }
484
Gustav Sennton27f13de2016-01-05 20:22:02 +0000485 // User did not choose, or the choice failed; use the most stable provider that is
486 // enabled and available by default (not through user choice).
487 for (WebViewProviderInfo provider : providers) {
488 if (provider.isAvailableByDefault() && provider.isEnabled()) {
489 return provider.getPackageInfo();
490 }
491 }
492
493 // Could not find any enabled package either, use the most stable provider.
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000494 for (WebViewProviderInfo provider : providers) {
495 return provider.getPackageInfo();
496 }
Gustav Sennton27f13de2016-01-05 20:22:02 +0000497
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000498 mAnyWebViewInstalled = false;
499 throw new WebViewFactory.MissingWebViewPackageException(
500 "Could not find a loadable WebView package");
501 }
502
503 /**
504 * Returns whether WebView is ready and is not going to go through its preparation phase again
505 * directly.
506 */
507 private boolean webViewIsReadyLocked() {
508 return !mWebViewPackageDirty
509 && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
510 && !mCurrentProviderBeingReplaced
511 // The current package might be replaced though we haven't received an intent declaring
512 // this yet, the following flag makes anyone loading WebView to wait in this case.
513 && mAnyWebViewInstalled;
514 }
515
516 private void checkIfRelrosDoneLocked() {
517 if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
518 if (mWebViewPackageDirty) {
519 mWebViewPackageDirty = false;
520 // If we have changed provider since we started the relro creation we need to
521 // redo the whole process using the new package instead.
522 // Though, if the current provider package is being replaced we don't want to change
523 // provider here since we will perform the change either when the package is added
524 // again or when we switch to another provider (whichever comes first).
525 if (!mCurrentProviderBeingReplaced) {
526 PackageInfo newPackage = findPreferredWebViewPackage();
527 onWebViewProviderChanged(newPackage);
528 }
529 } else {
530 this.notifyAll();
531 }
532 }
Ben Murdochdc00a842014-07-17 14:55:00 +0100533 }
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100534
535 private class BinderService extends IWebViewUpdateService.Stub {
536
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000537 @Override
538 public void onShellCommand(FileDescriptor in, FileDescriptor out,
539 FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
540 (new WebViewUpdateServiceShellCommand(this)).exec(
541 this, in, out, err, args, resultReceiver);
542 }
543
544
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100545 /**
546 * The shared relro process calls this to notify us that it's done trying to create a relro
547 * file. This method gets called even if the relro creation has failed or the process
548 * crashed.
549 */
550 @Override // Binder call
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000551 public void notifyRelroCreationCompleted() {
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100552 // Verify that the caller is either the shared relro process (nominal case) or the
553 // system server (only in the case the relro process crashes and we get here via the
554 // crashHandler).
555 if (Binder.getCallingUid() != Process.SHARED_RELRO_UID &&
556 Binder.getCallingUid() != Process.SYSTEM_UID) {
557 return;
558 }
559
560 synchronized (WebViewUpdateService.this) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000561 mNumRelroCreationsFinished++;
562 checkIfRelrosDoneLocked();
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100563 }
564 }
565
566 /**
567 * WebViewFactory calls this to block WebView loading until the relro file is created.
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000568 * Returns the WebView provider for which we create relro files.
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100569 */
570 @Override // Binder call
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000571 public WebViewProviderResponse waitForAndGetProvider() {
Primiano Tuccie76e81a2014-07-29 16:38:33 +0100572 // The WebViewUpdateService depends on the prepareWebViewInSystemServer call, which
573 // happens later (during the PHASE_ACTIVITY_MANAGER_READY) in SystemServer.java. If
574 // another service there tries to bring up a WebView in the between, the wait below
575 // would deadlock without the check below.
576 if (Binder.getCallingPid() == Process.myPid()) {
577 throw new IllegalStateException("Cannot create a WebView from the SystemServer");
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100578 }
579
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000580 PackageInfo webViewPackage = null;
Primiano Tuccie76e81a2014-07-29 16:38:33 +0100581 final long NS_PER_MS = 1000000;
582 final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000583 boolean webViewReady = false;
584 int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100585 synchronized (WebViewUpdateService.this) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000586 webViewReady = WebViewUpdateService.this.webViewIsReadyLocked();
587 while (!webViewReady) {
Primiano Tuccie76e81a2014-07-29 16:38:33 +0100588 final long timeNowMs = System.nanoTime() / NS_PER_MS;
589 if (timeNowMs >= timeoutTimeMs) break;
590 try {
591 WebViewUpdateService.this.wait(timeoutTimeMs - timeNowMs);
592 } catch (InterruptedException e) {}
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000593 webViewReady = WebViewUpdateService.this.webViewIsReadyLocked();
594 }
595 // Make sure we return the provider that was used to create the relro file
596 webViewPackage = WebViewUpdateService.this.mCurrentWebViewPackage;
597 if (webViewReady) {
598 } else if (mCurrentProviderBeingReplaced) {
599 // It is important that we check this flag before the one representing WebView
600 // being installed, otherwise we might think there is no WebView though the
601 // current one is just being replaced.
602 webViewStatus = WebViewFactory.LIBLOAD_WEBVIEW_BEING_REPLACED;
603 } else if (!mAnyWebViewInstalled) {
604 webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
605 } else {
606 // Either the current relro creation isn't done yet, or the new relro creatioin
607 // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
608 webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100609 }
610 }
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000611 if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
612 return new WebViewProviderResponse(webViewPackage, webViewStatus);
613 }
614
615 /**
616 * This is called from DeveloperSettings when the user changes WebView provider.
617 */
618 @Override // Binder call
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000619 public String changeProviderAndSetting(String newProvider) {
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000620 if (getContext().checkCallingPermission(
621 android.Manifest.permission.WRITE_SECURE_SETTINGS)
622 != PackageManager.PERMISSION_GRANTED) {
623 String msg = "Permission Denial: changeProviderAndSetting() from pid="
624 + Binder.getCallingPid()
625 + ", uid=" + Binder.getCallingUid()
626 + " requires " + android.Manifest.permission.WRITE_SECURE_SETTINGS;
627 Slog.w(TAG, msg);
628 throw new SecurityException(msg);
629 }
630
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000631 return WebViewUpdateService.this.changeProviderAndSetting(newProvider);
Gustav Sennton6258dcd2015-10-30 19:25:37 +0000632 }
633
634 @Override // Binder call
635 public WebViewProviderInfo[] getValidWebViewPackages() {
636 synchronized(WebViewUpdateService.this) {
637 return mCurrentValidWebViewPackages;
638 }
639 }
640
641 @Override // Binder call
642 public String getCurrentWebViewPackageName() {
643 synchronized(WebViewUpdateService.this) {
644 if (WebViewUpdateService.this.mCurrentWebViewPackage == null)
645 return null;
646 return WebViewUpdateService.this.mCurrentWebViewPackage.packageName;
647 }
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100648 }
Gustav Senntonc83e3fa2016-02-18 12:19:13 +0000649
650 @Override // Binder call
651 public boolean isFallbackPackage(String packageName) {
652 return WebViewUpdateService.isFallbackPackage(packageName);
653 }
654
655 @Override // Binder call
656 public void enableFallbackLogic(boolean enable) {
657 if (getContext().checkCallingPermission(
658 android.Manifest.permission.WRITE_SECURE_SETTINGS)
659 != PackageManager.PERMISSION_GRANTED) {
660 String msg = "Permission Denial: enableFallbackLogic() from pid="
661 + Binder.getCallingPid()
662 + ", uid=" + Binder.getCallingUid()
663 + " requires " + android.Manifest.permission.WRITE_SECURE_SETTINGS;
664 Slog.w(TAG, msg);
665 throw new SecurityException(msg);
666 }
667
668 WebViewUpdateService.enableFallbackLogic(enable);
669 }
Torne (Richard Coles)4dbeb352014-07-29 19:14:24 +0100670 }
Torne (Richard Coles)08cfaf62014-05-08 16:07:05 +0100671}