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 ApexInfo shim1 = 360 readApexInfoList().stream() 361 .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME)) 362 .filter(ApexInfo::getIsActive) 363 .findAny() 364 .orElseThrow(() -> 365 new AssertionError( 366 "No active version of " + SHIM_APEX_PACKAGE_NAME 367 + " found in /apex/apex-info-list.xml")); 368 369 installV3Apex(); 370 ApexInfo shim2 = 371 readApexInfoList().stream() 372 .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME)) 373 .filter(ApexInfo::getIsActive) 374 .findAny() 375 .orElseThrow(() -> 376 new AssertionError( 377 "No active version of " + SHIM_APEX_PACKAGE_NAME 378 + " found in /apex/apex-info-list.xml")); 379 assertThat(shim1.getLastUpdateMillis()).isNotEqualTo(shim2.getLastUpdateMillis()); 380 } 381 382 @Test 383 @LargeTest testInstallStagedApex_SameGrade_NewOneWins()384 public void testInstallStagedApex_SameGrade_NewOneWins() throws Exception { 385 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 386 387 installV2Apex(); 388 389 runPhase("testInstallStagedApex_SameGrade_NewOneWins_Commit"); 390 getDevice().reboot(); 391 runPhase("testInstallStagedApex_SameGrade_NewOneWins_VerifyPostReboot"); 392 } 393 394 @Test testInstallApex_DeviceDoesNotSupportApex_Fails()395 public void testInstallApex_DeviceDoesNotSupportApex_Fails() throws Exception { 396 assumeFalse("Device supports updating APEX", mHostUtils.isApexUpdateSupported()); 397 398 runPhase("testInstallApex_DeviceDoesNotSupportApex_Fails"); 399 } 400 installV2Apex()401 private void installV2Apex()throws Exception { 402 runPhase("testInstallV2Apex_Commit"); 403 getDevice().reboot(); 404 runPhase("testInstallV2Apex_VerifyPostReboot"); 405 } 406 installV2SignedBobApex()407 private void installV2SignedBobApex() throws Exception { 408 runPhase("testInstallV2SignedBobApex_Commit"); 409 getDevice().reboot(); 410 runPhase("testInstallV2SignedBobApex_VerifyPostReboot"); 411 } 412 installV3Apex()413 private void installV3Apex()throws Exception { 414 runPhase("testInstallV3Apex_Commit"); 415 getDevice().reboot(); 416 runPhase("testInstallV3Apex_VerifyPostReboot"); 417 } 418 419 @Test testFailsInvalidApexInstall()420 public void testFailsInvalidApexInstall() throws Exception { 421 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 422 runPhase("testFailsInvalidApexInstall_Commit"); 423 runPhase("testFailsInvalidApexInstall_AbandonSessionIsNoop"); 424 } 425 426 @Test testStagedApkSessionCallbacks()427 public void testStagedApkSessionCallbacks() throws Exception { 428 runPhase("testStagedApkSessionCallbacks"); 429 } 430 431 @Test 432 @LargeTest testInstallStagedApexWithoutApexSuffix()433 public void testInstallStagedApexWithoutApexSuffix() throws Exception { 434 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 435 436 runPhase("testInstallStagedApexWithoutApexSuffix_Commit"); 437 getDevice().reboot(); 438 runPhase("testInstallStagedApexWithoutApexSuffix_VerifyPostReboot"); 439 } 440 441 @Test testRejectsApexDifferentCertificate()442 public void testRejectsApexDifferentCertificate() throws Exception { 443 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 444 445 runPhase("testRejectsApexDifferentCertificate"); 446 } 447 448 /** 449 * Tests for staged install involving rotated keys. 450 * 451 * Here alice means the original default key that cts.shim.v1 package was signed with and 452 * bob is the new key alice rotates to. Where ambiguous, we will refer keys as alice and bob 453 * instead of "old key" and "new key". 454 * 455 * By default, rotated keys have rollback capability enabled for old keys. When we remove 456 * rollback capability from a key, it is called "Distrusting Event" and the distrusted key can 457 * not update the app anymore. 458 */ 459 460 // Should not be able to update with a key that has not been rotated. 461 @Test testUpdateWithDifferentKeyButNoRotation()462 public void testUpdateWithDifferentKeyButNoRotation() throws Exception { 463 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 464 465 runPhase("testUpdateWithDifferentKeyButNoRotation"); 466 } 467 468 // Should be able to update with a key that has been rotated. 469 @Test 470 @LargeTest testUpdateWithDifferentKey()471 public void testUpdateWithDifferentKey() throws Exception { 472 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 473 474 runPhase("testUpdateWithDifferentKey_Commit"); 475 getDevice().reboot(); 476 runPhase("testUpdateWithDifferentKey_VerifyPostReboot"); 477 } 478 479 // Should not be able to update with a key that is no longer trusted (i.e, has no 480 // rollback capability) 481 @Test 482 @LargeTest testUntrustedOldKeyIsRejected()483 public void testUntrustedOldKeyIsRejected() throws Exception { 484 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 485 486 installV2SignedBobApex(); 487 runPhase("testUntrustedOldKeyIsRejected"); 488 } 489 490 // Should be able to update with an old key which is trusted 491 @Test 492 @LargeTest testTrustedOldKeyIsAccepted()493 public void testTrustedOldKeyIsAccepted() throws Exception { 494 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 495 496 runPhase("testTrustedOldKeyIsAccepted_Commit"); 497 getDevice().reboot(); 498 runPhase("testTrustedOldKeyIsAccepted_CommitPostReboot"); 499 getDevice().reboot(); 500 runPhase("testTrustedOldKeyIsAccepted_VerifyPostReboot"); 501 } 502 503 // Should be able to update further with rotated key 504 @Test 505 @LargeTest testAfterRotationNewKeyCanUpdateFurther()506 public void testAfterRotationNewKeyCanUpdateFurther() throws Exception { 507 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 508 509 installV2SignedBobApex(); 510 runPhase("testAfterRotationNewKeyCanUpdateFurther_CommitPostReboot"); 511 getDevice().reboot(); 512 runPhase("testAfterRotationNewKeyCanUpdateFurther_VerifyPostReboot"); 513 } 514 515 @Test 516 @LargeTest testAfterRotationNewKeyCanUpdateFurtherWithoutLineage()517 public void testAfterRotationNewKeyCanUpdateFurtherWithoutLineage() throws Exception { 518 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 519 520 installV2SignedBobApex(); 521 runPhase("testAfterRotationNewKeyCanUpdateFurtherWithoutLineage"); 522 } 523 524 /** 525 * Tests for staging and installing multiple staged sessions. 526 */ 527 528 // Should fail to stage multiple sessions when check-point is not available 529 @Test testFailStagingMultipleSessionsIfNoCheckPoint()530 public void testFailStagingMultipleSessionsIfNoCheckPoint() throws Exception { 531 assumeFalse("Device supports file-system checkpoint", 532 mHostUtils.isCheckpointSupported()); 533 534 runPhase("testFailStagingMultipleSessionsIfNoCheckPoint"); 535 } 536 537 @Test testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk()538 public void testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk() throws Exception { 539 runPhase("testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk"); 540 } 541 542 @Test testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk()543 public void testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk() 544 throws Exception { 545 assumeTrue("Device does not support file-system checkpoint", 546 mHostUtils.isCheckpointSupported()); 547 548 runPhase("testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk"); 549 } 550 551 @Test testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk()552 public void testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk() throws Exception { 553 assumeTrue("Device does not support file-system checkpoint", 554 mHostUtils.isCheckpointSupported()); 555 556 runPhase("testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk"); 557 } 558 559 // Test for installing multiple staged sessions at the same time 560 @Test 561 @LargeTest testMultipleStagedInstall_ApkOnly()562 public void testMultipleStagedInstall_ApkOnly() throws Exception { 563 assumeTrue("Device does not support file-system checkpoint", 564 mHostUtils.isCheckpointSupported()); 565 566 runPhase("testMultipleStagedInstall_ApkOnly_Commit"); 567 getDevice().reboot(); 568 runPhase("testMultipleStagedInstall_ApkOnly_VerifyPostReboot"); 569 } 570 571 // If apk installation fails in one staged session, then all staged session should fail. 572 @Test 573 @LargeTest testInstallMultipleStagedSession_PartialFail_ApkOnly()574 public void testInstallMultipleStagedSession_PartialFail_ApkOnly() throws Exception { 575 assumeTrue("Device does not support file-system checkpoint", 576 mHostUtils.isCheckpointSupported()); 577 578 runPhase("testInstallMultipleStagedSession_PartialFail_ApkOnly_Commit"); 579 getDevice().reboot(); 580 runPhase("testInstallMultipleStagedSession_PartialFail_ApkOnly_VerifyPostReboot"); 581 } 582 583 // Failure reason of staged install should be be persisted for single sessions 584 @Test 585 @LargeTest testFailureReasonPersists_SingleSession()586 public void testFailureReasonPersists_SingleSession() throws Exception { 587 assumeTrue("Device does not support file-system checkpoint", 588 mHostUtils.isCheckpointSupported()); 589 590 runPhase("testFailureReasonPersists_SingleSession_Commit"); 591 getDevice().reboot(); 592 runPhase("testFailureReasonPersists_SingleSession_VerifyPostReboot"); 593 } 594 595 // Failure reason of staged install should be be persisted for multi session staged install 596 @Test 597 @LargeTest testFailureReasonPersists_MultiSession()598 public void testFailureReasonPersists_MultiSession() throws Exception { 599 assumeTrue("Device does not support file-system checkpoint", 600 mHostUtils.isCheckpointSupported()); 601 602 runPhase("testFailureReasonPersists_MultipleSession_Commit"); 603 getDevice().reboot(); 604 runPhase("testFailureReasonPersists_MultipleSession_VerifyPostReboot"); 605 } 606 607 @Test 608 @LargeTest testSamegradeSystemApex()609 public void testSamegradeSystemApex() throws Exception { 610 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 611 612 runPhase("testSamegradeSystemApex_Commit"); 613 getDevice().reboot(); 614 runPhase("testSamegradeSystemApex_VerifyPostReboot"); 615 } 616 617 @Test 618 @LargeTest testInstallApkChangingFingerprint()619 public void testInstallApkChangingFingerprint() throws Exception { 620 try { 621 getDevice().executeShellCommand("setprop persist.pm.mock-upgrade true"); 622 runPhase("testInstallApkChangingFingerprint"); 623 getDevice().reboot(); 624 runPhase("testInstallApkChangingFingerprint_VerifyAborted"); 625 } finally { 626 getDevice().executeShellCommand("setprop persist.pm.mock-upgrade false"); 627 } 628 } 629 630 @Test 631 @LargeTest testInstallStagedNoHashtreeApex()632 public void testInstallStagedNoHashtreeApex() throws Exception { 633 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 634 635 runPhase("testInstallStagedNoHashtreeApex_Commit"); 636 getDevice().reboot(); 637 runPhase("testInstallStagedNoHashtreeApex_VerifyPostReboot"); 638 } 639 640 /** 641 * Should fail to verify apex targeting older dev sdk 642 */ 643 @Test testApexTargetingOldDevSdkFailsVerification()644 public void testApexTargetingOldDevSdkFailsVerification() throws Exception { 645 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 646 647 runPhase("testApexTargetingOldDevSdkFailsVerification"); 648 } 649 650 /** 651 * Apex should fail to install if apk-in-apex fails to get scanned 652 */ 653 @Test 654 @LargeTest testApexFailsToInstallIfApkInApexFailsToScan()655 public void testApexFailsToInstallIfApkInApexFailsToScan() throws Exception { 656 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 657 658 runPhase("testApexFailsToInstallIfApkInApexFailsToScan_Commit"); 659 getDevice().reboot(); 660 runPhase("testApexFailsToInstallIfApkInApexFailsToScan_VerifyPostReboot"); 661 } 662 663 @Test testCorruptedApexFailsVerification_b146895998()664 public void testCorruptedApexFailsVerification_b146895998() throws Exception { 665 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 666 667 runPhase("testCorruptedApexFailsVerification_b146895998"); 668 } 669 670 /** 671 * Should fail to pass apk signature check 672 */ 673 @Test testApexWithUnsignedApkFailsVerification()674 public void testApexWithUnsignedApkFailsVerification() throws Exception { 675 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 676 677 runPhase("testApexWithUnsignedApkFailsVerification"); 678 } 679 680 /** 681 * Should fail to verify apex signed payload with a different key 682 */ 683 @Test testApexSignPayloadWithDifferentKeyFailsVerification()684 public void testApexSignPayloadWithDifferentKeyFailsVerification() throws Exception { 685 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 686 687 runPhase("testApexSignPayloadWithDifferentKeyFailsVerification"); 688 } 689 690 /** 691 * Should fail to verify apex with unsigned payload 692 */ 693 @Test testApexWithUnsignedPayloadFailsVerification()694 public void testApexWithUnsignedPayloadFailsVerification() throws Exception { 695 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 696 697 runPhase("testApexWithUnsignedPayloadFailsVerification"); 698 } 699 700 @Test 701 @LargeTest testApexSetsUpdatedSystemAppFlag()702 public void testApexSetsUpdatedSystemAppFlag() throws Exception { 703 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 704 705 runPhase("testApexSetsUpdatedSystemAppFlag_preUpdate"); 706 installV2Apex(); 707 runPhase("testApexSetsUpdatedSystemAppFlag_postUpdate"); 708 } 709 710 /** 711 * Test non-priv apps cannot access /data/app-staging folder contents 712 */ 713 @Test testAppStagingDirCannotBeReadByNonPrivApps()714 public void testAppStagingDirCannotBeReadByNonPrivApps() throws Exception { 715 runPhase("testAppStagingDirCannotBeReadByNonPrivApps"); 716 } 717 718 @Test 719 @LargeTest testUpdatedApexFromDataApexActiveCanBePulled()720 public void testUpdatedApexFromDataApexActiveCanBePulled() throws Exception { 721 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 722 723 installV2Apex(); 724 725 final ITestDevice.ApexInfo shimApex = mHostUtils.getShimApex().orElseThrow( 726 () -> new AssertionError("Can't find " + SHIM_APEX_PACKAGE_NAME) 727 ); 728 729 assertThat(shimApex.sourceDir).startsWith("/data/apex/active"); 730 assertThat(getDevice().pullFile(shimApex.sourceDir)).isNotNull(); 731 } 732 733 @Test testApexInfoList()734 public void testApexInfoList() throws Exception { 735 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 736 737 // Check that content of /apex/apex-info-list.xml matches output of 738 // `adb shell pm list packages --apex-only --show-versioncode -f`. 739 List<ApexInfo> apexInfoList = readApexInfoList(); 740 Set<ITestDevice.ApexInfo> activeApexes = getDevice().getActiveApexes(); 741 assertThat(apexInfoList.size()).isEqualTo(activeApexes.size()); 742 for (ITestDevice.ApexInfo apex : activeApexes) { 743 // Note: we can't assert equality of the apex.name and apexInfo.getModuleName() since 744 // they represent different concepts (the former is package name, while latter is apex 745 // module name) 746 List<ApexInfo> temp = 747 apexInfoList.stream() 748 .filter(a -> a.getModulePath().equals(apex.sourceDir)) 749 .collect(Collectors.toList()); 750 assertThat(temp).hasSize(1); 751 ApexInfo apexInfo = temp.get(0); 752 assertThat(apexInfo.getModulePath()).isEqualTo(apex.sourceDir); 753 assertThat(apexInfo.getVersionCode()).isEqualTo(apex.versionCode); 754 assertThat(apexInfo.getIsActive()).isTrue(); 755 assertThat(apexInfo.getLastUpdateMillis()).isGreaterThan(0); 756 } 757 } 758 759 @Test testApexInfoListAfterUpdate()760 public void testApexInfoListAfterUpdate() throws Exception { 761 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 762 763 ApexInfo shimBeforeUpdate = getShimApexInfo(); 764 765 installV2Apex(); 766 767 List<ApexInfo> shimApexInfo = 768 readApexInfoList().stream() 769 .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME)) 770 .collect(Collectors.toList()); 771 772 assertThat(shimApexInfo).hasSize(2); 773 774 ApexInfo factoryShimApexInfo = 775 shimApexInfo.stream() 776 .filter(ApexInfo::getIsFactory) 777 .findAny() 778 .orElseThrow(() -> 779 new AssertionError( 780 "No factory version of " + SHIM_APEX_PACKAGE_NAME 781 + " found in /apex/apex-info-list.xml")); 782 assertThat(factoryShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME); 783 assertThat(factoryShimApexInfo.getIsActive()).isFalse(); 784 assertThat(factoryShimApexInfo.getIsFactory()).isTrue(); 785 assertThat(factoryShimApexInfo.getVersionCode()).isEqualTo(1); 786 assertThat(factoryShimApexInfo.getModulePath()) 787 .isEqualTo(factoryShimApexInfo.getPreinstalledModulePath()); 788 assertThat(factoryShimApexInfo.getLastUpdateMillis()) 789 .isEqualTo(shimBeforeUpdate.getLastUpdateMillis()); 790 791 ApexInfo activeShimApexInfo = 792 shimApexInfo.stream() 793 .filter(ApexInfo::getIsActive) 794 .findAny() 795 .orElseThrow(() -> 796 new AssertionError( 797 "No active version of " + SHIM_APEX_PACKAGE_NAME 798 + " found in /apex/apex-info-list.xml")); 799 assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME); 800 assertThat(activeShimApexInfo.getIsActive()).isTrue(); 801 assertThat(activeShimApexInfo.getIsFactory()).isFalse(); 802 assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(2); 803 assertThat(activeShimApexInfo.getPreinstalledModulePath()) 804 .isEqualTo(factoryShimApexInfo.getModulePath()); 805 assertThat(activeShimApexInfo.getLastUpdateMillis()) 806 .isNotEqualTo(shimBeforeUpdate.getLastUpdateMillis()); 807 } 808 809 @Test 810 @LargeTest testRebootlessUpdate()811 public void testRebootlessUpdate() throws Exception { 812 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 813 814 runPhase("testRebootlessUpdate"); 815 ApexInfo activeShimApexInfo = getActiveShimApexInfo(); 816 assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME); 817 assertThat(activeShimApexInfo.getIsActive()).isTrue(); 818 assertThat(activeShimApexInfo.getIsFactory()).isFalse(); 819 assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(2); 820 } 821 822 @Test testRebootlessUpdate_fromV2ToV3_sameBoot()823 public void testRebootlessUpdate_fromV2ToV3_sameBoot() throws Exception { 824 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 825 826 runPhase("testRebootlessUpdate"); 827 runPhase("testRebootlessUpdate_installV3"); 828 ApexInfo activeShimApexInfo = getActiveShimApexInfo(); 829 assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME); 830 assertThat(activeShimApexInfo.getIsActive()).isTrue(); 831 assertThat(activeShimApexInfo.getIsFactory()).isFalse(); 832 assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(3); 833 } 834 835 @Test 836 @LargeTest testRebootlessUpdate_fromV2ToV3_rebootInBetween()837 public void testRebootlessUpdate_fromV2ToV3_rebootInBetween() throws Exception { 838 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 839 840 runPhase("testRebootlessUpdate"); 841 getDevice().reboot(); 842 runPhase("testRebootlessUpdate_installV3"); 843 ApexInfo activeShimApexInfo = getActiveShimApexInfo(); 844 assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME); 845 assertThat(activeShimApexInfo.getIsActive()).isTrue(); 846 assertThat(activeShimApexInfo.getIsFactory()).isFalse(); 847 assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(3); 848 } 849 850 @Test 851 @LargeTest testRebootlessUpdate_downgrage_fails()852 public void testRebootlessUpdate_downgrage_fails() throws Exception { 853 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 854 855 runPhase("testRebootlessUpdate_installV3"); 856 runPhase("testRebootlessUpdate_downgradeToV2_fails"); 857 } 858 859 @Test testRebootlessUpdate_noPermission_fails()860 public void testRebootlessUpdate_noPermission_fails() throws Exception { 861 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 862 863 runPhase("testRebootlessUpdate_noPermission_fails"); 864 } 865 866 @Test testRebootlessUpdate_noPreInstalledApex_fails()867 public void testRebootlessUpdate_noPreInstalledApex_fails() throws Exception { 868 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 869 870 runPhase("testRebootlessUpdate_noPreInstalledApex_fails"); 871 } 872 873 @Test testRebootlessUpdate_unsignedPayload_fails()874 public void testRebootlessUpdate_unsignedPayload_fails() throws Exception { 875 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 876 877 runPhase("testRebootlessUpdate_unsignedPayload_fails"); 878 } 879 880 @Test testRebootlessUpdate_payloadSignedWithDifferentKey_fails()881 public void testRebootlessUpdate_payloadSignedWithDifferentKey_fails() throws Exception { 882 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 883 884 runPhase("testRebootlessUpdate_payloadSignedWithDifferentKey_fails"); 885 } 886 887 @Test testRebootlessUpdate_outerContainerSignedWithDifferentCert_fails()888 public void testRebootlessUpdate_outerContainerSignedWithDifferentCert_fails() 889 throws Exception { 890 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 891 892 runPhase("testRebootlessUpdate_outerContainerSignedWithDifferentCert_fails"); 893 } 894 895 @Test testRebootlessUpdate_outerContainerUnsigned_fails()896 public void testRebootlessUpdate_outerContainerUnsigned_fails() throws Exception { 897 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 898 899 runPhase("testRebootlessUpdate_outerContainerUnsigned_fails"); 900 } 901 902 @Test testRebootlessUpdate_targetsOlderSdk_fails()903 public void testRebootlessUpdate_targetsOlderSdk_fails() throws Exception { 904 assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); 905 906 runPhase("testRebootlessUpdate_targetsOlderSdk_fails"); 907 } 908 readApexInfoList()909 private List<ApexInfo> readApexInfoList() throws Exception { 910 File file = getDevice().pullFile("/apex/apex-info-list.xml"); 911 try (FileInputStream stream = new FileInputStream(file)) { 912 return XmlParser.readApexInfoList(stream).getApexInfo(); 913 } 914 } 915 getShimApexInfo()916 private ApexInfo getShimApexInfo() throws Exception { 917 List<ApexInfo> temp = 918 readApexInfoList().stream() 919 .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME)) 920 .collect(Collectors.toList()); 921 assertThat(temp).hasSize(1); 922 return temp.get(0); 923 } 924 getActiveShimApexInfo()925 private ApexInfo getActiveShimApexInfo() throws Exception { 926 return readApexInfoList().stream() 927 .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME)) 928 .filter(ApexInfo::getIsActive) 929 .findAny() 930 .orElseThrow(() -> 931 new AssertionError( 932 "No active version of " + SHIM_APEX_PACKAGE_NAME 933 + " found in /apex/apex-info-list.xml")); 934 } 935 936 /** 937 * Store the component name of the default launcher. This value will be used to reset the 938 * default launcher to its correct component upon test completion. 939 */ storeDefaultLauncher()940 private void storeDefaultLauncher() throws DeviceNotAvailableException { 941 final String PREFIX = "Launcher: ComponentInfo{"; 942 final String POSTFIX = "}"; 943 for (String s : getDevice().executeShellCommand("cmd shortcut get-default-launcher") 944 .split("\n")) { 945 if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) { 946 mDefaultLauncher = s.substring(PREFIX.length(), s.length() - POSTFIX.length()); 947 } 948 } 949 } 950 951 /** 952 * Set the default launcher to a given component. 953 * If set to the broadcast receiver component of this test app, this will allow the test app to 954 * receive SESSION_COMMITTED broadcasts. 955 */ setDefaultLauncher(String launcherComponent)956 private void setDefaultLauncher(String launcherComponent) throws DeviceNotAvailableException { 957 assertThat(launcherComponent).isNotEmpty(); 958 int user = getDevice().getCurrentUser(); 959 getDevice().executeShellCommand( 960 "cmd package set-home-activity --user " + user + " " + launcherComponent); 961 } 962 963 private static final class FailedTestLogHook extends TestWatcher { 964 965 private final BaseHostJUnit4Test mInstance; 966 private String mStagedSessionsBeforeTest; 967 FailedTestLogHook(BaseHostJUnit4Test instance)968 private FailedTestLogHook(BaseHostJUnit4Test instance) { 969 this.mInstance = instance; 970 } 971 972 @Override failed(Throwable e, Description description)973 protected void failed(Throwable e, Description description) { 974 String stagedSessionsAfterTest = getStagedSessions(); 975 Log.e(TAG, "Test " + description + " failed.\n" 976 + "Staged sessions before test started:\n" + mStagedSessionsBeforeTest + "\n" 977 + "Staged sessions after test failed:\n" + stagedSessionsAfterTest); 978 } 979 980 @Override starting(Description description)981 protected void starting(Description description) { 982 mStagedSessionsBeforeTest = getStagedSessions(); 983 } 984 getStagedSessions()985 private String getStagedSessions() { 986 try { 987 return mInstance.getDevice().executeShellV2Command("pm get-stagedsessions").getStdout(); 988 } catch (DeviceNotAvailableException e) { 989 Log.e(TAG, e); 990 return "Failed to get staged sessions"; 991 } 992 } 993 } 994 isDebuggable()995 private boolean isDebuggable() throws Exception { 996 return getDevice().getIntProperty("ro.debuggable", 0) == 1; 997 } 998 } 999