1 /* 2 * Copyright (C) 2019 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 17 package com.android.tests.stagedinstall.host; 18 19 import static com.android.cts.shim.lib.ShimPackage.SHIM_APEX_PACKAGE_NAME; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.hamcrest.CoreMatchers.endsWith; 24 import static org.hamcrest.CoreMatchers.equalTo; 25 import static org.hamcrest.CoreMatchers.not; 26 import static org.junit.Assume.assumeFalse; 27 import static org.junit.Assume.assumeThat; 28 import static org.junit.Assume.assumeTrue; 29 30 import android.cts.install.lib.host.InstallUtilsHost; 31 import android.platform.test.annotations.LargeTest; 32 33 import com.android.apex.ApexInfo; 34 import com.android.apex.XmlParser; 35 import com.android.ddmlib.Log; 36 import com.android.tradefed.device.DeviceNotAvailableException; 37 import com.android.tradefed.device.ITestDevice; 38 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 39 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 40 41 42 import org.junit.After; 43 import org.junit.Before; 44 import org.junit.Rule; 45 import org.junit.Test; 46 import org.junit.rules.TestWatcher; 47 import org.junit.runner.Description; 48 import org.junit.runner.RunWith; 49 50 import java.io.File; 51 import java.io.FileInputStream; 52 import java.util.List; 53 import java.util.Set; 54 import java.util.stream.Collectors; 55 56 @RunWith(DeviceJUnit4ClassRunner.class) 57 public class StagedInstallTest extends BaseHostJUnit4Test { 58 59 private static final String TAG = "StagedInstallTest"; 60 61 private static final String PACKAGE_NAME = "com.android.tests.stagedinstall"; 62 63 private static final String BROADCAST_RECEIVER_COMPONENT = PACKAGE_NAME + "/" 64 + PACKAGE_NAME + ".LauncherActivity"; 65 66 private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); 67 68 private String mDefaultLauncher = null; 69 70 @Rule 71 public final FailedTestLogHook mFailedTestLogHook = new FailedTestLogHook(this); 72 73 /** 74 * Runs the given phase of a test by calling into the device. 75 * Throws an exception if the test phase fails. 76 * <p> 77 * For example, <code>runPhase("testInstallStagedApkCommit");</code> 78 */ runPhase(String phase)79 private void runPhase(String phase) throws Exception { 80 assertThat(runDeviceTests(PACKAGE_NAME, 81 "com.android.tests.stagedinstall.StagedInstallTest", 82 phase)).isTrue(); 83 } 84 85 // We do not assert the success of cleanup phase since it might fail due to flaky reasons. cleanUp()86 private void cleanUp() throws Exception { 87 try { 88 runDeviceTests(PACKAGE_NAME, 89 "com.android.tests.stagedinstall.StagedInstallTest", 90 "cleanUp"); 91 } catch (AssertionError e) { 92 Log.e(TAG, e); 93 } 94 } 95 96 @Before setUp()97 public void setUp() throws Exception { 98 cleanUp(); 99 mHostUtils.uninstallShimApexIfNecessary(); 100 storeDefaultLauncher(); 101 } 102 103 @After tearDown()104 public void tearDown() throws Exception { 105 cleanUp(); 106 mHostUtils.uninstallShimApexIfNecessary(); 107 setDefaultLauncher(mDefaultLauncher); 108 } 109 110 /** 111 * Tests for staged install involving only one apk. 112 */ 113 @Test 114 @LargeTest testInstallStagedApk()115 public void testInstallStagedApk() throws Exception { 116 assumeSystemUser(); 117 118 setDefaultLauncher(BROADCAST_RECEIVER_COMPONENT); 119 runPhase("testInstallStagedApk_Commit"); 120 getDevice().reboot(); 121 runPhase("testInstallStagedApk_VerifyPostReboot"); 122 runPhase("testInstallStagedApk_AbandonSessionIsNoop"); 123 } 124 125 @Test testFailInstallIfNoPermission()126 public void testFailInstallIfNoPermission() throws Exception { 127 runPhase("testFailInstallIfNoPermission"); 128 } 129 130 @Test 131 @LargeTest testAbandonStagedApkBeforeReboot()132 public void testAbandonStagedApkBeforeReboot() throws Exception { 133 runPhase("testAbandonStagedApkBeforeReboot_CommitAndAbandon"); 134 getDevice().reboot(); 135 runPhase("testAbandonStagedApkBeforeReboot_VerifyPostReboot"); 136 } 137 138 @Test 139 @LargeTest testAbandonStagedApkBeforeReady()140 public void testAbandonStagedApkBeforeReady() throws Exception { 141 runPhase("testAbandonStagedApkBeforeReady_CommitAndAbandon"); 142 getDevice().reboot(); 143 runPhase("testAbandonStagedApkBeforeReady_VerifyPostReboot"); 144 } 145 146 @Test testStageAnotherSessionImmediatelyAfterAbandon()147 public void testStageAnotherSessionImmediatelyAfterAbandon() throws Exception { 148 runPhase("testStageAnotherSessionImmediatelyAfterAbandon"); 149 } 150 151 @Test testStageAnotherSessionImmediatelyAfterAbandonMultiPackage()152 public void testStageAnotherSessionImmediatelyAfterAbandonMultiPackage() throws Exception { 153 runPhase("testStageAnotherSessionImmediatelyAfterAbandonMultiPackage"); 154 } 155 156 @Test testNoSessionUpdatedBroadcastSentForStagedSessionAbandon()157 public void testNoSessionUpdatedBroadcastSentForStagedSessionAbandon() throws Exception { 158 runPhase("testNoSessionUpdatedBroadcastSentForStagedSessionAbandon"); 159 } 160 161 @Test 162 @LargeTest testInstallMultipleStagedApks()163 public void testInstallMultipleStagedApks() throws Exception { 164 assumeSystemUser(); 165 166 setDefaultLauncher(BROADCAST_RECEIVER_COMPONENT); 167 runPhase("testInstallMultipleStagedApks_Commit"); 168 getDevice().reboot(); 169 runPhase("testInstallMultipleStagedApks_VerifyPostReboot"); 170 } 171 assumeSystemUser()172 private void assumeSystemUser() throws Exception { 173 String systemUser = "0"; 174 assumeThat("Current user is not system user", 175 getDevice().executeShellCommand("am get-current-user").trim(), equalTo(systemUser)); 176 } 177 178 @Test testGetActiveStagedSessions()179 public void testGetActiveStagedSessions() throws Exception { 180 assumeTrue("Device does not support file-system checkpoint", 181 mHostUtils.isCheckpointSupported()); 182 183 runPhase("testGetActiveStagedSessions"); 184 } 185 186 /** 187 * Verifies that active staged session fulfils conditions stated at 188 * {@link PackageInstaller.SessionInfo#isStagedSessionActive} 189 */ 190 @Test testIsStagedSessionActive()191 public void testIsStagedSessionActive() throws Exception { 192 runPhase("testIsStagedSessionActive"); 193 } 194 195 @Test testGetActiveStagedSessionsNoSessionActive()196 public void testGetActiveStagedSessionsNoSessionActive() throws Exception { 197 runPhase("testGetActiveStagedSessionsNoSessionActive"); 198 } 199 200 @Test testGetActiveStagedSessions_MultiApkSession()201 public void testGetActiveStagedSessions_MultiApkSession() throws Exception { 202 assumeTrue("Device does not support file-system checkpoint", 203 mHostUtils.isCheckpointSupported()); 204 205 runPhase("testGetActiveStagedSessions_MultiApkSession"); 206 } 207 208 @Test testStagedInstallDowngrade_DowngradeNotRequested_Fails()209 public void testStagedInstallDowngrade_DowngradeNotRequested_Fails() throws Exception { 210 runPhase("testStagedInstallDowngrade_DowngradeNotRequested_Fails_Commit"); 211 } 212 213 @Test 214 @LargeTest testStagedInstallDowngrade_DowngradeRequested_DebugBuild()215 public void testStagedInstallDowngrade_DowngradeRequested_DebugBuild() throws Exception { 216 assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user"))); 217 218 runPhase("testStagedInstallDowngrade_DowngradeRequested_Commit"); 219 getDevice().reboot(); 220 runPhase("testStagedInstallDowngrade_DowngradeRequested_DebugBuild_VerifyPostReboot"); 221 } 222 223 @Test testStagedInstallDowngrade_DowngradeRequested_UserBuild()224 public void testStagedInstallDowngrade_DowngradeRequested_UserBuild() throws Exception { 225 assumeThat(getDevice().getBuildFlavor(), endsWith("-user")); 226 assumeFalse("Device is debuggable", isDebuggable()); 227 228 runPhase("testStagedInstallDowngrade_DowngradeRequested_Fails_Commit"); 229 } 230 231 @Test testShimApexShouldPreInstalledIfUpdatingApexIsSupported()232 public void testShimApexShouldPreInstalledIfUpdatingApexIsSupported() throws Exception { 233 final ITestDevice.ApexInfo shimApex = mHostUtils.getShimApex().orElseThrow( 234 () -> new AssertionError("Can't find " + SHIM_APEX_PACKAGE_NAME) 235 ); 236 assertThat(shimApex.versionCode).isEqualTo(1); 237 } 238 239 @Test 240 @LargeTest testInstallStagedApex()241 public void testInstallStagedApex() throws Exception { 242 setDefaultLauncher(BROADCAST_RECEIVER_COMPONENT); 243 runPhase("testInstallStagedApex_Commit"); 244 getDevice().reboot(); 245 runPhase("testInstallStagedApex_VerifyPostReboot"); 246 } 247 248 @Test 249 // Don't mark as @LargeTest since we want at least one test to install apex during pre-submit. testInstallStagedApexAndApk()250 public void testInstallStagedApexAndApk() throws Exception { 251 assumeSystemUser(); 252 253 setDefaultLauncher(BROADCAST_RECEIVER_COMPONENT); 254 runPhase("testInstallStagedApexAndApk_Commit"); 255 getDevice().reboot(); 256 runPhase("testInstallStagedApexAndApk_VerifyPostReboot"); 257 } 258 259 @Test testsFailsNonStagedApexInstall()260 public void testsFailsNonStagedApexInstall() throws Exception { 261 runPhase("testsFailsNonStagedApexInstall"); 262 } 263 264 @Test testInstallStagedNonPreInstalledApex_Fails()265 public void testInstallStagedNonPreInstalledApex_Fails() throws Exception { 266 runPhase("testInstallStagedNonPreInstalledApex_Fails"); 267 } 268 269 @Test testInstallStagedDifferentPackageNameWithInstalledApex_Fails()270 public void testInstallStagedDifferentPackageNameWithInstalledApex_Fails() throws Exception { 271 runPhase("testInstallStagedDifferentPackageNameWithInstalledApex_Fails"); 272 } 273 274 @Test 275 @LargeTest testStageApkWithSameNameAsApexShouldFail()276 public void testStageApkWithSameNameAsApexShouldFail() throws Exception { 277 runPhase("testStageApkWithSameNameAsApexShouldFail_Commit"); 278 getDevice().reboot(); 279 runPhase("testStageApkWithSameNameAsApexShouldFail_VerifyPostReboot"); 280 } 281 282 @Test testNonStagedInstallApkWithSameNameAsApexShouldFail()283 public void testNonStagedInstallApkWithSameNameAsApexShouldFail() throws Exception { 284 runPhase("testNonStagedInstallApkWithSameNameAsApexShouldFail"); 285 } 286 287 @Test 288 @LargeTest testStagedInstallDowngradeApex_DowngradeNotRequested_Fails()289 public void testStagedInstallDowngradeApex_DowngradeNotRequested_Fails() throws Exception { 290 installV3Apex(); 291 runPhase("testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_Commit"); 292 getDevice().reboot(); 293 runPhase("testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_VerifyPostReboot"); 294 } 295 296 @Test 297 @LargeTest testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild()298 public void testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild() throws Exception { 299 assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user"))); 300 301 installV3Apex(); 302 runPhase("testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_Commit"); 303 getDevice().reboot(); 304 runPhase("testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_VerifyPostReboot"); 305 } 306 307 @Test 308 @LargeTest testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails()309 public void testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails() 310 throws Exception { 311 assumeThat(getDevice().getBuildFlavor(), endsWith("-user")); 312 assumeFalse("Device is debuggable", isDebuggable()); 313 314 installV3Apex(); 315 runPhase("testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_Commit"); 316 getDevice().reboot(); 317 runPhase("testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_" 318 + "VerifyPostReboot"); 319 } 320 321 @Test 322 @LargeTest testStagedInstallDowngradeApexToSystemVersion_DebugBuild()323 public void testStagedInstallDowngradeApexToSystemVersion_DebugBuild() throws Exception { 324 assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user"))); 325 326 installV2Apex(); 327 runPhase("testStagedInstallDowngradeApexToSystemVersion_DebugBuild_Commit"); 328 getDevice().reboot(); 329 runPhase("testStagedInstallDowngradeApexToSystemVersion_DebugBuild_VerifyPostReboot"); 330 } 331 332 @Test 333 @LargeTest testInstallStagedApex_SameGrade()334 public void testInstallStagedApex_SameGrade() throws Exception { 335 installV3Apex(); 336 ApexInfo shim1 = 337 readApexInfoList().stream() 338 .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME)) 339 .filter(ApexInfo::getIsActive) 340 .findAny() 341 .orElseThrow(() -> 342 new AssertionError( 343 "No active version of " + SHIM_APEX_PACKAGE_NAME 344 + " found in /apex/apex-info-list.xml")); 345 346 installV3Apex(); 347 ApexInfo shim2 = 348 readApexInfoList().stream() 349 .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME)) 350 .filter(ApexInfo::getIsActive) 351 .findAny() 352 .orElseThrow(() -> 353 new AssertionError( 354 "No active version of " + SHIM_APEX_PACKAGE_NAME 355 + " found in /apex/apex-info-list.xml")); 356 assertThat(shim1.getLastUpdateMillis()).isNotEqualTo(shim2.getLastUpdateMillis()); 357 } 358 359 @Test 360 @LargeTest testInstallStagedApex_SameGrade_NewOneWins()361 public void testInstallStagedApex_SameGrade_NewOneWins() throws Exception { 362 installV2Apex(); 363 364 runPhase("testInstallStagedApex_SameGrade_NewOneWins_Commit"); 365 getDevice().reboot(); 366 runPhase("testInstallStagedApex_SameGrade_NewOneWins_VerifyPostReboot"); 367 } 368 installV2Apex()369 private void installV2Apex()throws Exception { 370 runPhase("testInstallV2Apex_Commit"); 371 getDevice().reboot(); 372 runPhase("testInstallV2Apex_VerifyPostReboot"); 373 } 374 installV2SignedBobApex()375 private void installV2SignedBobApex() throws Exception { 376 runPhase("testInstallV2SignedBobApex_Commit"); 377 getDevice().reboot(); 378 runPhase("testInstallV2SignedBobApex_VerifyPostReboot"); 379 } 380 installV3Apex()381 private void installV3Apex()throws Exception { 382 runPhase("testInstallV3Apex_Commit"); 383 getDevice().reboot(); 384 runPhase("testInstallV3Apex_VerifyPostReboot"); 385 } 386 387 @Test testFailsInvalidApexInstall()388 public void testFailsInvalidApexInstall() throws Exception { 389 runPhase("testFailsInvalidApexInstall_Commit"); 390 runPhase("testFailsInvalidApexInstall_AbandonSessionIsNoop"); 391 } 392 393 @Test testStagedApkSessionCallbacks()394 public void testStagedApkSessionCallbacks() throws Exception { 395 runPhase("testStagedApkSessionCallbacks"); 396 } 397 398 @Test 399 @LargeTest testInstallStagedApexWithoutApexSuffix()400 public void testInstallStagedApexWithoutApexSuffix() throws Exception { 401 runPhase("testInstallStagedApexWithoutApexSuffix_Commit"); 402 getDevice().reboot(); 403 runPhase("testInstallStagedApexWithoutApexSuffix_VerifyPostReboot"); 404 } 405 406 @Test testRejectsApexDifferentCertificate()407 public void testRejectsApexDifferentCertificate() throws Exception { 408 runPhase("testRejectsApexDifferentCertificate"); 409 } 410 411 /** 412 * Tests for staged install involving rotated keys. 413 * 414 * Here alice means the original default key that cts.shim.v1 package was signed with and 415 * bob is the new key alice rotates to. Where ambiguous, we will refer keys as alice and bob 416 * instead of "old key" and "new key". 417 * 418 * By default, rotated keys have rollback capability enabled for old keys. When we remove 419 * rollback capability from a key, it is called "Distrusting Event" and the distrusted key can 420 * not update the app anymore. 421 */ 422 423 // Should not be able to update with a key that has not been rotated. 424 @Test testUpdateWithDifferentKeyButNoRotation()425 public void testUpdateWithDifferentKeyButNoRotation() throws Exception { 426 runPhase("testUpdateWithDifferentKeyButNoRotation"); 427 } 428 429 // Should be able to update with a key that has been rotated. 430 @Test 431 @LargeTest testUpdateWithDifferentKey()432 public void testUpdateWithDifferentKey() throws Exception { 433 runPhase("testUpdateWithDifferentKey_Commit"); 434 getDevice().reboot(); 435 runPhase("testUpdateWithDifferentKey_VerifyPostReboot"); 436 } 437 438 // Should not be able to update with a key that is no longer trusted (i.e, has no 439 // rollback capability) 440 @Test 441 @LargeTest testUntrustedOldKeyIsRejected()442 public void testUntrustedOldKeyIsRejected() throws Exception { 443 installV2SignedBobApex(); 444 runPhase("testUntrustedOldKeyIsRejected"); 445 } 446 447 // Should be able to update with an old key which is trusted 448 @Test 449 @LargeTest testTrustedOldKeyIsAccepted()450 public void testTrustedOldKeyIsAccepted() throws Exception { 451 runPhase("testTrustedOldKeyIsAccepted_Commit"); 452 getDevice().reboot(); 453 runPhase("testTrustedOldKeyIsAccepted_CommitPostReboot"); 454 getDevice().reboot(); 455 runPhase("testTrustedOldKeyIsAccepted_VerifyPostReboot"); 456 } 457 458 // Should be able to update further with rotated key 459 @Test 460 @LargeTest testAfterRotationNewKeyCanUpdateFurther()461 public void testAfterRotationNewKeyCanUpdateFurther() throws Exception { 462 installV2SignedBobApex(); 463 runPhase("testAfterRotationNewKeyCanUpdateFurther_CommitPostReboot"); 464 getDevice().reboot(); 465 runPhase("testAfterRotationNewKeyCanUpdateFurther_VerifyPostReboot"); 466 } 467 468 @Test 469 @LargeTest testAfterRotationNewKeyCanUpdateFurtherWithoutLineage()470 public void testAfterRotationNewKeyCanUpdateFurtherWithoutLineage() throws Exception { 471 installV2SignedBobApex(); 472 runPhase("testAfterRotationNewKeyCanUpdateFurtherWithoutLineage"); 473 } 474 475 /** 476 * Tests for staging and installing multiple staged sessions. 477 */ 478 479 // Should fail to stage multiple sessions when check-point is not available 480 @Test testFailStagingMultipleSessionsIfNoCheckPoint()481 public void testFailStagingMultipleSessionsIfNoCheckPoint() throws Exception { 482 assumeFalse("Device supports file-system checkpoint", 483 mHostUtils.isCheckpointSupported()); 484 485 runPhase("testFailStagingMultipleSessionsIfNoCheckPoint"); 486 } 487 488 @Test testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk()489 public void testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk() throws Exception { 490 assumeTrue("Device does not support file-system checkpoint", 491 mHostUtils.isCheckpointSupported()); 492 493 runPhase("testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk"); 494 } 495 496 @Test testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk()497 public void testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk() 498 throws Exception { 499 assumeTrue("Device does not support file-system checkpoint", 500 mHostUtils.isCheckpointSupported()); 501 502 runPhase("testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk"); 503 } 504 505 @Test testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk()506 public void testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk() throws Exception { 507 assumeTrue("Device does not support file-system checkpoint", 508 mHostUtils.isCheckpointSupported()); 509 510 runPhase("testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk"); 511 } 512 513 // Test for installing multiple staged sessions at the same time 514 @Test 515 @LargeTest testMultipleStagedInstall_ApkOnly()516 public void testMultipleStagedInstall_ApkOnly() throws Exception { 517 assumeTrue("Device does not support file-system checkpoint", 518 mHostUtils.isCheckpointSupported()); 519 520 runPhase("testMultipleStagedInstall_ApkOnly_Commit"); 521 getDevice().reboot(); 522 runPhase("testMultipleStagedInstall_ApkOnly_VerifyPostReboot"); 523 } 524 525 // If apk installation fails in one staged session, then all staged session should fail. 526 @Test 527 @LargeTest testInstallMultipleStagedSession_PartialFail_ApkOnly()528 public void testInstallMultipleStagedSession_PartialFail_ApkOnly() throws Exception { 529 assumeTrue("Device does not support file-system checkpoint", 530 mHostUtils.isCheckpointSupported()); 531 532 runPhase("testInstallMultipleStagedSession_PartialFail_ApkOnly_Commit"); 533 getDevice().reboot(); 534 runPhase("testInstallMultipleStagedSession_PartialFail_ApkOnly_VerifyPostReboot"); 535 } 536 537 // Failure reason of staged install should be be persisted for single sessions 538 @Test 539 @LargeTest testFailureReasonPersists_SingleSession()540 public void testFailureReasonPersists_SingleSession() throws Exception { 541 assumeTrue("Device does not support file-system checkpoint", 542 mHostUtils.isCheckpointSupported()); 543 544 runPhase("testFailureReasonPersists_SingleSession_Commit"); 545 getDevice().reboot(); 546 runPhase("testFailureReasonPersists_SingleSession_VerifyPostReboot"); 547 } 548 549 // Failure reason of staged install should be be persisted for multi session staged install 550 @Test 551 @LargeTest testFailureReasonPersists_MultiSession()552 public void testFailureReasonPersists_MultiSession() throws Exception { 553 assumeTrue("Device does not support file-system checkpoint", 554 mHostUtils.isCheckpointSupported()); 555 556 runPhase("testFailureReasonPersists_MultipleSession_Commit"); 557 getDevice().reboot(); 558 runPhase("testFailureReasonPersists_MultipleSession_VerifyPostReboot"); 559 } 560 561 @Test 562 @LargeTest testSamegradeSystemApex()563 public void testSamegradeSystemApex() throws Exception { 564 runPhase("testSamegradeSystemApex_Commit"); 565 getDevice().reboot(); 566 runPhase("testSamegradeSystemApex_VerifyPostReboot"); 567 } 568 569 @Test 570 @LargeTest testInstallApkChangingFingerprint()571 public void testInstallApkChangingFingerprint() throws Exception { 572 try { 573 getDevice().executeShellCommand("setprop persist.pm.mock-upgrade true"); 574 runPhase("testInstallApkChangingFingerprint"); 575 getDevice().reboot(); 576 runPhase("testInstallApkChangingFingerprint_VerifyAborted"); 577 } finally { 578 getDevice().executeShellCommand("setprop persist.pm.mock-upgrade false"); 579 } 580 } 581 582 @Test 583 @LargeTest testInstallStagedNoHashtreeApex()584 public void testInstallStagedNoHashtreeApex() throws Exception { 585 runPhase("testInstallStagedNoHashtreeApex_Commit"); 586 getDevice().reboot(); 587 runPhase("testInstallStagedNoHashtreeApex_VerifyPostReboot"); 588 } 589 590 /** 591 * Should fail to verify apex targeting older dev sdk 592 */ 593 @Test testApexTargetingOldDevSdkFailsVerification()594 public void testApexTargetingOldDevSdkFailsVerification() throws Exception { 595 runPhase("testApexTargetingOldDevSdkFailsVerification"); 596 } 597 598 /** 599 * Apex should fail to install if apk-in-apex fails to get scanned 600 */ 601 @Test 602 @LargeTest testApexFailsToInstallIfApkInApexFailsToScan()603 public void testApexFailsToInstallIfApkInApexFailsToScan() throws Exception { 604 runPhase("testApexFailsToInstallIfApkInApexFailsToScan_Commit"); 605 getDevice().reboot(); 606 runPhase("testApexFailsToInstallIfApkInApexFailsToScan_VerifyPostReboot"); 607 } 608 609 @Test testCorruptedApexFailsVerification_b146895998()610 public void testCorruptedApexFailsVerification_b146895998() throws Exception { 611 runPhase("testCorruptedApexFailsVerification_b146895998"); 612 } 613 614 /** 615 * Should fail to pass apk signature check 616 */ 617 @Test testApexWithUnsignedApkFailsVerification()618 public void testApexWithUnsignedApkFailsVerification() throws Exception { 619 runPhase("testApexWithUnsignedApkFailsVerification"); 620 } 621 622 /** 623 * Should fail to verify apex signed payload with a different key 624 */ 625 @Test testApexSignPayloadWithDifferentKeyFailsVerification()626 public void testApexSignPayloadWithDifferentKeyFailsVerification() throws Exception { 627 runPhase("testApexSignPayloadWithDifferentKeyFailsVerification"); 628 } 629 630 /** 631 * Should fail to verify apex with unsigned payload 632 */ 633 @Test testApexWithUnsignedPayloadFailsVerification()634 public void testApexWithUnsignedPayloadFailsVerification() throws Exception { 635 runPhase("testApexWithUnsignedPayloadFailsVerification"); 636 } 637 638 @Test 639 @LargeTest testApexSetsUpdatedSystemAppFlag()640 public void testApexSetsUpdatedSystemAppFlag() throws Exception { 641 runPhase("testApexSetsUpdatedSystemAppFlag_preUpdate"); 642 installV2Apex(); 643 runPhase("testApexSetsUpdatedSystemAppFlag_postUpdate"); 644 } 645 646 /** 647 * Test non-priv apps cannot access /data/app-staging folder contents 648 */ 649 @Test testAppStagingDirCannotBeReadByNonPrivApps()650 public void testAppStagingDirCannotBeReadByNonPrivApps() throws Exception { 651 runPhase("testAppStagingDirCannotBeReadByNonPrivApps"); 652 } 653 654 @Test 655 @LargeTest testUpdatedApexFromDataApexActiveCanBePulled()656 public void testUpdatedApexFromDataApexActiveCanBePulled() throws Exception { 657 installV2Apex(); 658 659 final ITestDevice.ApexInfo shimApex = mHostUtils.getShimApex().orElseThrow( 660 () -> new AssertionError("Can't find " + SHIM_APEX_PACKAGE_NAME) 661 ); 662 663 assertThat(shimApex.sourceDir).startsWith("/data/apex/active"); 664 assertThat(getDevice().pullFile(shimApex.sourceDir)).isNotNull(); 665 } 666 667 @Test testApexInfoList()668 public void testApexInfoList() throws Exception { 669 // Check that content of /apex/apex-info-list.xml matches output of 670 // `adb shell pm list packages --apex-only --show-versioncode -f`. 671 List<ApexInfo> apexInfoList = 672 readApexInfoList().stream() 673 .filter(a -> a.getIsActive()) 674 .collect(Collectors.toList()); 675 Set<ITestDevice.ApexInfo> activeApexes = getDevice().getActiveApexes(); 676 assertThat(apexInfoList.size()).isEqualTo(activeApexes.size()); 677 for (ITestDevice.ApexInfo apex : activeApexes) { 678 // Note: we can't assert equality of the apex.name and apexInfo.getModuleName() since 679 // they represent different concepts (the former is package name, while latter is apex 680 // module name) 681 List<ApexInfo> temp = 682 apexInfoList.stream() 683 .filter(a -> a.getModulePath().equals(apex.sourceDir)) 684 .collect(Collectors.toList()); 685 assertThat(temp).hasSize(1); 686 ApexInfo apexInfo = temp.get(0); 687 assertThat(apexInfo.getModulePath()).isEqualTo(apex.sourceDir); 688 assertThat(apexInfo.getVersionCode()).isEqualTo(apex.versionCode); 689 assertThat(apexInfo.getIsActive()).isTrue(); 690 assertThat(apexInfo.getLastUpdateMillis()).isGreaterThan(0); 691 } 692 } 693 694 @Test 695 @LargeTest testApexInfoListAfterUpdate()696 public void testApexInfoListAfterUpdate() throws Exception { 697 ApexInfo shimBeforeUpdate = getShimApexInfo(); 698 699 installV2Apex(); 700 701 List<ApexInfo> shimApexInfo = 702 readApexInfoList().stream() 703 .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME)) 704 .collect(Collectors.toList()); 705 706 assertThat(shimApexInfo).hasSize(2); 707 708 ApexInfo factoryShimApexInfo = 709 shimApexInfo.stream() 710 .filter(ApexInfo::getIsFactory) 711 .findAny() 712 .orElseThrow(() -> 713 new AssertionError( 714 "No factory version of " + SHIM_APEX_PACKAGE_NAME 715 + " found in /apex/apex-info-list.xml")); 716 assertThat(factoryShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME); 717 assertThat(factoryShimApexInfo.getIsActive()).isFalse(); 718 assertThat(factoryShimApexInfo.getIsFactory()).isTrue(); 719 assertThat(factoryShimApexInfo.getVersionCode()).isEqualTo(1); 720 assertThat(factoryShimApexInfo.getModulePath()) 721 .isEqualTo(factoryShimApexInfo.getPreinstalledModulePath()); 722 assertThat(factoryShimApexInfo.getLastUpdateMillis()) 723 .isEqualTo(shimBeforeUpdate.getLastUpdateMillis()); 724 725 ApexInfo activeShimApexInfo = 726 shimApexInfo.stream() 727 .filter(ApexInfo::getIsActive) 728 .findAny() 729 .orElseThrow(() -> 730 new AssertionError( 731 "No active version of " + SHIM_APEX_PACKAGE_NAME 732 + " found in /apex/apex-info-list.xml")); 733 assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME); 734 assertThat(activeShimApexInfo.getIsActive()).isTrue(); 735 assertThat(activeShimApexInfo.getIsFactory()).isFalse(); 736 assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(2); 737 assertThat(activeShimApexInfo.getPreinstalledModulePath()) 738 .isEqualTo(factoryShimApexInfo.getModulePath()); 739 assertThat(activeShimApexInfo.getLastUpdateMillis()) 740 .isNotEqualTo(shimBeforeUpdate.getLastUpdateMillis()); 741 } 742 743 @Test 744 @LargeTest testRebootlessUpdate()745 public void testRebootlessUpdate() throws Exception { 746 runPhase("testRebootlessUpdate"); 747 ApexInfo activeShimApexInfo = getActiveShimApexInfo(); 748 assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME); 749 assertThat(activeShimApexInfo.getIsActive()).isTrue(); 750 assertThat(activeShimApexInfo.getIsFactory()).isFalse(); 751 assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(2); 752 } 753 754 @Test 755 @LargeTest testRebootlessUpdate_fromV2ToV3_sameBoot()756 public void testRebootlessUpdate_fromV2ToV3_sameBoot() throws Exception { 757 runPhase("testRebootlessUpdate"); 758 runPhase("testRebootlessUpdate_installV3"); 759 ApexInfo activeShimApexInfo = getActiveShimApexInfo(); 760 assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME); 761 assertThat(activeShimApexInfo.getIsActive()).isTrue(); 762 assertThat(activeShimApexInfo.getIsFactory()).isFalse(); 763 assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(3); 764 } 765 766 @Test 767 @LargeTest testRebootlessUpdate_fromV2ToV3_rebootInBetween()768 public void testRebootlessUpdate_fromV2ToV3_rebootInBetween() throws Exception { 769 runPhase("testRebootlessUpdate"); 770 getDevice().reboot(); 771 runPhase("testRebootlessUpdate_installV3"); 772 ApexInfo activeShimApexInfo = getActiveShimApexInfo(); 773 assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME); 774 assertThat(activeShimApexInfo.getIsActive()).isTrue(); 775 assertThat(activeShimApexInfo.getIsFactory()).isFalse(); 776 assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(3); 777 } 778 779 @Test 780 @LargeTest testRebootlessUpdate_downgrage_fails()781 public void testRebootlessUpdate_downgrage_fails() throws Exception { 782 runPhase("testRebootlessUpdate_installV3"); 783 runPhase("testRebootlessUpdate_downgradeToV2_fails"); 784 } 785 786 @Test testRebootlessUpdate_noPermission_fails()787 public void testRebootlessUpdate_noPermission_fails() throws Exception { 788 runPhase("testRebootlessUpdate_noPermission_fails"); 789 } 790 791 @Test testRebootlessUpdate_noPreInstalledApex_fails()792 public void testRebootlessUpdate_noPreInstalledApex_fails() throws Exception { 793 runPhase("testRebootlessUpdate_noPreInstalledApex_fails"); 794 } 795 796 @Test testRebootlessUpdate_unsignedPayload_fails()797 public void testRebootlessUpdate_unsignedPayload_fails() throws Exception { 798 runPhase("testRebootlessUpdate_unsignedPayload_fails"); 799 } 800 801 @Test testRebootlessUpdate_payloadSignedWithDifferentKey_fails()802 public void testRebootlessUpdate_payloadSignedWithDifferentKey_fails() throws Exception { 803 runPhase("testRebootlessUpdate_payloadSignedWithDifferentKey_fails"); 804 } 805 806 @Test testRebootlessUpdate_outerContainerSignedWithDifferentCert_fails()807 public void testRebootlessUpdate_outerContainerSignedWithDifferentCert_fails() 808 throws Exception { 809 runPhase("testRebootlessUpdate_outerContainerSignedWithDifferentCert_fails"); 810 } 811 812 @Test testRebootlessUpdate_outerContainerUnsigned_fails()813 public void testRebootlessUpdate_outerContainerUnsigned_fails() throws Exception { 814 runPhase("testRebootlessUpdate_outerContainerUnsigned_fails"); 815 } 816 817 @Test testRebootlessUpdate_targetsOlderSdk_fails()818 public void testRebootlessUpdate_targetsOlderSdk_fails() throws Exception { 819 runPhase("testRebootlessUpdate_targetsOlderSdk_fails"); 820 } 821 822 @Test 823 @LargeTest testGetInactiveApexFactoryPackagesAfterApexInstall_containsNoDuplicates()824 public void testGetInactiveApexFactoryPackagesAfterApexInstall_containsNoDuplicates() 825 throws Exception { 826 installV2Apex(); 827 runPhase("testGetInactiveApexFactoryPackagesAfterApexInstall_containsNoDuplicates"); 828 } 829 readApexInfoList()830 private List<ApexInfo> readApexInfoList() throws Exception { 831 File file = getDevice().pullFile("/apex/apex-info-list.xml"); 832 try (FileInputStream stream = new FileInputStream(file)) { 833 return XmlParser.readApexInfoList(stream).getApexInfo(); 834 } 835 } 836 getShimApexInfo()837 private ApexInfo getShimApexInfo() throws Exception { 838 List<ApexInfo> temp = 839 readApexInfoList().stream() 840 .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME)) 841 .collect(Collectors.toList()); 842 assertThat(temp).hasSize(1); 843 return temp.get(0); 844 } 845 getActiveShimApexInfo()846 private ApexInfo getActiveShimApexInfo() throws Exception { 847 return readApexInfoList().stream() 848 .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME)) 849 .filter(ApexInfo::getIsActive) 850 .findAny() 851 .orElseThrow(() -> 852 new AssertionError( 853 "No active version of " + SHIM_APEX_PACKAGE_NAME 854 + " found in /apex/apex-info-list.xml")); 855 } 856 857 /** 858 * Store the component name of the default launcher. This value will be used to reset the 859 * default launcher to its correct component upon test completion. 860 */ storeDefaultLauncher()861 private void storeDefaultLauncher() throws DeviceNotAvailableException { 862 final String PREFIX = "Launcher: ComponentInfo{"; 863 final String POSTFIX = "}"; 864 for (String s : getDevice().executeShellCommand("cmd shortcut get-default-launcher") 865 .split("\n")) { 866 if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) { 867 mDefaultLauncher = s.substring(PREFIX.length(), s.length() - POSTFIX.length()); 868 } 869 } 870 } 871 872 /** 873 * Set the default launcher to a given component. 874 * If set to the broadcast receiver component of this test app, this will allow the test app to 875 * receive SESSION_COMMITTED broadcasts. 876 */ setDefaultLauncher(String launcherComponent)877 private void setDefaultLauncher(String launcherComponent) throws DeviceNotAvailableException { 878 assertThat(launcherComponent).isNotEmpty(); 879 int user = getDevice().getCurrentUser(); 880 getDevice().executeShellCommand( 881 "cmd package set-home-activity --user " + user + " " + launcherComponent); 882 } 883 884 private static final class FailedTestLogHook extends TestWatcher { 885 886 private final BaseHostJUnit4Test mInstance; 887 private String mStagedSessionsBeforeTest; 888 FailedTestLogHook(BaseHostJUnit4Test instance)889 private FailedTestLogHook(BaseHostJUnit4Test instance) { 890 this.mInstance = instance; 891 } 892 893 @Override failed(Throwable e, Description description)894 protected void failed(Throwable e, Description description) { 895 String stagedSessionsAfterTest = getStagedSessions(); 896 Log.e(TAG, "Test " + description + " failed.\n" 897 + "Staged sessions before test started:\n" + mStagedSessionsBeforeTest + "\n" 898 + "Staged sessions after test failed:\n" + stagedSessionsAfterTest); 899 } 900 901 @Override starting(Description description)902 protected void starting(Description description) { 903 mStagedSessionsBeforeTest = getStagedSessions(); 904 } 905 getStagedSessions()906 private String getStagedSessions() { 907 try { 908 return mInstance.getDevice().executeShellV2Command("pm get-stagedsessions").getStdout(); 909 } catch (DeviceNotAvailableException e) { 910 Log.e(TAG, e); 911 return "Failed to get staged sessions"; 912 } 913 } 914 } 915 isDebuggable()916 private boolean isDebuggable() throws Exception { 917 return getDevice().getIntProperty("ro.debuggable", 0) == 1; 918 } 919 } 920