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