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 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 149 runPhase("testStageAnotherSessionImmediatelyAfterAbandon"); 150 } 151 152 @Test testStageAnotherSessionImmediatelyAfterAbandonMultiPackage()153 public void testStageAnotherSessionImmediatelyAfterAbandonMultiPackage() throws Exception { 154 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 155 runPhase("testStageAnotherSessionImmediatelyAfterAbandonMultiPackage"); 156 } 157 158 @Test testNoSessionUpdatedBroadcastSentForStagedSessionAbandon()159 public void testNoSessionUpdatedBroadcastSentForStagedSessionAbandon() throws Exception { 160 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 161 runPhase("testNoSessionUpdatedBroadcastSentForStagedSessionAbandon"); 162 } 163 164 @Test 165 @LargeTest testInstallMultipleStagedApks()166 public void testInstallMultipleStagedApks() throws Exception { 167 assumeSystemUser(); 168 169 setDefaultLauncher(BROADCAST_RECEIVER_COMPONENT); 170 runPhase("testInstallMultipleStagedApks_Commit"); 171 getDevice().reboot(); 172 runPhase("testInstallMultipleStagedApks_VerifyPostReboot"); 173 } 174 assumeSystemUser()175 private void assumeSystemUser() throws Exception { 176 String systemUser = "0"; 177 assumeThat("Current user is not system user", 178 getDevice().executeShellCommand("am get-current-user").trim(), equalTo(systemUser)); 179 } 180 181 @Test testGetActiveStagedSessions()182 public void testGetActiveStagedSessions() throws Exception { 183 assumeTrue("Device does not support file-system checkpoint", 184 mHostUtils.isCheckpointSupported()); 185 186 runPhase("testGetActiveStagedSessions"); 187 } 188 189 /** 190 * Verifies that active staged session fulfils conditions stated at 191 * {@link PackageInstaller.SessionInfo#isStagedSessionActive} 192 */ 193 @Test testIsStagedSessionActive()194 public void testIsStagedSessionActive() throws Exception { 195 runPhase("testIsStagedSessionActive"); 196 } 197 198 @Test testGetActiveStagedSessionsNoSessionActive()199 public void testGetActiveStagedSessionsNoSessionActive() throws Exception { 200 runPhase("testGetActiveStagedSessionsNoSessionActive"); 201 } 202 203 @Test testGetActiveStagedSessions_MultiApkSession()204 public void testGetActiveStagedSessions_MultiApkSession() throws Exception { 205 assumeTrue("Device does not support file-system checkpoint", 206 mHostUtils.isCheckpointSupported()); 207 208 runPhase("testGetActiveStagedSessions_MultiApkSession"); 209 } 210 211 @Test testStagedInstallDowngrade_DowngradeNotRequested_Fails()212 public void testStagedInstallDowngrade_DowngradeNotRequested_Fails() throws Exception { 213 runPhase("testStagedInstallDowngrade_DowngradeNotRequested_Fails_Commit"); 214 } 215 216 @Test 217 @LargeTest testStagedInstallDowngrade_DowngradeRequested_DebugBuild()218 public void testStagedInstallDowngrade_DowngradeRequested_DebugBuild() throws Exception { 219 assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user"))); 220 221 runPhase("testStagedInstallDowngrade_DowngradeRequested_Commit"); 222 getDevice().reboot(); 223 runPhase("testStagedInstallDowngrade_DowngradeRequested_DebugBuild_VerifyPostReboot"); 224 } 225 226 @Test testStagedInstallDowngrade_DowngradeRequested_UserBuild()227 public void testStagedInstallDowngrade_DowngradeRequested_UserBuild() throws Exception { 228 assumeThat(getDevice().getBuildFlavor(), endsWith("-user")); 229 assumeFalse("Device is debuggable", isDebuggable()); 230 231 runPhase("testStagedInstallDowngrade_DowngradeRequested_Fails_Commit"); 232 } 233 234 @Test testShimApexShouldPreInstalledIfUpdatingApexIsSupported()235 public void testShimApexShouldPreInstalledIfUpdatingApexIsSupported() throws Exception { 236 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 237 238 final ITestDevice.ApexInfo shimApex = mHostUtils.getShimApex().orElseThrow( 239 () -> new AssertionError("Can't find " + SHIM_APEX_PACKAGE_NAME) 240 ); 241 assertThat(shimApex.versionCode).isEqualTo(1); 242 } 243 244 @Test 245 @LargeTest testInstallStagedApex()246 public void testInstallStagedApex() throws Exception { 247 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 248 249 setDefaultLauncher(BROADCAST_RECEIVER_COMPONENT); 250 runPhase("testInstallStagedApex_Commit"); 251 getDevice().reboot(); 252 runPhase("testInstallStagedApex_VerifyPostReboot"); 253 } 254 255 @Test 256 // Don't mark as @LargeTest since we want at least one test to install apex during pre-submit. testInstallStagedApexAndApk()257 public void testInstallStagedApexAndApk() throws Exception { 258 assumeSystemUser(); 259 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 260 261 setDefaultLauncher(BROADCAST_RECEIVER_COMPONENT); 262 runPhase("testInstallStagedApexAndApk_Commit"); 263 getDevice().reboot(); 264 runPhase("testInstallStagedApexAndApk_VerifyPostReboot"); 265 } 266 267 @Test testsFailsNonStagedApexInstall()268 public void testsFailsNonStagedApexInstall() throws Exception { 269 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 270 271 runPhase("testsFailsNonStagedApexInstall"); 272 } 273 274 @Test testInstallStagedNonPreInstalledApex_Fails()275 public void testInstallStagedNonPreInstalledApex_Fails() throws Exception { 276 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 277 278 runPhase("testInstallStagedNonPreInstalledApex_Fails"); 279 } 280 281 @Test testInstallStagedDifferentPackageNameWithInstalledApex_Fails()282 public void testInstallStagedDifferentPackageNameWithInstalledApex_Fails() throws Exception { 283 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 284 285 runPhase("testInstallStagedDifferentPackageNameWithInstalledApex_Fails"); 286 } 287 288 @Test 289 @LargeTest testStageApkWithSameNameAsApexShouldFail()290 public void testStageApkWithSameNameAsApexShouldFail() throws Exception { 291 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 292 293 runPhase("testStageApkWithSameNameAsApexShouldFail_Commit"); 294 getDevice().reboot(); 295 runPhase("testStageApkWithSameNameAsApexShouldFail_VerifyPostReboot"); 296 } 297 298 @Test testNonStagedInstallApkWithSameNameAsApexShouldFail()299 public void testNonStagedInstallApkWithSameNameAsApexShouldFail() throws Exception { 300 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 301 runPhase("testNonStagedInstallApkWithSameNameAsApexShouldFail"); 302 } 303 304 @Test 305 @LargeTest testStagedInstallDowngradeApex_DowngradeNotRequested_Fails()306 public void testStagedInstallDowngradeApex_DowngradeNotRequested_Fails() throws Exception { 307 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 308 309 installV3Apex(); 310 runPhase("testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_Commit"); 311 getDevice().reboot(); 312 runPhase("testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_VerifyPostReboot"); 313 } 314 315 @Test 316 @LargeTest testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild()317 public void testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild() throws Exception { 318 assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user"))); 319 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 320 321 installV3Apex(); 322 runPhase("testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_Commit"); 323 getDevice().reboot(); 324 runPhase("testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_VerifyPostReboot"); 325 } 326 327 @Test 328 @LargeTest testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails()329 public void testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails() 330 throws Exception { 331 assumeThat(getDevice().getBuildFlavor(), endsWith("-user")); 332 assumeFalse("Device is debuggable", isDebuggable()); 333 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 334 335 installV3Apex(); 336 runPhase("testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_Commit"); 337 getDevice().reboot(); 338 runPhase("testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_" 339 + "VerifyPostReboot"); 340 } 341 342 @Test 343 @LargeTest testStagedInstallDowngradeApexToSystemVersion_DebugBuild()344 public void testStagedInstallDowngradeApexToSystemVersion_DebugBuild() throws Exception { 345 assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user"))); 346 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 347 348 installV2Apex(); 349 runPhase("testStagedInstallDowngradeApexToSystemVersion_DebugBuild_Commit"); 350 getDevice().reboot(); 351 runPhase("testStagedInstallDowngradeApexToSystemVersion_DebugBuild_VerifyPostReboot"); 352 } 353 354 @Test 355 @LargeTest testInstallStagedApex_SameGrade()356 public void testInstallStagedApex_SameGrade() throws Exception { 357 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 358 installV3Apex(); 359 installV3Apex(); 360 } 361 362 @Test 363 @LargeTest testInstallStagedApex_SameGrade_NewOneWins()364 public void testInstallStagedApex_SameGrade_NewOneWins() throws Exception { 365 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 366 367 installV2Apex(); 368 369 runPhase("testInstallStagedApex_SameGrade_NewOneWins_Commit"); 370 getDevice().reboot(); 371 runPhase("testInstallStagedApex_SameGrade_NewOneWins_VerifyPostReboot"); 372 } 373 374 @Test testInstallApex_DeviceDoesNotSupportApex_Fails()375 public void testInstallApex_DeviceDoesNotSupportApex_Fails() throws Exception { 376 assumeFalse("Device supports updating APEX", mHostUtils.isApexUpdateSupported()); 377 378 runPhase("testInstallApex_DeviceDoesNotSupportApex_Fails"); 379 } 380 installV2Apex()381 private void installV2Apex()throws Exception { 382 runPhase("testInstallV2Apex_Commit"); 383 getDevice().reboot(); 384 runPhase("testInstallV2Apex_VerifyPostReboot"); 385 } 386 installV2SignedBobApex()387 private void installV2SignedBobApex() throws Exception { 388 runPhase("testInstallV2SignedBobApex_Commit"); 389 getDevice().reboot(); 390 runPhase("testInstallV2SignedBobApex_VerifyPostReboot"); 391 } 392 installV3Apex()393 private void installV3Apex()throws Exception { 394 runPhase("testInstallV3Apex_Commit"); 395 getDevice().reboot(); 396 runPhase("testInstallV3Apex_VerifyPostReboot"); 397 } 398 399 @Test testFailsInvalidApexInstall()400 public void testFailsInvalidApexInstall() throws Exception { 401 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 402 runPhase("testFailsInvalidApexInstall_Commit"); 403 runPhase("testFailsInvalidApexInstall_AbandonSessionIsNoop"); 404 } 405 406 @Test testStagedApkSessionCallbacks()407 public void testStagedApkSessionCallbacks() throws Exception { 408 runPhase("testStagedApkSessionCallbacks"); 409 } 410 411 @Test 412 @LargeTest testInstallStagedApexWithoutApexSuffix()413 public void testInstallStagedApexWithoutApexSuffix() throws Exception { 414 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 415 416 runPhase("testInstallStagedApexWithoutApexSuffix_Commit"); 417 getDevice().reboot(); 418 runPhase("testInstallStagedApexWithoutApexSuffix_VerifyPostReboot"); 419 } 420 421 @Test testRejectsApexDifferentCertificate()422 public void testRejectsApexDifferentCertificate() throws Exception { 423 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 424 425 runPhase("testRejectsApexDifferentCertificate"); 426 } 427 428 /** 429 * Tests for staged install involving rotated keys. 430 * 431 * Here alice means the original default key that cts.shim.v1 package was signed with and 432 * bob is the new key alice rotates to. Where ambiguous, we will refer keys as alice and bob 433 * instead of "old key" and "new key". 434 * 435 * By default, rotated keys have rollback capability enabled for old keys. When we remove 436 * rollback capability from a key, it is called "Distrusting Event" and the distrusted key can 437 * not update the app anymore. 438 */ 439 440 // Should not be able to update with a key that has not been rotated. 441 @Test testUpdateWithDifferentKeyButNoRotation()442 public void testUpdateWithDifferentKeyButNoRotation() throws Exception { 443 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 444 445 runPhase("testUpdateWithDifferentKeyButNoRotation"); 446 } 447 448 // Should be able to update with a key that has been rotated. 449 @Test 450 @LargeTest testUpdateWithDifferentKey()451 public void testUpdateWithDifferentKey() throws Exception { 452 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 453 454 runPhase("testUpdateWithDifferentKey_Commit"); 455 getDevice().reboot(); 456 runPhase("testUpdateWithDifferentKey_VerifyPostReboot"); 457 } 458 459 // Should not be able to update with a key that is no longer trusted (i.e, has no 460 // rollback capability) 461 @Test 462 @LargeTest testUntrustedOldKeyIsRejected()463 public void testUntrustedOldKeyIsRejected() throws Exception { 464 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 465 466 installV2SignedBobApex(); 467 runPhase("testUntrustedOldKeyIsRejected"); 468 } 469 470 // Should be able to update with an old key which is trusted 471 @Test 472 @LargeTest testTrustedOldKeyIsAccepted()473 public void testTrustedOldKeyIsAccepted() throws Exception { 474 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 475 476 runPhase("testTrustedOldKeyIsAccepted_Commit"); 477 getDevice().reboot(); 478 runPhase("testTrustedOldKeyIsAccepted_CommitPostReboot"); 479 getDevice().reboot(); 480 runPhase("testTrustedOldKeyIsAccepted_VerifyPostReboot"); 481 } 482 483 // Should be able to update further with rotated key 484 @Test 485 @LargeTest testAfterRotationNewKeyCanUpdateFurther()486 public void testAfterRotationNewKeyCanUpdateFurther() throws Exception { 487 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 488 489 installV2SignedBobApex(); 490 runPhase("testAfterRotationNewKeyCanUpdateFurther_CommitPostReboot"); 491 getDevice().reboot(); 492 runPhase("testAfterRotationNewKeyCanUpdateFurther_VerifyPostReboot"); 493 } 494 495 @Test 496 @LargeTest testAfterRotationNewKeyCanUpdateFurtherWithoutLineage()497 public void testAfterRotationNewKeyCanUpdateFurtherWithoutLineage() throws Exception { 498 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 499 500 installV2SignedBobApex(); 501 runPhase("testAfterRotationNewKeyCanUpdateFurtherWithoutLineage"); 502 } 503 504 /** 505 * Tests for staging and installing multiple staged sessions. 506 */ 507 508 // Should fail to stage multiple sessions when check-point is not available 509 @Test testFailStagingMultipleSessionsIfNoCheckPoint()510 public void testFailStagingMultipleSessionsIfNoCheckPoint() throws Exception { 511 assumeFalse("Device supports file-system checkpoint", 512 mHostUtils.isCheckpointSupported()); 513 514 runPhase("testFailStagingMultipleSessionsIfNoCheckPoint"); 515 } 516 517 @Test testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk()518 public void testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk() throws Exception { 519 runPhase("testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk"); 520 } 521 522 @Test testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk()523 public void testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk() 524 throws Exception { 525 assumeTrue("Device does not support file-system checkpoint", 526 mHostUtils.isCheckpointSupported()); 527 528 runPhase("testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk"); 529 } 530 531 @Test testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk()532 public void testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk() throws Exception { 533 assumeTrue("Device does not support file-system checkpoint", 534 mHostUtils.isCheckpointSupported()); 535 536 runPhase("testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk"); 537 } 538 539 // Test for installing multiple staged sessions at the same time 540 @Test 541 @LargeTest testMultipleStagedInstall_ApkOnly()542 public void testMultipleStagedInstall_ApkOnly() throws Exception { 543 assumeTrue("Device does not support file-system checkpoint", 544 mHostUtils.isCheckpointSupported()); 545 546 runPhase("testMultipleStagedInstall_ApkOnly_Commit"); 547 getDevice().reboot(); 548 runPhase("testMultipleStagedInstall_ApkOnly_VerifyPostReboot"); 549 } 550 551 // If apk installation fails in one staged session, then all staged session should fail. 552 @Test 553 @LargeTest testInstallMultipleStagedSession_PartialFail_ApkOnly()554 public void testInstallMultipleStagedSession_PartialFail_ApkOnly() throws Exception { 555 assumeTrue("Device does not support file-system checkpoint", 556 mHostUtils.isCheckpointSupported()); 557 558 runPhase("testInstallMultipleStagedSession_PartialFail_ApkOnly_Commit"); 559 getDevice().reboot(); 560 runPhase("testInstallMultipleStagedSession_PartialFail_ApkOnly_VerifyPostReboot"); 561 } 562 563 // Failure reason of staged install should be be persisted for single sessions 564 @Test 565 @LargeTest testFailureReasonPersists_SingleSession()566 public void testFailureReasonPersists_SingleSession() throws Exception { 567 assumeTrue("Device does not support file-system checkpoint", 568 mHostUtils.isCheckpointSupported()); 569 570 runPhase("testFailureReasonPersists_SingleSession_Commit"); 571 getDevice().reboot(); 572 runPhase("testFailureReasonPersists_SingleSession_VerifyPostReboot"); 573 } 574 575 // Failure reason of staged install should be be persisted for multi session staged install 576 @Test 577 @LargeTest testFailureReasonPersists_MultiSession()578 public void testFailureReasonPersists_MultiSession() throws Exception { 579 assumeTrue("Device does not support file-system checkpoint", 580 mHostUtils.isCheckpointSupported()); 581 582 runPhase("testFailureReasonPersists_MultipleSession_Commit"); 583 getDevice().reboot(); 584 runPhase("testFailureReasonPersists_MultipleSession_VerifyPostReboot"); 585 } 586 587 @Test 588 @LargeTest testSamegradeSystemApex()589 public void testSamegradeSystemApex() throws Exception { 590 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 591 592 runPhase("testSamegradeSystemApex_Commit"); 593 getDevice().reboot(); 594 runPhase("testSamegradeSystemApex_VerifyPostReboot"); 595 } 596 597 @Test 598 @LargeTest testInstallApkChangingFingerprint()599 public void testInstallApkChangingFingerprint() throws Exception { 600 try { 601 getDevice().executeShellCommand("setprop persist.pm.mock-upgrade true"); 602 runPhase("testInstallApkChangingFingerprint"); 603 getDevice().reboot(); 604 runPhase("testInstallApkChangingFingerprint_VerifyAborted"); 605 } finally { 606 getDevice().executeShellCommand("setprop persist.pm.mock-upgrade false"); 607 } 608 } 609 610 @Test 611 @LargeTest testInstallStagedNoHashtreeApex()612 public void testInstallStagedNoHashtreeApex() throws Exception { 613 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 614 615 runPhase("testInstallStagedNoHashtreeApex_Commit"); 616 getDevice().reboot(); 617 runPhase("testInstallStagedNoHashtreeApex_VerifyPostReboot"); 618 } 619 620 /** 621 * Should fail to verify apex targeting older dev sdk 622 */ 623 @Test testApexTargetingOldDevSdkFailsVerification()624 public void testApexTargetingOldDevSdkFailsVerification() throws Exception { 625 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 626 627 runPhase("testApexTargetingOldDevSdkFailsVerification"); 628 } 629 630 /** 631 * Apex should fail to install if apk-in-apex fails to get scanned 632 */ 633 @Test 634 @LargeTest testApexFailsToInstallIfApkInApexFailsToScan()635 public void testApexFailsToInstallIfApkInApexFailsToScan() throws Exception { 636 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 637 638 runPhase("testApexFailsToInstallIfApkInApexFailsToScan_Commit"); 639 getDevice().reboot(); 640 runPhase("testApexFailsToInstallIfApkInApexFailsToScan_VerifyPostReboot"); 641 } 642 643 @Test testCorruptedApexFailsVerification_b146895998()644 public void testCorruptedApexFailsVerification_b146895998() throws Exception { 645 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 646 647 runPhase("testCorruptedApexFailsVerification_b146895998"); 648 } 649 650 /** 651 * Should fail to pass apk signature check 652 */ 653 @Test testApexWithUnsignedApkFailsVerification()654 public void testApexWithUnsignedApkFailsVerification() throws Exception { 655 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 656 657 runPhase("testApexWithUnsignedApkFailsVerification"); 658 } 659 660 /** 661 * Should fail to verify apex signed payload with a different key 662 */ 663 @Test testApexSignPayloadWithDifferentKeyFailsVerification()664 public void testApexSignPayloadWithDifferentKeyFailsVerification() throws Exception { 665 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 666 667 runPhase("testApexSignPayloadWithDifferentKeyFailsVerification"); 668 } 669 670 /** 671 * Should fail to verify apex with unsigned payload 672 */ 673 @Test testApexWithUnsignedPayloadFailsVerification()674 public void testApexWithUnsignedPayloadFailsVerification() throws Exception { 675 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 676 677 runPhase("testApexWithUnsignedPayloadFailsVerification"); 678 } 679 680 @Test 681 @LargeTest testApexSetsUpdatedSystemAppFlag()682 public void testApexSetsUpdatedSystemAppFlag() throws Exception { 683 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 684 685 runPhase("testApexSetsUpdatedSystemAppFlag_preUpdate"); 686 installV2Apex(); 687 runPhase("testApexSetsUpdatedSystemAppFlag_postUpdate"); 688 } 689 690 /** 691 * Test non-priv apps cannot access /data/app-staging folder contents 692 */ 693 @Test testAppStagingDirCannotBeReadByNonPrivApps()694 public void testAppStagingDirCannotBeReadByNonPrivApps() throws Exception { 695 runPhase("testAppStagingDirCannotBeReadByNonPrivApps"); 696 } 697 698 @Test 699 @LargeTest testUpdatedApexFromDataApexActiveCanBePulled()700 public void testUpdatedApexFromDataApexActiveCanBePulled() throws Exception { 701 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 702 703 installV2Apex(); 704 705 final ITestDevice.ApexInfo shimApex = mHostUtils.getShimApex().orElseThrow( 706 () -> new AssertionError("Can't find " + SHIM_APEX_PACKAGE_NAME) 707 ); 708 709 assertThat(shimApex.sourceDir).startsWith("/data/apex/active"); 710 assertThat(getDevice().pullFile(shimApex.sourceDir)).isNotNull(); 711 } 712 713 @Test testApexInfoList()714 public void testApexInfoList() throws Exception { 715 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 716 717 // Check that content of /apex/apex-info-list.xml matches output of 718 // `adb shell pm list packages --apex-only --show-versioncode -f`. 719 List<ApexInfo> apexInfoList = readApexInfoList(); 720 Set<ITestDevice.ApexInfo> activeApexes = getDevice().getActiveApexes(); 721 assertThat(apexInfoList.size()).isEqualTo(activeApexes.size()); 722 for (ITestDevice.ApexInfo apex : activeApexes) { 723 // Note: we can't assert equality of the apex.name and apexInfo.getModuleName() since 724 // they represent different concepts (the former is package name, while latter is apex 725 // module name) 726 List<ApexInfo> temp = 727 apexInfoList.stream() 728 .filter(a -> a.getModulePath().equals(apex.sourceDir)) 729 .collect(Collectors.toList()); 730 assertThat(temp).hasSize(1); 731 ApexInfo apexInfo = temp.get(0); 732 assertThat(apexInfo.getModulePath()).isEqualTo(apex.sourceDir); 733 assertThat(apexInfo.getVersionCode()).isEqualTo(apex.versionCode); 734 assertThat(apexInfo.getIsActive()).isTrue(); 735 } 736 } 737 738 @Test testApexInfoListAfterUpdate()739 public void testApexInfoListAfterUpdate() throws Exception { 740 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 741 742 installV2Apex(); 743 744 List<ApexInfo> shimApexInfo = 745 readApexInfoList().stream() 746 .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME)) 747 .collect(Collectors.toList()); 748 749 assertThat(shimApexInfo).hasSize(2); 750 751 ApexInfo factoryShimApexInfo = 752 shimApexInfo.stream() 753 .filter(ApexInfo::getIsFactory) 754 .findAny() 755 .orElseThrow(() -> 756 new AssertionError( 757 "No factory version of " + SHIM_APEX_PACKAGE_NAME 758 + " found in /apex/apex-info-list.xml")); 759 assertThat(factoryShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME); 760 assertThat(factoryShimApexInfo.getIsActive()).isFalse(); 761 assertThat(factoryShimApexInfo.getIsFactory()).isTrue(); 762 assertThat(factoryShimApexInfo.getVersionCode()).isEqualTo(1); 763 assertThat(factoryShimApexInfo.getModulePath()) 764 .isEqualTo(factoryShimApexInfo.getPreinstalledModulePath()); 765 766 ApexInfo activeShimApexInfo = 767 shimApexInfo.stream() 768 .filter(ApexInfo::getIsActive) 769 .findAny() 770 .orElseThrow(() -> 771 new AssertionError( 772 "No active version of " + SHIM_APEX_PACKAGE_NAME 773 + " found in /apex/apex-info-list.xml")); 774 assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME); 775 assertThat(activeShimApexInfo.getIsActive()).isTrue(); 776 assertThat(activeShimApexInfo.getIsFactory()).isFalse(); 777 assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(2); 778 assertThat(activeShimApexInfo.getPreinstalledModulePath()) 779 .isEqualTo(factoryShimApexInfo.getModulePath()); 780 } 781 readApexInfoList()782 private List<ApexInfo> readApexInfoList() throws Exception { 783 File file = getDevice().pullFile("/apex/apex-info-list.xml"); 784 try (FileInputStream stream = new FileInputStream(file)) { 785 return XmlParser.readApexInfoList(stream).getApexInfo(); 786 } 787 } 788 789 /** 790 * Store the component name of the default launcher. This value will be used to reset the 791 * default launcher to its correct component upon test completion. 792 */ storeDefaultLauncher()793 private void storeDefaultLauncher() throws DeviceNotAvailableException { 794 final String PREFIX = "Launcher: ComponentInfo{"; 795 final String POSTFIX = "}"; 796 for (String s : getDevice().executeShellCommand("cmd shortcut get-default-launcher") 797 .split("\n")) { 798 if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) { 799 mDefaultLauncher = s.substring(PREFIX.length(), s.length() - POSTFIX.length()); 800 } 801 } 802 } 803 804 /** 805 * Set the default launcher to a given component. 806 * If set to the broadcast receiver component of this test app, this will allow the test app to 807 * receive SESSION_COMMITTED broadcasts. 808 */ setDefaultLauncher(String launcherComponent)809 private void setDefaultLauncher(String launcherComponent) throws DeviceNotAvailableException { 810 assertThat(launcherComponent).isNotEmpty(); 811 int user = getDevice().getCurrentUser(); 812 getDevice().executeShellCommand( 813 "cmd package set-home-activity --user " + user + " " + launcherComponent); 814 } 815 816 private static final class FailedTestLogHook extends TestWatcher { 817 818 private final BaseHostJUnit4Test mInstance; 819 private String mStagedSessionsBeforeTest; 820 FailedTestLogHook(BaseHostJUnit4Test instance)821 private FailedTestLogHook(BaseHostJUnit4Test instance) { 822 this.mInstance = instance; 823 } 824 825 @Override failed(Throwable e, Description description)826 protected void failed(Throwable e, Description description) { 827 String stagedSessionsAfterTest = getStagedSessions(); 828 Log.e(TAG, "Test " + description + " failed.\n" 829 + "Staged sessions before test started:\n" + mStagedSessionsBeforeTest + "\n" 830 + "Staged sessions after test failed:\n" + stagedSessionsAfterTest); 831 } 832 833 @Override starting(Description description)834 protected void starting(Description description) { 835 mStagedSessionsBeforeTest = getStagedSessions(); 836 } 837 getStagedSessions()838 private String getStagedSessions() { 839 try { 840 return mInstance.getDevice().executeShellV2Command("pm get-stagedsessions").getStdout(); 841 } catch (DeviceNotAvailableException e) { 842 Log.e(TAG, e); 843 return "Failed to get staged sessions"; 844 } 845 } 846 } 847 isDebuggable()848 private boolean isDebuggable() throws Exception { 849 return getDevice().getIntProperty("ro.debuggable", 0) == 1; 850 } 851 } 852