WebViewUpdateServiceTest.java revision 364e16029017a4e16ed727a5e501f70363d04e5a
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 17package com.android.server.webkit; 18 19import static org.junit.Assert.assertEquals; 20import static org.junit.Assert.assertFalse; 21 22import android.content.Context; 23import android.content.pm.ApplicationInfo; 24import android.content.pm.PackageInfo; 25import android.content.pm.Signature; 26import android.os.Bundle; 27import android.support.test.InstrumentationRegistry; 28import android.support.test.runner.AndroidJUnit4; 29import android.test.suitebuilder.annotation.MediumTest; 30import android.util.Base64; 31import android.webkit.WebViewFactory; 32import android.webkit.WebViewProviderInfo; 33import android.webkit.WebViewProviderResponse; 34 35import org.hamcrest.Description; 36 37import org.junit.Test; 38import org.junit.runner.RunWith; 39 40import org.mockito.Mockito; 41import org.mockito.Matchers; 42import org.mockito.ArgumentMatcher; 43 44import java.util.concurrent.CountDownLatch; 45 46 47/** 48 * Tests for WebViewUpdateService 49 runtest --path frameworks/base/services/tests/servicestests/ \ 50 -c com.android.server.webkit.WebViewUpdateServiceTest 51 */ 52// Use MediumTest instead of SmallTest as the implementation of WebViewUpdateService 53// is intended to work on several threads and uses at least one sleep/wait-statement. 54@RunWith(AndroidJUnit4.class) 55@MediumTest 56public class WebViewUpdateServiceTest { 57 private final static String TAG = WebViewUpdateServiceTest.class.getSimpleName(); 58 59 private WebViewUpdateServiceImpl mWebViewUpdateServiceImpl; 60 private TestSystemImpl mTestSystemImpl; 61 62 private static final String WEBVIEW_LIBRARY_FLAG = "com.android.webview.WebViewLibrary"; 63 64 /** 65 * Creates a new instance. 66 */ 67 public WebViewUpdateServiceTest() { 68 } 69 70 private void setupWithPackages(WebViewProviderInfo[] packages) { 71 setupWithPackages(packages, true); 72 } 73 74 private void setupWithPackages(WebViewProviderInfo[] packages, 75 boolean fallbackLogicEnabled) { 76 setupWithPackages(packages, fallbackLogicEnabled, 1); 77 } 78 79 private void setupWithPackages(WebViewProviderInfo[] packages, 80 boolean fallbackLogicEnabled, int numRelros) { 81 setupWithPackages(packages, fallbackLogicEnabled, numRelros, 82 true /* isDebuggable == true -> don't check package signatures */); 83 } 84 85 private void setupWithPackages(WebViewProviderInfo[] packages, 86 boolean fallbackLogicEnabled, int numRelros, boolean isDebuggable) { 87 TestSystemImpl testing = new TestSystemImpl(packages, fallbackLogicEnabled, numRelros, 88 isDebuggable); 89 mTestSystemImpl = Mockito.spy(testing); 90 mWebViewUpdateServiceImpl = 91 new WebViewUpdateServiceImpl(null /*Context*/, mTestSystemImpl); 92 } 93 94 private void setEnabledAndValidPackageInfos(WebViewProviderInfo[] providers) { 95 // Set package infos for the primary user (user 0). 96 setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, providers); 97 } 98 99 private void setEnabledAndValidPackageInfosForUser(int userId, 100 WebViewProviderInfo[] providers) { 101 for(WebViewProviderInfo wpi : providers) { 102 mTestSystemImpl.setPackageInfoForUser(userId, createPackageInfo(wpi.packageName, 103 true /* enabled */, true /* valid */, true /* installed */)); 104 } 105 } 106 107 private void checkCertainPackageUsedAfterWebViewBootPreparation(String expectedProviderName, 108 WebViewProviderInfo[] webviewPackages) { 109 checkCertainPackageUsedAfterWebViewBootPreparation( 110 expectedProviderName, webviewPackages, 1); 111 } 112 113 private void checkCertainPackageUsedAfterWebViewBootPreparation(String expectedProviderName, 114 WebViewProviderInfo[] webviewPackages, int numRelros) { 115 setupWithPackages(webviewPackages, true, numRelros); 116 // Add (enabled and valid) package infos for each provider 117 setEnabledAndValidPackageInfos(webviewPackages); 118 119 runWebViewBootPreparationOnMainSync(); 120 121 Mockito.verify(mTestSystemImpl).onWebViewProviderChanged( 122 Mockito.argThat(new IsPackageInfoWithName(expectedProviderName))); 123 124 for (int n = 0; n < numRelros; n++) { 125 mWebViewUpdateServiceImpl.notifyRelroCreationCompleted(); 126 } 127 128 WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider(); 129 assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status); 130 assertEquals(expectedProviderName, response.packageInfo.packageName); 131 } 132 133 // For matching the package name of a PackageInfo 134 private class IsPackageInfoWithName extends ArgumentMatcher<PackageInfo> { 135 private final String mPackageName; 136 137 IsPackageInfoWithName(String name) { 138 mPackageName = name; 139 } 140 141 @Override 142 public boolean matches(Object p) { 143 return ((PackageInfo) p).packageName.equals(mPackageName); 144 } 145 146 // Provide a more useful description in case of mismatch 147 @Override 148 public void describeTo (Description description) { 149 description.appendText(String.format("PackageInfo with name '%s'", mPackageName)); 150 } 151 } 152 153 private static PackageInfo createPackageInfo( 154 String packageName, boolean enabled, boolean valid, boolean installed) { 155 PackageInfo p = new PackageInfo(); 156 p.packageName = packageName; 157 p.applicationInfo = new ApplicationInfo(); 158 p.applicationInfo.enabled = enabled; 159 p.applicationInfo.metaData = new Bundle(); 160 if (installed) { 161 p.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED; 162 } else { 163 p.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED; 164 } 165 if (valid) { 166 // no flag means invalid 167 p.applicationInfo.metaData.putString(WEBVIEW_LIBRARY_FLAG, "blah"); 168 } 169 return p; 170 } 171 172 private static PackageInfo createPackageInfo(String packageName, boolean enabled, boolean valid, 173 boolean installed, Signature[] signatures, long updateTime) { 174 PackageInfo p = createPackageInfo(packageName, enabled, valid, installed); 175 p.signatures = signatures; 176 p.lastUpdateTime = updateTime; 177 return p; 178 } 179 180 private static PackageInfo createPackageInfo(String packageName, boolean enabled, boolean valid, 181 boolean installed, Signature[] signatures, long updateTime, boolean hidden) { 182 PackageInfo p = 183 createPackageInfo(packageName, enabled, valid, installed, signatures, updateTime); 184 if (hidden) { 185 p.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN; 186 } else { 187 p.applicationInfo.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_HIDDEN; 188 } 189 return p; 190 } 191 192 private static PackageInfo createPackageInfo(String packageName, boolean enabled, boolean valid, 193 boolean installed, Signature[] signatures, long updateTime, boolean hidden, 194 int versionCode, boolean isSystemApp) { 195 PackageInfo p = createPackageInfo(packageName, enabled, valid, installed, signatures, 196 updateTime, hidden); 197 p.versionCode = versionCode; 198 p.applicationInfo.versionCode = versionCode; 199 if (isSystemApp) p.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; 200 return p; 201 } 202 203 private void checkPreparationPhasesForPackage(String expectedPackage, int numPreparation) { 204 // Verify that onWebViewProviderChanged was called for the numPreparation'th time for the 205 // expected package 206 Mockito.verify(mTestSystemImpl, Mockito.times(numPreparation)).onWebViewProviderChanged( 207 Mockito.argThat(new IsPackageInfoWithName(expectedPackage))); 208 209 mWebViewUpdateServiceImpl.notifyRelroCreationCompleted(); 210 211 WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider(); 212 assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status); 213 assertEquals(expectedPackage, response.packageInfo.packageName); 214 } 215 216 /** 217 * The WebView preparation boot phase is run on the main thread (especially on a thread with a 218 * looper) so to avoid bugs where our tests fail because a looper hasn't been attached to the 219 * thread running prepareWebViewInSystemServer we run it on the main thread. 220 */ 221 private void runWebViewBootPreparationOnMainSync() { 222 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 223 @Override 224 public void run() { 225 mWebViewUpdateServiceImpl.prepareWebViewInSystemServer(); 226 } 227 }); 228 } 229 230 231 // **************** 232 // Tests 233 // **************** 234 235 236 @Test 237 public void testWithSinglePackage() { 238 String testPackageName = "test.package.name"; 239 checkCertainPackageUsedAfterWebViewBootPreparation(testPackageName, 240 new WebViewProviderInfo[] { 241 new WebViewProviderInfo(testPackageName, "", 242 true /*default available*/, false /* fallback */, null)}); 243 } 244 245 @Test 246 public void testDefaultPackageUsedOverNonDefault() { 247 String defaultPackage = "defaultPackage"; 248 String nonDefaultPackage = "nonDefaultPackage"; 249 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 250 new WebViewProviderInfo(nonDefaultPackage, "", false, false, null), 251 new WebViewProviderInfo(defaultPackage, "", true, false, null)}; 252 checkCertainPackageUsedAfterWebViewBootPreparation(defaultPackage, packages); 253 } 254 255 @Test 256 public void testSeveralRelros() { 257 String singlePackage = "singlePackage"; 258 checkCertainPackageUsedAfterWebViewBootPreparation( 259 singlePackage, 260 new WebViewProviderInfo[] { 261 new WebViewProviderInfo(singlePackage, "", true /*def av*/, false, null)}, 262 2); 263 } 264 265 // Ensure that package with valid signatures is chosen rather than package with invalid 266 // signatures. 267 @Test 268 public void testWithSignatures() { 269 String validPackage = "valid package"; 270 String invalidPackage = "invalid package"; 271 272 Signature validSignature = new Signature("11"); 273 Signature invalidExpectedSignature = new Signature("22"); 274 Signature invalidPackageSignature = new Signature("33"); 275 276 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 277 new WebViewProviderInfo(invalidPackage, "", true, false, new String[]{ 278 Base64.encodeToString( 279 invalidExpectedSignature.toByteArray(), Base64.DEFAULT)}), 280 new WebViewProviderInfo(validPackage, "", true, false, new String[]{ 281 Base64.encodeToString( 282 validSignature.toByteArray(), Base64.DEFAULT)}) 283 }; 284 setupWithPackages(packages, true /* fallback logic enabled */, 1 /* numRelros */, 285 false /* isDebuggable */); 286 mTestSystemImpl.setPackageInfo(createPackageInfo(invalidPackage, true /* enabled */, 287 true /* valid */, true /* installed */, new Signature[]{invalidPackageSignature} 288 , 0 /* updateTime */)); 289 mTestSystemImpl.setPackageInfo(createPackageInfo(validPackage, true /* enabled */, 290 true /* valid */, true /* installed */, new Signature[]{validSignature} 291 , 0 /* updateTime */)); 292 293 runWebViewBootPreparationOnMainSync(); 294 295 296 checkPreparationPhasesForPackage(validPackage, 1 /* first preparation for this package */); 297 298 WebViewProviderInfo[] validPackages = mWebViewUpdateServiceImpl.getValidWebViewPackages(); 299 assertEquals(1, validPackages.length); 300 assertEquals(validPackage, validPackages[0].packageName); 301 } 302 303 @Test 304 public void testFailWaitingForRelro() { 305 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 306 new WebViewProviderInfo("packagename", "", true, true, null)}; 307 setupWithPackages(packages); 308 setEnabledAndValidPackageInfos(packages); 309 310 runWebViewBootPreparationOnMainSync(); 311 312 Mockito.verify(mTestSystemImpl).onWebViewProviderChanged( 313 Mockito.argThat(new IsPackageInfoWithName(packages[0].packageName))); 314 315 // Never call notifyRelroCreation() 316 317 WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider(); 318 assertEquals(WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO, response.status); 319 } 320 321 @Test 322 public void testFailListingEmptyWebviewPackages() { 323 WebViewProviderInfo[] packages = new WebViewProviderInfo[0]; 324 setupWithPackages(packages); 325 setEnabledAndValidPackageInfos(packages); 326 327 runWebViewBootPreparationOnMainSync(); 328 329 Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged( 330 Matchers.anyObject()); 331 332 WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider(); 333 assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status); 334 assertEquals(null, mWebViewUpdateServiceImpl.getCurrentWebViewPackage()); 335 336 // Now install a package 337 String singlePackage = "singlePackage"; 338 packages = new WebViewProviderInfo[]{ 339 new WebViewProviderInfo(singlePackage, "", true, false, null)}; 340 setupWithPackages(packages); 341 setEnabledAndValidPackageInfos(packages); 342 343 mWebViewUpdateServiceImpl.packageStateChanged(singlePackage, 344 WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID); 345 346 checkPreparationPhasesForPackage(singlePackage, 1 /* number of finished preparations */); 347 assertEquals(singlePackage, 348 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName); 349 350 // Remove the package again 351 mTestSystemImpl.removePackageInfo(singlePackage); 352 mWebViewUpdateServiceImpl.packageStateChanged(singlePackage, 353 WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID); 354 355 // Package removed - ensure our interface states that there is no package 356 response = mWebViewUpdateServiceImpl.waitForAndGetProvider(); 357 assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status); 358 assertEquals(null, mWebViewUpdateServiceImpl.getCurrentWebViewPackage()); 359 } 360 361 @Test 362 public void testFailListingInvalidWebviewPackage() { 363 WebViewProviderInfo wpi = new WebViewProviderInfo("package", "", true, true, null); 364 WebViewProviderInfo[] packages = new WebViewProviderInfo[] {wpi}; 365 setupWithPackages(packages); 366 mTestSystemImpl.setPackageInfo( 367 createPackageInfo(wpi.packageName, true /* enabled */, false /* valid */, 368 true /* installed */)); 369 370 runWebViewBootPreparationOnMainSync(); 371 372 Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged( 373 Matchers.anyObject()); 374 375 WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider(); 376 assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status); 377 378 // Verify that we can recover from failing to list webview packages. 379 mTestSystemImpl.setPackageInfo( 380 createPackageInfo(wpi.packageName, true /* enabled */, true /* valid */, 381 true /* installed */)); 382 mWebViewUpdateServiceImpl.packageStateChanged(wpi.packageName, 383 WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID); 384 385 checkPreparationPhasesForPackage(wpi.packageName, 1); 386 } 387 388 // Test that switching provider using changeProviderAndSetting works. 389 @Test 390 public void testSwitchingProvider() { 391 String firstPackage = "first"; 392 String secondPackage = "second"; 393 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 394 new WebViewProviderInfo(firstPackage, "", true, false, null), 395 new WebViewProviderInfo(secondPackage, "", true, false, null)}; 396 checkSwitchingProvider(packages, firstPackage, secondPackage); 397 } 398 399 @Test 400 public void testSwitchingProviderToNonDefault() { 401 String defaultPackage = "defaultPackage"; 402 String nonDefaultPackage = "nonDefaultPackage"; 403 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 404 new WebViewProviderInfo(defaultPackage, "", true, false, null), 405 new WebViewProviderInfo(nonDefaultPackage, "", false, false, null)}; 406 checkSwitchingProvider(packages, defaultPackage, nonDefaultPackage); 407 } 408 409 private void checkSwitchingProvider(WebViewProviderInfo[] packages, String initialPackage, 410 String finalPackage) { 411 checkCertainPackageUsedAfterWebViewBootPreparation(initialPackage, packages); 412 413 mWebViewUpdateServiceImpl.changeProviderAndSetting(finalPackage); 414 checkPreparationPhasesForPackage(finalPackage, 1 /* first preparation for this package */); 415 416 Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(initialPackage)); 417 } 418 419 // Change provider during relro creation by using changeProviderAndSetting 420 @Test 421 public void testSwitchingProviderDuringRelroCreation() { 422 checkChangingProviderDuringRelroCreation(true); 423 } 424 425 // Change provider during relro creation by enabling a provider 426 @Test 427 public void testChangingProviderThroughEnablingDuringRelroCreation() { 428 checkChangingProviderDuringRelroCreation(false); 429 } 430 431 private void checkChangingProviderDuringRelroCreation(boolean settingsChange) { 432 String firstPackage = "first"; 433 String secondPackage = "second"; 434 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 435 new WebViewProviderInfo(firstPackage, "", true, false, null), 436 new WebViewProviderInfo(secondPackage, "", true, false, null)}; 437 setupWithPackages(packages); 438 // Have all packages be enabled, so that we can change provider however we want to 439 setEnabledAndValidPackageInfos(packages); 440 441 CountDownLatch countdown = new CountDownLatch(1); 442 443 runWebViewBootPreparationOnMainSync(); 444 445 Mockito.verify(mTestSystemImpl).onWebViewProviderChanged( 446 Mockito.argThat(new IsPackageInfoWithName(firstPackage))); 447 448 assertEquals(firstPackage, 449 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName); 450 451 new Thread(new Runnable() { 452 @Override 453 public void run() { 454 WebViewProviderResponse threadResponse = 455 mWebViewUpdateServiceImpl.waitForAndGetProvider(); 456 assertEquals(WebViewFactory.LIBLOAD_SUCCESS, threadResponse.status); 457 assertEquals(secondPackage, threadResponse.packageInfo.packageName); 458 // Verify that we killed the first package if we performed a settings change - 459 // otherwise we had to disable the first package, in which case its dependents 460 // should have been killed by the framework. 461 if (settingsChange) { 462 Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage)); 463 } 464 countdown.countDown(); 465 } 466 }).start(); 467 try { 468 Thread.sleep(500); // Let the new thread run / be blocked 469 } catch (InterruptedException e) { 470 } 471 472 if (settingsChange) { 473 mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage); 474 } else { 475 // Enable the second provider 476 mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */, 477 true /* valid */, true /* installed */)); 478 mWebViewUpdateServiceImpl.packageStateChanged( 479 secondPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID); 480 481 // Ensure we haven't changed package yet. 482 assertEquals(firstPackage, 483 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName); 484 485 // Switch provider by disabling the first one 486 mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, false /* enabled */, 487 true /* valid */, true /* installed */)); 488 mWebViewUpdateServiceImpl.packageStateChanged( 489 firstPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID); 490 } 491 mWebViewUpdateServiceImpl.notifyRelroCreationCompleted(); 492 // first package done, should start on second 493 494 Mockito.verify(mTestSystemImpl).onWebViewProviderChanged( 495 Mockito.argThat(new IsPackageInfoWithName(secondPackage))); 496 497 mWebViewUpdateServiceImpl.notifyRelroCreationCompleted(); 498 // second package done, the other thread should now be unblocked 499 try { 500 countdown.await(); 501 } catch (InterruptedException e) { 502 } 503 } 504 505 @Test 506 public void testRunFallbackLogicIfEnabled() { 507 checkFallbackLogicBeingRun(true); 508 } 509 510 @Test 511 public void testDontRunFallbackLogicIfDisabled() { 512 checkFallbackLogicBeingRun(false); 513 } 514 515 private void checkFallbackLogicBeingRun(boolean fallbackLogicEnabled) { 516 String primaryPackage = "primary"; 517 String fallbackPackage = "fallback"; 518 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 519 new WebViewProviderInfo( 520 primaryPackage, "", true /* default available */, false /* fallback */, null), 521 new WebViewProviderInfo( 522 fallbackPackage, "", true /* default available */, true /* fallback */, null)}; 523 setupWithPackages(packages, fallbackLogicEnabled); 524 setEnabledAndValidPackageInfos(packages); 525 526 runWebViewBootPreparationOnMainSync(); 527 // Verify that we disable the fallback package if fallback logic enabled, and don't disable 528 // the fallback package if that logic is disabled 529 if (fallbackLogicEnabled) { 530 Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers( 531 Matchers.anyObject(), Mockito.eq(fallbackPackage)); 532 } else { 533 Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers( 534 Matchers.anyObject(), Matchers.anyObject()); 535 } 536 Mockito.verify(mTestSystemImpl).onWebViewProviderChanged( 537 Mockito.argThat(new IsPackageInfoWithName(primaryPackage))); 538 539 // Enable fallback package 540 mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */, 541 true /* valid */, true /* installed */)); 542 mWebViewUpdateServiceImpl.packageStateChanged( 543 fallbackPackage, WebViewUpdateService.PACKAGE_CHANGED, TestSystemImpl.PRIMARY_USER_ID); 544 545 if (fallbackLogicEnabled) { 546 // Check that we have now disabled the fallback package twice 547 Mockito.verify(mTestSystemImpl, Mockito.times(2)).uninstallAndDisablePackageForAllUsers( 548 Matchers.anyObject(), Mockito.eq(fallbackPackage)); 549 } else { 550 // Check that we still haven't disabled any package 551 Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers( 552 Matchers.anyObject(), Matchers.anyObject()); 553 } 554 } 555 556 /** 557 * Scenario for installing primary package when fallback enabled. 558 * 1. Start with only fallback installed 559 * 2. Install non-fallback 560 * 3. Fallback should be disabled 561 */ 562 @Test 563 public void testInstallingNonFallbackPackage() { 564 String primaryPackage = "primary"; 565 String fallbackPackage = "fallback"; 566 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 567 new WebViewProviderInfo( 568 primaryPackage, "", true /* default available */, false /* fallback */, null), 569 new WebViewProviderInfo( 570 fallbackPackage, "", true /* default available */, true /* fallback */, null)}; 571 setupWithPackages(packages, true /* isFallbackLogicEnabled */); 572 mTestSystemImpl.setPackageInfo( 573 createPackageInfo(fallbackPackage, true /* enabled */ , true /* valid */, 574 true /* installed */)); 575 576 runWebViewBootPreparationOnMainSync(); 577 Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers( 578 Matchers.anyObject(), Matchers.anyObject()); 579 580 checkPreparationPhasesForPackage(fallbackPackage, 581 1 /* first preparation for this package*/); 582 583 // Install primary package 584 mTestSystemImpl.setPackageInfo( 585 createPackageInfo(primaryPackage, true /* enabled */ , true /* valid */, 586 true /* installed */)); 587 mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage, 588 WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID); 589 590 // Verify fallback disabled, primary package used as provider, and fallback package killed 591 Mockito.verify(mTestSystemImpl).uninstallAndDisablePackageForAllUsers( 592 Matchers.anyObject(), Mockito.eq(fallbackPackage)); 593 checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation for this package*/); 594 Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(fallbackPackage)); 595 } 596 597 @Test 598 public void testFallbackChangesEnabledStateSingleUser() { 599 for (PackageRemovalType removalType : REMOVAL_TYPES) { 600 checkFallbackChangesEnabledState(false /* multiUser */, removalType); 601 } 602 } 603 604 @Test 605 public void testFallbackChangesEnabledStateMultiUser() { 606 for (PackageRemovalType removalType : REMOVAL_TYPES) { 607 checkFallbackChangesEnabledState(true /* multiUser */, removalType); 608 } 609 } 610 611 /** 612 * Represents how to remove a package during a tests (disabling it / uninstalling it / hiding 613 * it). 614 */ 615 private enum PackageRemovalType { 616 UNINSTALL, DISABLE, HIDE 617 } 618 619 private PackageRemovalType[] REMOVAL_TYPES = PackageRemovalType.class.getEnumConstants(); 620 621 public void checkFallbackChangesEnabledState(boolean multiUser, 622 PackageRemovalType removalType) { 623 String primaryPackage = "primary"; 624 String fallbackPackage = "fallback"; 625 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 626 new WebViewProviderInfo( 627 primaryPackage, "", true /* default available */, false /* fallback */, null), 628 new WebViewProviderInfo( 629 fallbackPackage, "", true /* default available */, true /* fallback */, null)}; 630 setupWithPackages(packages, true /* fallbackLogicEnabled */); 631 int secondaryUserId = 10; 632 int userIdToChangePackageFor = multiUser ? secondaryUserId : TestSystemImpl.PRIMARY_USER_ID; 633 if (multiUser) { 634 mTestSystemImpl.addUser(secondaryUserId); 635 setEnabledAndValidPackageInfosForUser(secondaryUserId, packages); 636 } 637 setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, packages); 638 639 runWebViewBootPreparationOnMainSync(); 640 641 // Verify fallback disabled at boot when primary package enabled 642 checkEnablePackageForUserCalled(fallbackPackage, false, multiUser 643 ? new int[] {TestSystemImpl.PRIMARY_USER_ID, secondaryUserId} 644 : new int[] {TestSystemImpl.PRIMARY_USER_ID}, 1 /* numUsages */); 645 646 checkPreparationPhasesForPackage(primaryPackage, 1); 647 648 boolean enabled = !(removalType == PackageRemovalType.DISABLE); 649 boolean installed = !(removalType == PackageRemovalType.UNINSTALL); 650 boolean hidden = (removalType == PackageRemovalType.HIDE); 651 // Disable primary package and ensure fallback becomes enabled and used 652 mTestSystemImpl.setPackageInfoForUser(userIdToChangePackageFor, 653 createPackageInfo(primaryPackage, enabled /* enabled */, true /* valid */, 654 installed /* installed */, null /* signature */, 0 /* updateTime */, 655 hidden /* hidden */)); 656 mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage, 657 removalType == PackageRemovalType.DISABLE 658 ? WebViewUpdateService.PACKAGE_CHANGED : WebViewUpdateService.PACKAGE_REMOVED, 659 userIdToChangePackageFor); // USER ID 660 661 checkEnablePackageForUserCalled(fallbackPackage, true, multiUser 662 ? new int[] {TestSystemImpl.PRIMARY_USER_ID, secondaryUserId} 663 : new int[] {TestSystemImpl.PRIMARY_USER_ID}, 1 /* numUsages */); 664 665 checkPreparationPhasesForPackage(fallbackPackage, 1); 666 667 668 // Again enable primary package and verify primary is used and fallback becomes disabled 669 mTestSystemImpl.setPackageInfoForUser(userIdToChangePackageFor, 670 createPackageInfo(primaryPackage, true /* enabled */, true /* valid */, 671 true /* installed */)); 672 mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage, 673 removalType == PackageRemovalType.DISABLE 674 ? WebViewUpdateService.PACKAGE_CHANGED : WebViewUpdateService.PACKAGE_ADDED, 675 userIdToChangePackageFor); 676 677 // Verify fallback is disabled a second time when primary package becomes enabled 678 checkEnablePackageForUserCalled(fallbackPackage, false, multiUser 679 ? new int[] {TestSystemImpl.PRIMARY_USER_ID, secondaryUserId} 680 : new int[] {TestSystemImpl.PRIMARY_USER_ID}, 2 /* numUsages */); 681 682 checkPreparationPhasesForPackage(primaryPackage, 2); 683 } 684 685 private void checkEnablePackageForUserCalled(String packageName, boolean expectEnabled, 686 int[] userIds, int numUsages) { 687 for (int userId : userIds) { 688 Mockito.verify(mTestSystemImpl, Mockito.times(numUsages)).enablePackageForUser( 689 Mockito.eq(packageName), Mockito.eq(expectEnabled), Mockito.eq(userId)); 690 } 691 } 692 693 @Test 694 public void testAddUserWhenFallbackLogicEnabled() { 695 checkAddingNewUser(true); 696 } 697 698 @Test 699 public void testAddUserWhenFallbackLogicDisabled() { 700 checkAddingNewUser(false); 701 } 702 703 public void checkAddingNewUser(boolean fallbackLogicEnabled) { 704 String primaryPackage = "primary"; 705 String fallbackPackage = "fallback"; 706 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 707 new WebViewProviderInfo( 708 primaryPackage, "", true /* default available */, false /* fallback */, null), 709 new WebViewProviderInfo( 710 fallbackPackage, "", true /* default available */, true /* fallback */, null)}; 711 setupWithPackages(packages, fallbackLogicEnabled); 712 setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, packages); 713 int newUser = 100; 714 mTestSystemImpl.addUser(newUser); 715 setEnabledAndValidPackageInfosForUser(newUser, packages); 716 mWebViewUpdateServiceImpl.handleNewUser(newUser); 717 if (fallbackLogicEnabled) { 718 // Verify fallback package becomes disabled for new user 719 Mockito.verify(mTestSystemImpl).enablePackageForUser( 720 Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */, 721 Mockito.eq(newUser)); 722 } else { 723 // Verify that we don't disable fallback for new user 724 Mockito.verify(mTestSystemImpl, Mockito.never()).enablePackageForUser( 725 Mockito.anyObject(), Matchers.anyBoolean() /* enable */, 726 Matchers.anyInt() /* user */); 727 } 728 } 729 730 /** 731 * Ensures that adding a new user for which the current WebView package is uninstalled causes a 732 * change of WebView provider. 733 */ 734 @Test 735 public void testAddingNewUserWithUninstalledPackage() { 736 String primaryPackage = "primary"; 737 String fallbackPackage = "fallback"; 738 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 739 new WebViewProviderInfo( 740 primaryPackage, "", true /* default available */, false /* fallback */, null), 741 new WebViewProviderInfo( 742 fallbackPackage, "", true /* default available */, true /* fallback */, null)}; 743 setupWithPackages(packages, true /* fallbackLogicEnabled */); 744 setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, packages); 745 int newUser = 100; 746 mTestSystemImpl.addUser(newUser); 747 // Let the primary package be uninstalled for the new user 748 mTestSystemImpl.setPackageInfoForUser(newUser, 749 createPackageInfo(primaryPackage, true /* enabled */, true /* valid */, 750 false /* installed */)); 751 mTestSystemImpl.setPackageInfoForUser(newUser, 752 createPackageInfo(fallbackPackage, false /* enabled */, true /* valid */, 753 true /* installed */)); 754 mWebViewUpdateServiceImpl.handleNewUser(newUser); 755 // Verify fallback package doesn't become disabled for the primary user. 756 Mockito.verify(mTestSystemImpl, Mockito.never()).enablePackageForUser( 757 Mockito.anyObject(), Mockito.eq(false) /* enable */, 758 Mockito.eq(TestSystemImpl.PRIMARY_USER_ID) /* user */); 759 // Verify that we enable the fallback package for the secondary user. 760 Mockito.verify(mTestSystemImpl, Mockito.times(1)).enablePackageForUser( 761 Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */, 762 Mockito.eq(newUser) /* user */); 763 checkPreparationPhasesForPackage(fallbackPackage, 1 /* numRelros */); 764 } 765 766 /** 767 * Timing dependent test where we verify that the list of valid webview packages becoming empty 768 * at a certain point doesn't crash us or break our state. 769 */ 770 @Test 771 public void testNotifyRelroDoesntCrashIfNoPackages() { 772 String firstPackage = "first"; 773 String secondPackage = "second"; 774 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 775 new WebViewProviderInfo(firstPackage, "", true /* default available */, 776 false /* fallback */, null), 777 new WebViewProviderInfo(secondPackage, "", true /* default available */, 778 false /* fallback */, null)}; 779 setupWithPackages(packages); 780 // Add (enabled and valid) package infos for each provider 781 setEnabledAndValidPackageInfos(packages); 782 783 runWebViewBootPreparationOnMainSync(); 784 785 Mockito.verify(mTestSystemImpl).onWebViewProviderChanged( 786 Mockito.argThat(new IsPackageInfoWithName(firstPackage))); 787 788 // Change provider during relro creation to enter a state where we are 789 // waiting for relro creation to complete just to re-run relro creation. 790 // (so that in next notifyRelroCreationCompleted() call we have to list webview packages) 791 mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage); 792 793 // Make packages invalid to cause exception to be thrown 794 mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */, 795 false /* valid */, true /* installed */, null /* signatures */, 796 0 /* updateTime */)); 797 mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */, 798 false /* valid */, true /* installed */)); 799 800 // This shouldn't throw an exception! 801 mWebViewUpdateServiceImpl.notifyRelroCreationCompleted(); 802 803 WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider(); 804 assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status); 805 806 // Now make a package valid again and verify that we can switch back to that 807 mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */, 808 true /* valid */, true /* installed */, null /* signatures */, 809 1 /* updateTime */ )); 810 811 mWebViewUpdateServiceImpl.packageStateChanged(firstPackage, 812 WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID); 813 814 // Ensure we use firstPackage 815 checkPreparationPhasesForPackage(firstPackage, 2 /* second preparation for this package */); 816 } 817 818 /** 819 * Verify that even if a user-chosen package is removed temporarily we start using it again when 820 * it is added back. 821 */ 822 @Test 823 public void testTempRemovePackageDoesntSwitchProviderPermanently() { 824 String firstPackage = "first"; 825 String secondPackage = "second"; 826 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 827 new WebViewProviderInfo(firstPackage, "", true /* default available */, 828 false /* fallback */, null), 829 new WebViewProviderInfo(secondPackage, "", true /* default available */, 830 false /* fallback */, null)}; 831 checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages); 832 833 // Explicitly use the second package 834 mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage); 835 checkPreparationPhasesForPackage(secondPackage, 1 /* first time for this package */); 836 837 // Remove second package (invalidate it) and verify that first package is used 838 mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */, 839 false /* valid */, true /* installed */)); 840 mWebViewUpdateServiceImpl.packageStateChanged(secondPackage, 841 WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID); 842 checkPreparationPhasesForPackage(firstPackage, 2 /* second time for this package */); 843 844 // Now make the second package valid again and verify that it is used again 845 mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */, 846 true /* valid */, true /* installed */)); 847 mWebViewUpdateServiceImpl.packageStateChanged(secondPackage, 848 WebViewUpdateService.PACKAGE_ADDED, TestSystemImpl.PRIMARY_USER_ID); 849 checkPreparationPhasesForPackage(secondPackage, 2 /* second time for this package */); 850 } 851 852 /** 853 * Ensure that we update the user-chosen setting across boots if the chosen package is no 854 * longer installed and valid. 855 */ 856 @Test 857 public void testProviderSettingChangedDuringBootIfProviderNotAvailable() { 858 String chosenPackage = "chosenPackage"; 859 String nonChosenPackage = "non-chosenPackage"; 860 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 861 new WebViewProviderInfo(chosenPackage, "", true /* default available */, 862 false /* fallback */, null), 863 new WebViewProviderInfo(nonChosenPackage, "", true /* default available */, 864 false /* fallback */, null)}; 865 866 setupWithPackages(packages); 867 // Only 'install' nonChosenPackage 868 mTestSystemImpl.setPackageInfo( 869 createPackageInfo(nonChosenPackage, true /* enabled */, true /* valid */, true /* installed */)); 870 871 // Set user-chosen package 872 mTestSystemImpl.updateUserSetting(null, chosenPackage); 873 874 runWebViewBootPreparationOnMainSync(); 875 876 // Verify that we switch the setting to point to the current package 877 Mockito.verify(mTestSystemImpl).updateUserSetting( 878 Mockito.anyObject(), Mockito.eq(nonChosenPackage)); 879 assertEquals(nonChosenPackage, mTestSystemImpl.getUserChosenWebViewProvider(null)); 880 881 checkPreparationPhasesForPackage(nonChosenPackage, 1); 882 } 883 884 @Test 885 public void testRecoverFailedListingWebViewPackagesSettingsChange() { 886 checkRecoverAfterFailListingWebviewPackages(true); 887 } 888 889 @Test 890 public void testRecoverFailedListingWebViewPackagesAddedPackage() { 891 checkRecoverAfterFailListingWebviewPackages(false); 892 } 893 894 /** 895 * Test that we can recover correctly from failing to list WebView packages. 896 * settingsChange: whether to fail during changeProviderAndSetting or packageStateChanged 897 */ 898 public void checkRecoverAfterFailListingWebviewPackages(boolean settingsChange) { 899 String firstPackage = "first"; 900 String secondPackage = "second"; 901 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 902 new WebViewProviderInfo(firstPackage, "", true /* default available */, 903 false /* fallback */, null), 904 new WebViewProviderInfo(secondPackage, "", true /* default available */, 905 false /* fallback */, null)}; 906 checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages); 907 908 // Make both packages invalid so that we fail listing WebView packages 909 mTestSystemImpl.setPackageInfo(createPackageInfo(firstPackage, true /* enabled */, 910 false /* valid */, true /* installed */)); 911 mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */, 912 false /* valid */, true /* installed */)); 913 914 // Change package to hit the webview packages listing problem. 915 if (settingsChange) { 916 mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage); 917 } else { 918 mWebViewUpdateServiceImpl.packageStateChanged(secondPackage, 919 WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID); 920 } 921 922 WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider(); 923 assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status); 924 925 // Make second package valid and verify that we can load it again 926 mTestSystemImpl.setPackageInfo(createPackageInfo(secondPackage, true /* enabled */, 927 true /* valid */, true /* installed */)); 928 929 mWebViewUpdateServiceImpl.packageStateChanged(secondPackage, 930 WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID); 931 932 933 checkPreparationPhasesForPackage(secondPackage, 1); 934 } 935 936 @Test 937 public void testDontKillIfPackageReplaced() { 938 checkDontKillIfPackageRemoved(true); 939 } 940 941 @Test 942 public void testDontKillIfPackageRemoved() { 943 checkDontKillIfPackageRemoved(false); 944 } 945 946 public void checkDontKillIfPackageRemoved(boolean replaced) { 947 String firstPackage = "first"; 948 String secondPackage = "second"; 949 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 950 new WebViewProviderInfo(firstPackage, "", true /* default available */, 951 false /* fallback */, null), 952 new WebViewProviderInfo(secondPackage, "", true /* default available */, 953 false /* fallback */, null)}; 954 checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages); 955 956 // Replace or remove the current webview package 957 if (replaced) { 958 mTestSystemImpl.setPackageInfo( 959 createPackageInfo(firstPackage, true /* enabled */, false /* valid */, 960 true /* installed */)); 961 mWebViewUpdateServiceImpl.packageStateChanged(firstPackage, 962 WebViewUpdateService.PACKAGE_ADDED_REPLACED, TestSystemImpl.PRIMARY_USER_ID); 963 } else { 964 mTestSystemImpl.removePackageInfo(firstPackage); 965 mWebViewUpdateServiceImpl.packageStateChanged(firstPackage, 966 WebViewUpdateService.PACKAGE_REMOVED, TestSystemImpl.PRIMARY_USER_ID); 967 } 968 969 checkPreparationPhasesForPackage(secondPackage, 1); 970 971 Mockito.verify(mTestSystemImpl, Mockito.never()).killPackageDependents( 972 Mockito.anyObject()); 973 } 974 975 @Test 976 public void testKillIfSettingChanged() { 977 String firstPackage = "first"; 978 String secondPackage = "second"; 979 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 980 new WebViewProviderInfo(firstPackage, "", true /* default available */, 981 false /* fallback */, null), 982 new WebViewProviderInfo(secondPackage, "", true /* default available */, 983 false /* fallback */, null)}; 984 checkCertainPackageUsedAfterWebViewBootPreparation(firstPackage, packages); 985 986 mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage); 987 988 checkPreparationPhasesForPackage(secondPackage, 1); 989 990 Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(firstPackage)); 991 } 992 993 /** 994 * Test that we kill apps using an old provider when we change the provider setting, even if the 995 * new provider is not the one we intended to change to. 996 */ 997 @Test 998 public void testKillIfChangeProviderIncorrectly() { 999 String firstPackage = "first"; 1000 String secondPackage = "second"; 1001 String thirdPackage = "third"; 1002 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 1003 new WebViewProviderInfo(firstPackage, "", true /* default available */, 1004 false /* fallback */, null), 1005 new WebViewProviderInfo(secondPackage, "", true /* default available */, 1006 false /* fallback */, null), 1007 new WebViewProviderInfo(thirdPackage, "", true /* default available */, 1008 false /* fallback */, null)}; 1009 setupWithPackages(packages); 1010 setEnabledAndValidPackageInfos(packages); 1011 1012 // Start with the setting pointing to the third package 1013 mTestSystemImpl.updateUserSetting(null, thirdPackage); 1014 1015 runWebViewBootPreparationOnMainSync(); 1016 checkPreparationPhasesForPackage(thirdPackage, 1); 1017 1018 mTestSystemImpl.setPackageInfo( 1019 createPackageInfo(secondPackage, true /* enabled */, false /* valid */, true /* installed */)); 1020 1021 // Try to switch to the invalid second package, this should result in switching to the first 1022 // package, since that is more preferred than the third one. 1023 assertEquals(firstPackage, 1024 mWebViewUpdateServiceImpl.changeProviderAndSetting(secondPackage)); 1025 1026 checkPreparationPhasesForPackage(firstPackage, 1); 1027 1028 Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(thirdPackage)); 1029 } 1030 1031 @Test 1032 public void testLowerPackageVersionNotValid() { 1033 checkPackageVersions(new int[]{200000} /* system version */, 100000/* candidate version */, 1034 false /* expected validity */); 1035 } 1036 1037 @Test 1038 public void testEqualPackageVersionValid() { 1039 checkPackageVersions(new int[]{100000} /* system version */, 100000 /* candidate version */, 1040 true /* expected validity */); 1041 } 1042 1043 @Test 1044 public void testGreaterPackageVersionValid() { 1045 checkPackageVersions(new int[]{100000} /* system versions */, 200000 /* candidate version */, 1046 true /* expected validity */); 1047 } 1048 1049 @Test 1050 public void testLastFiveDigitsIgnored() { 1051 checkPackageVersions(new int[]{654321} /* system version */, 612345 /* candidate version */, 1052 true /* expected validity */); 1053 } 1054 1055 @Test 1056 public void testMinimumSystemVersionUsedTwoDefaultsCandidateValid() { 1057 checkPackageVersions(new int[]{300000, 100000} /* system versions */, 1058 200000 /* candidate version */, true /* expected validity */); 1059 } 1060 1061 @Test 1062 public void testMinimumSystemVersionUsedTwoDefaultsCandidateInvalid() { 1063 checkPackageVersions(new int[]{300000, 200000} /* system versions */, 1064 100000 /* candidate version */, false /* expected validity */); 1065 } 1066 1067 @Test 1068 public void testMinimumSystemVersionUsedSeveralDefaultsCandidateValid() { 1069 checkPackageVersions(new int[]{100000, 200000, 300000, 400000, 500000} /* system versions */, 1070 100000 /* candidate version */, true /* expected validity */); 1071 } 1072 1073 @Test 1074 public void testMinimumSystemVersionUsedSeveralDefaultsCandidateInvalid() { 1075 checkPackageVersions(new int[]{200000, 300000, 400000, 500000, 600000} /* system versions */, 1076 100000 /* candidate version */, false /* expected validity */); 1077 } 1078 1079 @Test 1080 public void testMinimumSystemVersionUsedFallbackIgnored() { 1081 checkPackageVersions(new int[]{300000, 400000, 500000, 600000, 700000} /* system versions */, 1082 200000 /* candidate version */, false /* expected validity */, true /* add fallback */, 1083 100000 /* fallback version */, false /* expected validity of fallback */); 1084 } 1085 1086 @Test 1087 public void testFallbackValid() { 1088 checkPackageVersions(new int[]{300000, 400000, 500000, 600000, 700000} /* system versions */, 1089 200000/* candidate version */, false /* expected validity */, true /* add fallback */, 1090 300000 /* fallback version */, true /* expected validity of fallback */); 1091 } 1092 1093 private void checkPackageVersions(int[] systemVersions, int candidateVersion, 1094 boolean candidateShouldBeValid) { 1095 checkPackageVersions(systemVersions, candidateVersion, candidateShouldBeValid, 1096 false, 0, false); 1097 } 1098 1099 /** 1100 * Utility method for checking that package version restriction works as it should. 1101 * I.e. that a package with lower version than the system-default is not valid and that a 1102 * package with greater than or equal version code is considered valid. 1103 */ 1104 private void checkPackageVersions(int[] systemVersions, int candidateVersion, 1105 boolean candidateShouldBeValid, boolean addFallback, int fallbackVersion, 1106 boolean fallbackShouldBeValid) { 1107 int numSystemPackages = systemVersions.length; 1108 int numFallbackPackages = (addFallback ? 1 : 0); 1109 int numPackages = systemVersions.length + 1 + numFallbackPackages; 1110 String candidatePackage = "candidatePackage"; 1111 String systemPackage = "systemPackage"; 1112 String fallbackPackage = "fallbackPackage"; 1113 1114 // Each package needs a valid signature since we set isDebuggable to false 1115 Signature signature = new Signature("11"); 1116 String encodedSignatureString = 1117 Base64.encodeToString(signature.toByteArray(), Base64.DEFAULT); 1118 1119 // Set up config 1120 // 1. candidatePackage 1121 // 2-N. default available non-fallback packages 1122 // N+1. default available fallback package 1123 WebViewProviderInfo[] packages = new WebViewProviderInfo[numPackages]; 1124 packages[0] = new WebViewProviderInfo(candidatePackage, "", 1125 false /* available by default */, false /* fallback */, 1126 new String[]{encodedSignatureString}); 1127 for(int n = 1; n < numSystemPackages + 1; n++) { 1128 packages[n] = new WebViewProviderInfo(systemPackage + n, "", 1129 true /* available by default */, false /* fallback */, 1130 new String[]{encodedSignatureString}); 1131 } 1132 if (addFallback) { 1133 packages[packages.length-1] = new WebViewProviderInfo(fallbackPackage, "", 1134 true /* available by default */, true /* fallback */, 1135 new String[]{encodedSignatureString}); 1136 } 1137 1138 setupWithPackages(packages, true /* fallback logic enabled */, 1 /* numRelros */, 1139 false /* isDebuggable */); 1140 1141 // Set package infos 1142 mTestSystemImpl.setPackageInfo( 1143 createPackageInfo(candidatePackage, true /* enabled */, true /* valid */, 1144 true /* installed */, new Signature[]{signature}, 0 /* updateTime */, 1145 false /* hidden */, candidateVersion, false /* isSystemApp */)); 1146 for(int n = 1; n < numSystemPackages + 1; n++) { 1147 mTestSystemImpl.setPackageInfo( 1148 createPackageInfo(systemPackage + n, true /* enabled */, true /* valid */, 1149 true /* installed */, new Signature[]{signature}, 0 /* updateTime */, 1150 false /* hidden */, systemVersions[n-1], true /* isSystemApp */)); 1151 } 1152 if (addFallback) { 1153 mTestSystemImpl.setPackageInfo( 1154 createPackageInfo(fallbackPackage, true /* enabled */, true /* valid */, 1155 true /* installed */, new Signature[]{signature}, 0 /* updateTime */, 1156 false /* hidden */, fallbackVersion, true /* isSystemApp */)); 1157 } 1158 1159 WebViewProviderInfo[] validPackages = mWebViewUpdateServiceImpl.getValidWebViewPackages(); 1160 int expectedNumValidPackages = numSystemPackages; 1161 if (candidateShouldBeValid) { 1162 expectedNumValidPackages++; 1163 } else { 1164 // Ensure the candidate package is not one of the valid packages 1165 for(int n = 0; n < validPackages.length; n++) { 1166 assertFalse(candidatePackage.equals(validPackages[n].packageName)); 1167 } 1168 } 1169 1170 if (fallbackShouldBeValid) { 1171 expectedNumValidPackages += numFallbackPackages; 1172 } else { 1173 // Ensure the fallback package is not one of the valid packages 1174 for(int n = 0; n < validPackages.length; n++) { 1175 assertFalse(fallbackPackage.equals(validPackages[n].packageName)); 1176 } 1177 } 1178 1179 assertEquals(expectedNumValidPackages, validPackages.length); 1180 1181 runWebViewBootPreparationOnMainSync(); 1182 1183 // The non-system package is not available by default so it shouldn't be used here 1184 checkPreparationPhasesForPackage(systemPackage + "1", 1); 1185 1186 // Try explicitly switching to the candidate package 1187 String packageChange = mWebViewUpdateServiceImpl.changeProviderAndSetting(candidatePackage); 1188 if (candidateShouldBeValid) { 1189 assertEquals(candidatePackage, packageChange); 1190 checkPreparationPhasesForPackage(candidatePackage, 1); 1191 } else { 1192 assertEquals(systemPackage + "1", packageChange); 1193 // We didn't change package so the webview preparation won't run here 1194 } 1195 } 1196 1197 /** 1198 * Ensure that the update service does use an uninstalled package when that is the only 1199 * package available. 1200 */ 1201 @Test 1202 public void testWithSingleUninstalledPackage() { 1203 String testPackageName = "test.package.name"; 1204 WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] { 1205 new WebViewProviderInfo(testPackageName, "", 1206 true /*default available*/, false /* fallback */, null)}; 1207 setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */); 1208 mTestSystemImpl.setPackageInfo(createPackageInfo(testPackageName, true /* enabled */, 1209 true /* valid */, false /* installed */)); 1210 1211 runWebViewBootPreparationOnMainSync(); 1212 1213 checkPreparationPhasesForPackage(testPackageName, 1 /* first preparation phase */); 1214 // TODO(gsennton) change this logic to use the code below when we have created a functional 1215 // stub. 1216 //Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged( 1217 // Matchers.anyObject()); 1218 //WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider(); 1219 //assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status); 1220 //assertEquals(null, mWebViewUpdateServiceImpl.getCurrentWebViewPackage()); 1221 } 1222 1223 @Test 1224 public void testNonhiddenPackageUserOverHidden() { 1225 checkVisiblePackageUserOverNonVisible(false /* multiUser*/, PackageRemovalType.HIDE); 1226 checkVisiblePackageUserOverNonVisible(true /* multiUser*/, PackageRemovalType.HIDE); 1227 } 1228 1229 @Test 1230 public void testInstalledPackageUsedOverUninstalled() { 1231 checkVisiblePackageUserOverNonVisible(false /* multiUser*/, PackageRemovalType.UNINSTALL); 1232 checkVisiblePackageUserOverNonVisible(true /* multiUser*/, PackageRemovalType.UNINSTALL); 1233 } 1234 1235 private void checkVisiblePackageUserOverNonVisible(boolean multiUser, 1236 PackageRemovalType removalType) { 1237 assert removalType != PackageRemovalType.DISABLE; 1238 boolean testUninstalled = removalType == PackageRemovalType.UNINSTALL; 1239 boolean testHidden = removalType == PackageRemovalType.HIDE; 1240 String installedPackage = "installedPackage"; 1241 String uninstalledPackage = "uninstalledPackage"; 1242 WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] { 1243 new WebViewProviderInfo(uninstalledPackage, "", true /* available by default */, 1244 false /* fallback */, null), 1245 new WebViewProviderInfo(installedPackage, "", true /* available by default */, 1246 false /* fallback */, null)}; 1247 1248 setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */); 1249 int secondaryUserId = 5; 1250 if (multiUser) { 1251 mTestSystemImpl.addUser(secondaryUserId); 1252 // Install all packages for the primary user. 1253 setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, webviewPackages); 1254 mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo( 1255 installedPackage, true /* enabled */, true /* valid */, true /* installed */)); 1256 // Hide or uninstall the primary package for the second user 1257 mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */, 1258 true /* valid */, (testUninstalled ? false : true) /* installed */, 1259 null /* signatures */, 0 /* updateTime */, (testHidden ? true : false))); 1260 } else { 1261 mTestSystemImpl.setPackageInfo(createPackageInfo(installedPackage, true /* enabled */, 1262 true /* valid */, true /* installed */)); 1263 // Hide or uninstall the primary package 1264 mTestSystemImpl.setPackageInfo(createPackageInfo(uninstalledPackage, true /* enabled */, 1265 true /* valid */, (testUninstalled ? false : true) /* installed */, 1266 null /* signatures */, 0 /* updateTime */, (testHidden ? true : false))); 1267 } 1268 1269 runWebViewBootPreparationOnMainSync(); 1270 1271 checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */); 1272 } 1273 1274 @Test 1275 public void testCantSwitchToHiddenPackage () { 1276 checkCantSwitchToNonVisiblePackage(false /* true == uninstalled, false == hidden */); 1277 } 1278 1279 1280 @Test 1281 public void testCantSwitchToUninstalledPackage () { 1282 checkCantSwitchToNonVisiblePackage(true /* true == uninstalled, false == hidden */); 1283 } 1284 1285 /** 1286 * Ensure that we won't prioritize an uninstalled (or hidden) package even if it is user-chosen. 1287 */ 1288 private void checkCantSwitchToNonVisiblePackage(boolean uninstalledNotHidden) { 1289 boolean testUninstalled = uninstalledNotHidden; 1290 boolean testHidden = !uninstalledNotHidden; 1291 String installedPackage = "installedPackage"; 1292 String uninstalledPackage = "uninstalledPackage"; 1293 WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] { 1294 new WebViewProviderInfo(uninstalledPackage, "", true /* available by default */, 1295 false /* fallback */, null), 1296 new WebViewProviderInfo(installedPackage, "", true /* available by default */, 1297 false /* fallback */, null)}; 1298 1299 setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */); 1300 int secondaryUserId = 412; 1301 mTestSystemImpl.addUser(secondaryUserId); 1302 1303 // Let all packages be installed and enabled for the primary user. 1304 setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, webviewPackages); 1305 // Only uninstall the 'uninstalled package' for the secondary user. 1306 mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(installedPackage, 1307 true /* enabled */, true /* valid */, true /* installed */)); 1308 mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(uninstalledPackage, 1309 true /* enabled */, true /* valid */, !testUninstalled /* installed */, 1310 null /* signatures */, 0 /* updateTime */, testHidden /* hidden */)); 1311 1312 runWebViewBootPreparationOnMainSync(); 1313 1314 checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */); 1315 1316 // ensure that we don't switch to the uninstalled package (it will be used if it becomes 1317 // installed later) 1318 assertEquals(installedPackage, 1319 mWebViewUpdateServiceImpl.changeProviderAndSetting(uninstalledPackage)); 1320 1321 // Ensure both packages are considered valid. 1322 assertEquals(2, mWebViewUpdateServiceImpl.getValidWebViewPackages().length); 1323 1324 1325 // We should only have called onWebViewProviderChanged once (before calling 1326 // changeProviderAndSetting 1327 Mockito.verify(mTestSystemImpl, Mockito.times(1)).onWebViewProviderChanged( 1328 Mockito.argThat(new IsPackageInfoWithName(installedPackage))); 1329 } 1330 1331 @Test 1332 public void testHiddenPackageNotPrioritizedEvenIfChosen() { 1333 checkNonvisiblePackageNotPrioritizedEvenIfChosen( 1334 false /* true == uninstalled, false == hidden */); 1335 } 1336 1337 @Test 1338 public void testUninstalledPackageNotPrioritizedEvenIfChosen() { 1339 checkNonvisiblePackageNotPrioritizedEvenIfChosen( 1340 true /* true == uninstalled, false == hidden */); 1341 } 1342 1343 public void checkNonvisiblePackageNotPrioritizedEvenIfChosen(boolean uninstalledNotHidden) { 1344 boolean testUninstalled = uninstalledNotHidden; 1345 boolean testHidden = !uninstalledNotHidden; 1346 String installedPackage = "installedPackage"; 1347 String uninstalledPackage = "uninstalledPackage"; 1348 WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] { 1349 new WebViewProviderInfo(uninstalledPackage, "", true /* available by default */, 1350 false /* fallback */, null), 1351 new WebViewProviderInfo(installedPackage, "", true /* available by default */, 1352 false /* fallback */, null)}; 1353 1354 setupWithPackages(webviewPackages, true /* fallback logic enabled */, 1 /* numRelros */); 1355 int secondaryUserId = 4; 1356 mTestSystemImpl.addUser(secondaryUserId); 1357 1358 setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, webviewPackages); 1359 mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(installedPackage, 1360 true /* enabled */, true /* valid */, true /* installed */)); 1361 mTestSystemImpl.setPackageInfoForUser(secondaryUserId, createPackageInfo(uninstalledPackage, 1362 true /* enabled */, true /* valid */, 1363 (testUninstalled ? false : true) /* installed */, null /* signatures */, 1364 0 /* updateTime */, (testHidden ? true : false) /* hidden */)); 1365 1366 // Start with the setting pointing to the uninstalled package 1367 mTestSystemImpl.updateUserSetting(null, uninstalledPackage); 1368 1369 runWebViewBootPreparationOnMainSync(); 1370 1371 checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */); 1372 } 1373 1374 @Test 1375 public void testFallbackEnabledIfPrimaryUninstalledSingleUser() { 1376 checkFallbackEnabledIfPrimaryUninstalled(false /* multiUser */); 1377 } 1378 1379 @Test 1380 public void testFallbackEnabledIfPrimaryUninstalledMultiUser() { 1381 checkFallbackEnabledIfPrimaryUninstalled(true /* multiUser */); 1382 } 1383 1384 /** 1385 * Ensures that fallback becomes enabled at boot if the primary package is uninstalled for some 1386 * user. 1387 */ 1388 private void checkFallbackEnabledIfPrimaryUninstalled(boolean multiUser) { 1389 String primaryPackage = "primary"; 1390 String fallbackPackage = "fallback"; 1391 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 1392 new WebViewProviderInfo( 1393 primaryPackage, "", true /* default available */, false /* fallback */, null), 1394 new WebViewProviderInfo( 1395 fallbackPackage, "", true /* default available */, true /* fallback */, null)}; 1396 setupWithPackages(packages, true /* fallback logic enabled */); 1397 int secondaryUserId = 5; 1398 if (multiUser) { 1399 mTestSystemImpl.addUser(secondaryUserId); 1400 // Install all packages for the primary user. 1401 setEnabledAndValidPackageInfosForUser(TestSystemImpl.PRIMARY_USER_ID, packages); 1402 // Only install fallback package for secondary user. 1403 mTestSystemImpl.setPackageInfoForUser(secondaryUserId, 1404 createPackageInfo(primaryPackage, true /* enabled */, 1405 true /* valid */, false /* installed */)); 1406 mTestSystemImpl.setPackageInfoForUser(secondaryUserId, 1407 createPackageInfo(fallbackPackage, false /* enabled */, 1408 true /* valid */, true /* installed */)); 1409 } else { 1410 mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */, 1411 true /* valid */, false /* installed */)); 1412 mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, false /* enabled */, 1413 true /* valid */, true /* installed */)); 1414 } 1415 1416 runWebViewBootPreparationOnMainSync(); 1417 // Verify that we enable the fallback package 1418 Mockito.verify(mTestSystemImpl).enablePackageForAllUsers( 1419 Mockito.anyObject(), Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */); 1420 1421 checkPreparationPhasesForPackage(fallbackPackage, 1 /* first preparation phase */); 1422 } 1423 1424 @Test 1425 public void testPreparationRunsIffNewPackage() { 1426 String primaryPackage = "primary"; 1427 String fallbackPackage = "fallback"; 1428 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 1429 new WebViewProviderInfo( 1430 primaryPackage, "", true /* default available */, false /* fallback */, null), 1431 new WebViewProviderInfo( 1432 fallbackPackage, "", true /* default available */, true /* fallback */, null)}; 1433 setupWithPackages(packages, true /* fallback logic enabled */); 1434 mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */, 1435 true /* valid */, true /* installed */, null /* signatures */, 1436 10 /* lastUpdateTime*/ )); 1437 mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */, 1438 true /* valid */, true /* installed */)); 1439 1440 runWebViewBootPreparationOnMainSync(); 1441 1442 checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation phase */); 1443 Mockito.verify(mTestSystemImpl, Mockito.times(1)).enablePackageForUser( 1444 Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */, 1445 Matchers.anyInt() /* user */); 1446 1447 1448 mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage, 1449 WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0 /* userId */); 1450 mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage, 1451 WebViewUpdateService.PACKAGE_ADDED_REPLACED, 1 /* userId */); 1452 mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage, 1453 WebViewUpdateService.PACKAGE_ADDED_REPLACED, 2 /* userId */); 1454 // package still has the same update-time so we shouldn't run preparation here 1455 Mockito.verify(mTestSystemImpl, Mockito.times(1)).onWebViewProviderChanged( 1456 Mockito.argThat(new IsPackageInfoWithName(primaryPackage))); 1457 Mockito.verify(mTestSystemImpl, Mockito.times(1)).enablePackageForUser( 1458 Mockito.eq(fallbackPackage), Mockito.eq(false) /* enable */, 1459 Matchers.anyInt() /* user */); 1460 1461 // Ensure we can still load the package 1462 WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider(); 1463 assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status); 1464 assertEquals(primaryPackage, response.packageInfo.packageName); 1465 1466 1467 mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */, 1468 true /* valid */, true /* installed */, null /* signatures */, 1469 20 /* lastUpdateTime*/ )); 1470 mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage, 1471 WebViewUpdateService.PACKAGE_ADDED_REPLACED, 0); 1472 // The package has now changed - ensure that we have run the preparation phase a second time 1473 checkPreparationPhasesForPackage(primaryPackage, 2 /* second preparation phase */); 1474 1475 1476 mTestSystemImpl.setPackageInfo(createPackageInfo(primaryPackage, true /* enabled */, 1477 true /* valid */, true /* installed */, null /* signatures */, 1478 50 /* lastUpdateTime*/ )); 1479 // Receive intent for different user 1480 mWebViewUpdateServiceImpl.packageStateChanged(primaryPackage, 1481 WebViewUpdateService.PACKAGE_ADDED_REPLACED, 2); 1482 1483 checkPreparationPhasesForPackage(primaryPackage, 3 /* third preparation phase */); 1484 } 1485 1486 @Test 1487 public void testGetCurrentWebViewPackage() { 1488 PackageInfo firstPackage = createPackageInfo("first", true /* enabled */, 1489 true /* valid */, true /* installed */); 1490 firstPackage.versionCode = 100; 1491 firstPackage.versionName = "first package version"; 1492 WebViewProviderInfo[] packages = new WebViewProviderInfo[] { 1493 new WebViewProviderInfo(firstPackage.packageName, "", true, false, null)}; 1494 setupWithPackages(packages, true); 1495 mTestSystemImpl.setPackageInfo(firstPackage); 1496 1497 runWebViewBootPreparationOnMainSync(); 1498 1499 Mockito.verify(mTestSystemImpl).onWebViewProviderChanged( 1500 Mockito.argThat(new IsPackageInfoWithName(firstPackage.packageName))); 1501 1502 mWebViewUpdateServiceImpl.notifyRelroCreationCompleted(); 1503 1504 // Ensure the API is correct before running waitForAndGetProvider 1505 assertEquals(firstPackage.packageName, 1506 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName); 1507 assertEquals(firstPackage.versionCode, 1508 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionCode); 1509 assertEquals(firstPackage.versionName, 1510 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionName); 1511 1512 WebViewProviderResponse response = mWebViewUpdateServiceImpl.waitForAndGetProvider(); 1513 assertEquals(WebViewFactory.LIBLOAD_SUCCESS, response.status); 1514 assertEquals(firstPackage.packageName, response.packageInfo.packageName); 1515 1516 // Ensure the API is still correct after running waitForAndGetProvider 1517 assertEquals(firstPackage.packageName, 1518 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName); 1519 assertEquals(firstPackage.versionCode, 1520 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionCode); 1521 assertEquals(firstPackage.versionName, 1522 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionName); 1523 } 1524} 1525