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.annotation.Nullable; 19 import android.content.Context; 20 import android.content.pm.PackageInfo; 21 import android.content.pm.PackageManager.NameNotFoundException; 22 import android.content.pm.Signature; 23 import android.os.AsyncTask; 24 import android.os.Trace; 25 import android.os.UserHandle; 26 import android.util.Slog; 27 import android.webkit.UserPackage; 28 import android.webkit.WebViewFactory; 29 import android.webkit.WebViewProviderInfo; 30 import android.webkit.WebViewProviderResponse; 31 32 import java.io.PrintWriter; 33 import java.util.ArrayList; 34 import java.util.List; 35 36 /** 37 * Implementation of the WebViewUpdateService. 38 * This class doesn't depend on the android system like the actual Service does and can be used 39 * directly by tests (as long as they implement a SystemInterface). 40 * 41 * This class keeps track of and prepares the current WebView implementation, and needs to keep 42 * track of a couple of different things such as what package is used as WebView implementation. 43 * 44 * The package-visible methods in this class are accessed from WebViewUpdateService either on the UI 45 * thread or on one of multiple Binder threads. The WebView preparation code shares state between 46 * threads meaning that code that chooses a new WebView implementation or checks which 47 * implementation is being used needs to hold a lock. 48 * 49 * The WebViewUpdateService can be accessed in a couple of different ways. 50 * 1. It is started from the SystemServer at boot - at that point we just initiate some state such 51 * as the WebView preparation class. 52 * 2. The SystemServer calls WebViewUpdateService.prepareWebViewInSystemServer. This happens at boot 53 * and the WebViewUpdateService should not have been accessed before this call. In this call we 54 * choose WebView implementation for the first time. 55 * 3. The update service listens for Intents related to package installs and removals. These intents 56 * are received and processed on the UI thread. Each intent can result in changing WebView 57 * implementation. 58 * 4. The update service can be reached through Binder calls which are handled on specific binder 59 * threads. These calls can be made from any process. Generally they are used for changing WebView 60 * implementation (from Settings), getting information about the current WebView implementation (for 61 * loading WebView into an app process), or notifying the service about Relro creation being 62 * completed. 63 * 64 * @hide 65 */ 66 class WebViewUpdateServiceImpl { 67 private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName(); 68 69 private static class WebViewPackageMissingException extends Exception { WebViewPackageMissingException(String message)70 WebViewPackageMissingException(String message) { 71 super(message); 72 } 73 WebViewPackageMissingException(Exception e)74 WebViewPackageMissingException(Exception e) { 75 super(e); 76 } 77 } 78 79 private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000. 80 private static final long NS_PER_MS = 1000000; 81 82 private static final int VALIDITY_OK = 0; 83 private static final int VALIDITY_INCORRECT_SDK_VERSION = 1; 84 private static final int VALIDITY_INCORRECT_VERSION_CODE = 2; 85 private static final int VALIDITY_INCORRECT_SIGNATURE = 3; 86 private static final int VALIDITY_NO_LIBRARY_FLAG = 4; 87 88 private static final int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE; 89 private static final int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE; 90 91 private final SystemInterface mSystemInterface; 92 private final Context mContext; 93 94 private long mMinimumVersionCode = -1; 95 96 // Keeps track of the number of running relro creations 97 private int mNumRelroCreationsStarted = 0; 98 private int mNumRelroCreationsFinished = 0; 99 // Implies that we need to rerun relro creation because we are using an out-of-date package 100 private boolean mWebViewPackageDirty = false; 101 private boolean mAnyWebViewInstalled = false; 102 103 private static final int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE; 104 105 // The WebView package currently in use (or the one we are preparing). 106 private PackageInfo mCurrentWebViewPackage = null; 107 108 private final Object mLock = new Object(); 109 WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface)110 WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) { 111 mContext = context; 112 mSystemInterface = systemInterface; 113 } 114 packageStateChanged(String packageName, int changedState, int userId)115 void packageStateChanged(String packageName, int changedState, int userId) { 116 // We don't early out here in different cases where we could potentially early-out (e.g. if 117 // we receive PACKAGE_CHANGED for another user than the system user) since that would 118 // complicate this logic further and open up for more edge cases. 119 for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { 120 String webviewPackage = provider.packageName; 121 122 if (webviewPackage.equals(packageName)) { 123 boolean updateWebView = false; 124 boolean removedOrChangedOldPackage = false; 125 String oldProviderName = null; 126 PackageInfo newPackage = null; 127 synchronized (mLock) { 128 try { 129 newPackage = findPreferredWebViewPackage(); 130 if (mCurrentWebViewPackage != null) { 131 oldProviderName = mCurrentWebViewPackage.packageName; 132 } 133 // Only trigger update actions if the updated package is the one 134 // that will be used, or the one that was in use before the 135 // update, or if we haven't seen a valid WebView package before. 136 updateWebView = 137 provider.packageName.equals(newPackage.packageName) 138 || provider.packageName.equals(oldProviderName) 139 || mCurrentWebViewPackage == null; 140 // We removed the old package if we received an intent to remove 141 // or replace the old package. 142 removedOrChangedOldPackage = 143 provider.packageName.equals(oldProviderName); 144 if (updateWebView) { 145 onWebViewProviderChanged(newPackage); 146 } 147 } catch (WebViewPackageMissingException e) { 148 mCurrentWebViewPackage = null; 149 Slog.e(TAG, "Could not find valid WebView package to create relro with " 150 + e); 151 } 152 } 153 if (updateWebView && !removedOrChangedOldPackage 154 && oldProviderName != null) { 155 // If the provider change is the result of adding or replacing a 156 // package that was not the previous provider then we must kill 157 // packages dependent on the old package ourselves. The framework 158 // only kills dependents of packages that are being removed. 159 mSystemInterface.killPackageDependents(oldProviderName); 160 } 161 return; 162 } 163 } 164 } 165 prepareWebViewInSystemServer()166 void prepareWebViewInSystemServer() { 167 mSystemInterface.notifyZygote(isMultiProcessEnabled()); 168 try { 169 synchronized (mLock) { 170 mCurrentWebViewPackage = findPreferredWebViewPackage(); 171 String userSetting = mSystemInterface.getUserChosenWebViewProvider(mContext); 172 if (userSetting != null 173 && !userSetting.equals(mCurrentWebViewPackage.packageName)) { 174 // Don't persist the user-chosen setting across boots if the package being 175 // chosen is not used (could be disabled or uninstalled) so that the user won't 176 // be surprised by the device switching to using a certain webview package, 177 // that was uninstalled/disabled a long time ago, if it is installed/enabled 178 // again. 179 mSystemInterface.updateUserSetting(mContext, 180 mCurrentWebViewPackage.packageName); 181 } 182 onWebViewProviderChanged(mCurrentWebViewPackage); 183 } 184 } catch (Throwable t) { 185 // Log and discard errors at this stage as we must not crash the system server. 186 Slog.e(TAG, "error preparing webview provider from system server", t); 187 } 188 189 if (getCurrentWebViewPackage() == null) { 190 // We didn't find a valid WebView implementation. Try explicitly re-enabling the 191 // fallback package for all users in case it was disabled, even if we already did the 192 // one-time migration before. If this actually changes the state, we will see the 193 // PackageManager broadcast shortly and try again. 194 WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); 195 WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders); 196 if (fallbackProvider != null) { 197 Slog.w(TAG, "No valid provider, trying to enable " + fallbackProvider.packageName); 198 mSystemInterface.enablePackageForAllUsers(mContext, fallbackProvider.packageName, 199 true); 200 } else { 201 Slog.e(TAG, "No valid provider and no fallback available."); 202 } 203 } 204 } 205 startZygoteWhenReady()206 private void startZygoteWhenReady() { 207 // Wait on a background thread for RELRO creation to be done. We ignore the return value 208 // because even if RELRO creation failed we still want to start the zygote. 209 waitForAndGetProvider(); 210 mSystemInterface.ensureZygoteStarted(); 211 } 212 handleNewUser(int userId)213 void handleNewUser(int userId) { 214 // The system user is always started at boot, and by that point we have already run one 215 // round of the package-changing logic (through prepareWebViewInSystemServer()), so early 216 // out here. 217 if (userId == UserHandle.USER_SYSTEM) return; 218 handleUserChange(); 219 } 220 handleUserRemoved(int userId)221 void handleUserRemoved(int userId) { 222 handleUserChange(); 223 } 224 225 /** 226 * Called when a user was added or removed to ensure WebView preparation is triggered. 227 * This has to be done since the WebView package we use depends on the enabled-state 228 * of packages for all users (so adding or removing a user might cause us to change package). 229 */ handleUserChange()230 private void handleUserChange() { 231 // Potentially trigger package-changing logic. 232 updateCurrentWebViewPackage(null); 233 } 234 notifyRelroCreationCompleted()235 void notifyRelroCreationCompleted() { 236 synchronized (mLock) { 237 mNumRelroCreationsFinished++; 238 checkIfRelrosDoneLocked(); 239 } 240 } 241 waitForAndGetProvider()242 WebViewProviderResponse waitForAndGetProvider() { 243 PackageInfo webViewPackage = null; 244 final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS; 245 boolean webViewReady = false; 246 int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS; 247 synchronized (mLock) { 248 webViewReady = webViewIsReadyLocked(); 249 while (!webViewReady) { 250 final long timeNowMs = System.nanoTime() / NS_PER_MS; 251 if (timeNowMs >= timeoutTimeMs) break; 252 try { 253 mLock.wait(timeoutTimeMs - timeNowMs); 254 } catch (InterruptedException e) { 255 // ignore 256 } 257 webViewReady = webViewIsReadyLocked(); 258 } 259 // Make sure we return the provider that was used to create the relro file 260 webViewPackage = mCurrentWebViewPackage; 261 if (webViewReady) { 262 // success 263 } else if (!mAnyWebViewInstalled) { 264 webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES; 265 } else { 266 // Either the current relro creation isn't done yet, or the new relro creatioin 267 // hasn't kicked off yet (the last relro creation used an out-of-date WebView). 268 webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO; 269 String timeoutError = "Timed out waiting for relro creation, relros started " 270 + mNumRelroCreationsStarted 271 + " relros finished " + mNumRelroCreationsFinished 272 + " package dirty? " + mWebViewPackageDirty; 273 Slog.e(TAG, timeoutError); 274 Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, timeoutError); 275 } 276 } 277 if (!webViewReady) Slog.w(TAG, "creating relro file timed out"); 278 return new WebViewProviderResponse(webViewPackage, webViewStatus); 279 } 280 281 /** 282 * Change WebView provider and provider setting and kill packages using the old provider. 283 * Return the new provider (in case we are in the middle of creating relro files, or 284 * replacing that provider it will not be in use directly, but will be used when the relros 285 * or the replacement are done). 286 */ changeProviderAndSetting(String newProviderName)287 String changeProviderAndSetting(String newProviderName) { 288 PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName); 289 if (newPackage == null) return ""; 290 return newPackage.packageName; 291 } 292 293 /** 294 * Update the current WebView package. 295 * @param newProviderName the package to switch to, null if no package has been explicitly 296 * chosen. 297 */ updateCurrentWebViewPackage(@ullable String newProviderName)298 private PackageInfo updateCurrentWebViewPackage(@Nullable String newProviderName) { 299 PackageInfo oldPackage = null; 300 PackageInfo newPackage = null; 301 boolean providerChanged = false; 302 synchronized (mLock) { 303 oldPackage = mCurrentWebViewPackage; 304 305 if (newProviderName != null) { 306 mSystemInterface.updateUserSetting(mContext, newProviderName); 307 } 308 309 try { 310 newPackage = findPreferredWebViewPackage(); 311 providerChanged = (oldPackage == null) 312 || !newPackage.packageName.equals(oldPackage.packageName); 313 } catch (WebViewPackageMissingException e) { 314 // If updated the Setting but don't have an installed WebView package, the 315 // Setting will be used when a package is available. 316 mCurrentWebViewPackage = null; 317 Slog.e(TAG, "Couldn't find WebView package to use " + e); 318 return null; 319 } 320 // Perform the provider change if we chose a new provider 321 if (providerChanged) { 322 onWebViewProviderChanged(newPackage); 323 } 324 } 325 // Kill apps using the old provider only if we changed provider 326 if (providerChanged && oldPackage != null) { 327 mSystemInterface.killPackageDependents(oldPackage.packageName); 328 } 329 // Return the new provider, this is not necessarily the one we were asked to switch to, 330 // but the persistent setting will now be pointing to the provider we were asked to 331 // switch to anyway. 332 return newPackage; 333 } 334 335 /** 336 * This is called when we change WebView provider, either when the current provider is 337 * updated or a new provider is chosen / takes precedence. 338 */ onWebViewProviderChanged(PackageInfo newPackage)339 private void onWebViewProviderChanged(PackageInfo newPackage) { 340 synchronized (mLock) { 341 mAnyWebViewInstalled = true; 342 if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { 343 mCurrentWebViewPackage = newPackage; 344 345 // The relro creations might 'finish' (not start at all) before 346 // WebViewFactory.onWebViewProviderChanged which means we might not know the 347 // number of started creations before they finish. 348 mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN; 349 mNumRelroCreationsFinished = 0; 350 mNumRelroCreationsStarted = 351 mSystemInterface.onWebViewProviderChanged(newPackage); 352 // If the relro creations finish before we know the number of started creations 353 // we will have to do any cleanup/notifying here. 354 checkIfRelrosDoneLocked(); 355 } else { 356 mWebViewPackageDirty = true; 357 } 358 } 359 360 // Once we've notified the system that the provider has changed and started RELRO creation, 361 // try to restart the zygote so that it will be ready when apps use it. 362 if (isMultiProcessEnabled()) { 363 AsyncTask.THREAD_POOL_EXECUTOR.execute(this::startZygoteWhenReady); 364 } 365 } 366 367 /** 368 * Fetch only the currently valid WebView packages. 369 **/ getValidWebViewPackages()370 WebViewProviderInfo[] getValidWebViewPackages() { 371 ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos(); 372 WebViewProviderInfo[] providers = 373 new WebViewProviderInfo[providersAndPackageInfos.length]; 374 for (int n = 0; n < providersAndPackageInfos.length; n++) { 375 providers[n] = providersAndPackageInfos[n].provider; 376 } 377 return providers; 378 } 379 380 private static class ProviderAndPackageInfo { 381 public final WebViewProviderInfo provider; 382 public final PackageInfo packageInfo; 383 ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo)384 ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) { 385 this.provider = provider; 386 this.packageInfo = packageInfo; 387 } 388 } 389 getValidWebViewPackagesAndInfos()390 private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() { 391 WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages(); 392 List<ProviderAndPackageInfo> providers = new ArrayList<>(); 393 for (int n = 0; n < allProviders.length; n++) { 394 try { 395 PackageInfo packageInfo = 396 mSystemInterface.getPackageInfoForProvider(allProviders[n]); 397 if (validityResult(allProviders[n], packageInfo) == VALIDITY_OK) { 398 providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo)); 399 } 400 } catch (NameNotFoundException e) { 401 // Don't add non-existent packages 402 } 403 } 404 return providers.toArray(new ProviderAndPackageInfo[providers.size()]); 405 } 406 407 /** 408 * Returns either the package info of the WebView provider determined in the following way: 409 * If the user has chosen a provider then use that if it is valid, 410 * otherwise use the first package in the webview priority list that is valid. 411 * 412 */ findPreferredWebViewPackage()413 private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException { 414 ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos(); 415 416 String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext); 417 418 // If the user has chosen provider, use that (if it's installed and enabled for all 419 // users). 420 for (ProviderAndPackageInfo providerAndPackage : providers) { 421 if (providerAndPackage.provider.packageName.equals(userChosenProvider)) { 422 // userPackages can contain null objects. 423 List<UserPackage> userPackages = 424 mSystemInterface.getPackageInfoForProviderAllUsers(mContext, 425 providerAndPackage.provider); 426 if (isInstalledAndEnabledForAllUsers(userPackages)) { 427 return providerAndPackage.packageInfo; 428 } 429 } 430 } 431 432 // User did not choose, or the choice failed; use the most stable provider that is 433 // installed and enabled for all users, and available by default (not through 434 // user choice). 435 for (ProviderAndPackageInfo providerAndPackage : providers) { 436 if (providerAndPackage.provider.availableByDefault) { 437 // userPackages can contain null objects. 438 List<UserPackage> userPackages = 439 mSystemInterface.getPackageInfoForProviderAllUsers(mContext, 440 providerAndPackage.provider); 441 if (isInstalledAndEnabledForAllUsers(userPackages)) { 442 return providerAndPackage.packageInfo; 443 } 444 } 445 } 446 447 // This should never happen during normal operation (only with modified system images). 448 mAnyWebViewInstalled = false; 449 throw new WebViewPackageMissingException("Could not find a loadable WebView package"); 450 } 451 452 /** 453 * Return true iff {@param packageInfos} point to only installed and enabled packages. 454 * The given packages {@param packageInfos} should all be pointing to the same package, but each 455 * PackageInfo representing a different user's package. 456 */ isInstalledAndEnabledForAllUsers( List<UserPackage> userPackages)457 private static boolean isInstalledAndEnabledForAllUsers( 458 List<UserPackage> userPackages) { 459 for (UserPackage userPackage : userPackages) { 460 if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) { 461 return false; 462 } 463 } 464 return true; 465 } 466 getWebViewPackages()467 WebViewProviderInfo[] getWebViewPackages() { 468 return mSystemInterface.getWebViewPackages(); 469 } 470 getCurrentWebViewPackage()471 PackageInfo getCurrentWebViewPackage() { 472 synchronized (mLock) { 473 return mCurrentWebViewPackage; 474 } 475 } 476 477 /** 478 * Returns whether WebView is ready and is not going to go through its preparation phase 479 * again directly. 480 */ webViewIsReadyLocked()481 private boolean webViewIsReadyLocked() { 482 return !mWebViewPackageDirty 483 && (mNumRelroCreationsStarted == mNumRelroCreationsFinished) 484 // The current package might be replaced though we haven't received an intent 485 // declaring this yet, the following flag makes anyone loading WebView to wait in 486 // this case. 487 && mAnyWebViewInstalled; 488 } 489 checkIfRelrosDoneLocked()490 private void checkIfRelrosDoneLocked() { 491 if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { 492 if (mWebViewPackageDirty) { 493 mWebViewPackageDirty = false; 494 // If we have changed provider since we started the relro creation we need to 495 // redo the whole process using the new package instead. 496 try { 497 PackageInfo newPackage = findPreferredWebViewPackage(); 498 onWebViewProviderChanged(newPackage); 499 } catch (WebViewPackageMissingException e) { 500 mCurrentWebViewPackage = null; 501 // If we can't find any valid WebView package we are now in a state where 502 // mAnyWebViewInstalled is false, so loading WebView will be blocked and we 503 // should simply wait until we receive an intent declaring a new package was 504 // installed. 505 } 506 } else { 507 mLock.notifyAll(); 508 } 509 } 510 } 511 validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo)512 private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) { 513 // Ensure the provider targets this framework release (or a later one). 514 if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) { 515 return VALIDITY_INCORRECT_SDK_VERSION; 516 } 517 if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode()) 518 && !mSystemInterface.systemIsDebuggable()) { 519 // Webview providers may be downgraded arbitrarily low, prevent that by enforcing 520 // minimum version code. This check is only enforced for user builds. 521 return VALIDITY_INCORRECT_VERSION_CODE; 522 } 523 if (!providerHasValidSignature(configInfo, packageInfo, mSystemInterface)) { 524 return VALIDITY_INCORRECT_SIGNATURE; 525 } 526 if (WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) == null) { 527 return VALIDITY_NO_LIBRARY_FLAG; 528 } 529 return VALIDITY_OK; 530 } 531 532 /** 533 * Both versionCodes should be from a WebView provider package implemented by Chromium. 534 * VersionCodes from other kinds of packages won't make any sense in this method. 535 * 536 * An introduction to Chromium versionCode scheme: 537 * "BBBBPPPXX" 538 * BBBB: 4 digit branch number. It monotonically increases over time. 539 * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits 540 * may change their meaning in the future. 541 * XX: Digits to differentiate different APK builds of the same source version. 542 * 543 * This method takes the "BBBB" of versionCodes and compare them. 544 * 545 * https://www.chromium.org/developers/version-numbers describes general Chromium versioning; 546 * https://source.chromium.org/chromium/chromium/src/+/master:build/util/android_chrome_version.py 547 * is the canonical source for how Chromium versionCodes are calculated. 548 * 549 * @return true if versionCode1 is higher than or equal to versionCode2. 550 */ versionCodeGE(long versionCode1, long versionCode2)551 private static boolean versionCodeGE(long versionCode1, long versionCode2) { 552 long v1 = versionCode1 / 100000; 553 long v2 = versionCode2 / 100000; 554 555 return v1 >= v2; 556 } 557 558 /** 559 * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode 560 * of all available-by-default WebView provider packages. If there is no such WebView provider 561 * package on the system, then return -1, which means all positive versionCode WebView packages 562 * are accepted. 563 * 564 * Note that this is a private method that handles a variable (mMinimumVersionCode) which is 565 * shared between threads. Furthermore, this method does not hold mLock meaning that we must 566 * take extra care to ensure this method is thread-safe. 567 */ getMinimumVersionCode()568 private long getMinimumVersionCode() { 569 if (mMinimumVersionCode > 0) { 570 return mMinimumVersionCode; 571 } 572 573 long minimumVersionCode = -1; 574 for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { 575 if (provider.availableByDefault) { 576 try { 577 long versionCode = 578 mSystemInterface.getFactoryPackageVersion(provider.packageName); 579 if (minimumVersionCode < 0 || versionCode < minimumVersionCode) { 580 minimumVersionCode = versionCode; 581 } 582 } catch (NameNotFoundException e) { 583 // Safe to ignore. 584 } 585 } 586 } 587 588 mMinimumVersionCode = minimumVersionCode; 589 return mMinimumVersionCode; 590 } 591 providerHasValidSignature(WebViewProviderInfo provider, PackageInfo packageInfo, SystemInterface systemInterface)592 private static boolean providerHasValidSignature(WebViewProviderInfo provider, 593 PackageInfo packageInfo, SystemInterface systemInterface) { 594 // Skip checking signatures on debuggable builds, for development purposes. 595 if (systemInterface.systemIsDebuggable()) return true; 596 597 // Allow system apps to be valid providers regardless of signature. 598 if (packageInfo.applicationInfo.isSystemApp()) return true; 599 600 // We don't support packages with multiple signatures. 601 if (packageInfo.signatures.length != 1) return false; 602 603 // If any of the declared signatures match the package signature, it's valid. 604 for (Signature signature : provider.signatures) { 605 if (signature.equals(packageInfo.signatures[0])) return true; 606 } 607 608 return false; 609 } 610 611 /** 612 * Returns the only fallback provider in the set of given packages, or null if there is none. 613 */ getFallbackProvider(WebViewProviderInfo[] webviewPackages)614 private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) { 615 for (WebViewProviderInfo provider : webviewPackages) { 616 if (provider.isFallback) { 617 return provider; 618 } 619 } 620 return null; 621 } 622 isMultiProcessEnabled()623 boolean isMultiProcessEnabled() { 624 int settingValue = mSystemInterface.getMultiProcessSetting(mContext); 625 if (mSystemInterface.isMultiProcessDefaultEnabled()) { 626 // Multiprocess should be enabled unless the user has turned it off manually. 627 return settingValue > MULTIPROCESS_SETTING_OFF_VALUE; 628 } else { 629 // Multiprocess should not be enabled, unless the user has turned it on manually. 630 return settingValue >= MULTIPROCESS_SETTING_ON_VALUE; 631 } 632 } 633 enableMultiProcess(boolean enable)634 void enableMultiProcess(boolean enable) { 635 PackageInfo current = getCurrentWebViewPackage(); 636 mSystemInterface.setMultiProcessSetting(mContext, 637 enable ? MULTIPROCESS_SETTING_ON_VALUE : MULTIPROCESS_SETTING_OFF_VALUE); 638 mSystemInterface.notifyZygote(enable); 639 if (current != null) { 640 mSystemInterface.killPackageDependents(current.packageName); 641 } 642 } 643 644 /** 645 * Dump the state of this Service. 646 */ dumpState(PrintWriter pw)647 void dumpState(PrintWriter pw) { 648 pw.println("Current WebView Update Service state"); 649 pw.println(String.format(" Multiprocess enabled: %b", isMultiProcessEnabled())); 650 synchronized (mLock) { 651 if (mCurrentWebViewPackage == null) { 652 pw.println(" Current WebView package is null"); 653 } else { 654 pw.println(String.format(" Current WebView package (name, version): (%s, %s)", 655 mCurrentWebViewPackage.packageName, 656 mCurrentWebViewPackage.versionName)); 657 } 658 pw.println(String.format(" Minimum targetSdkVersion: %d", 659 UserPackage.MINIMUM_SUPPORTED_SDK)); 660 pw.println(String.format(" Minimum WebView version code: %d", 661 mMinimumVersionCode)); 662 pw.println(String.format(" Number of relros started: %d", 663 mNumRelroCreationsStarted)); 664 pw.println(String.format(" Number of relros finished: %d", 665 mNumRelroCreationsFinished)); 666 pw.println(String.format(" WebView package dirty: %b", mWebViewPackageDirty)); 667 pw.println(String.format(" Any WebView package installed: %b", 668 mAnyWebViewInstalled)); 669 670 try { 671 PackageInfo preferredWebViewPackage = findPreferredWebViewPackage(); 672 pw.println(String.format( 673 " Preferred WebView package (name, version): (%s, %s)", 674 preferredWebViewPackage.packageName, 675 preferredWebViewPackage.versionName)); 676 } catch (WebViewPackageMissingException e) { 677 pw.println(String.format(" Preferred WebView package: none")); 678 } 679 680 dumpAllPackageInformationLocked(pw); 681 } 682 } 683 dumpAllPackageInformationLocked(PrintWriter pw)684 private void dumpAllPackageInformationLocked(PrintWriter pw) { 685 WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages(); 686 pw.println(" WebView packages:"); 687 for (WebViewProviderInfo provider : allProviders) { 688 List<UserPackage> userPackages = 689 mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider); 690 PackageInfo systemUserPackageInfo = 691 userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo(); 692 if (systemUserPackageInfo == null) { 693 pw.println(String.format(" %s is NOT installed.", provider.packageName)); 694 continue; 695 } 696 697 int validity = validityResult(provider, systemUserPackageInfo); 698 String packageDetails = String.format( 699 "versionName: %s, versionCode: %d, targetSdkVersion: %d", 700 systemUserPackageInfo.versionName, 701 systemUserPackageInfo.getLongVersionCode(), 702 systemUserPackageInfo.applicationInfo.targetSdkVersion); 703 if (validity == VALIDITY_OK) { 704 boolean installedForAllUsers = isInstalledAndEnabledForAllUsers( 705 mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider)); 706 pw.println(String.format( 707 " Valid package %s (%s) is %s installed/enabled for all users", 708 systemUserPackageInfo.packageName, 709 packageDetails, 710 installedForAllUsers ? "" : "NOT")); 711 } else { 712 pw.println(String.format(" Invalid package %s (%s), reason: %s", 713 systemUserPackageInfo.packageName, 714 packageDetails, 715 getInvalidityReason(validity))); 716 } 717 } 718 } 719 getInvalidityReason(int invalidityReason)720 private static String getInvalidityReason(int invalidityReason) { 721 switch (invalidityReason) { 722 case VALIDITY_INCORRECT_SDK_VERSION: 723 return "SDK version too low"; 724 case VALIDITY_INCORRECT_VERSION_CODE: 725 return "Version code too low"; 726 case VALIDITY_INCORRECT_SIGNATURE: 727 return "Incorrect signature"; 728 case VALIDITY_NO_LIBRARY_FLAG: 729 return "No WebView-library manifest flag"; 730 default: 731 return "Unexcepted validity-reason"; 732 } 733 } 734 } 735