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