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.google.common.truth.Truth.assertThat; 20 21 import static org.hamcrest.CoreMatchers.endsWith; 22 import static org.hamcrest.CoreMatchers.equalTo; 23 import static org.hamcrest.CoreMatchers.not; 24 import static org.junit.Assume.assumeFalse; 25 import static org.junit.Assume.assumeThat; 26 import static org.junit.Assume.assumeTrue; 27 28 import android.platform.test.annotations.LargeTest; 29 30 import com.android.ddmlib.Log; 31 import com.android.tradefed.device.DeviceNotAvailableException; 32 import com.android.tradefed.device.ITestDevice; 33 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 34 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 35 36 import org.junit.After; 37 import org.junit.Before; 38 import org.junit.Rule; 39 import org.junit.Test; 40 import org.junit.rules.TestWatcher; 41 import org.junit.runner.Description; 42 import org.junit.runner.RunWith; 43 44 @RunWith(DeviceJUnit4ClassRunner.class) 45 public class StagedInstallTest extends BaseHostJUnit4Test { 46 47 private static final String TAG = "StagedInstallTest"; 48 49 private static final String SHIM_APEX_PACKAGE_NAME = "com.android.apex.cts.shim"; 50 51 @Rule 52 public final FailedTestLogHook mFailedTestLogHook = new FailedTestLogHook(this); 53 54 /** 55 * Runs the given phase of a test by calling into the device. 56 * Throws an exception if the test phase fails. 57 * <p> 58 * For example, <code>runPhase("testInstallStagedApkCommit");</code> 59 */ runPhase(String phase)60 private void runPhase(String phase) throws Exception { 61 assertThat(runDeviceTests("com.android.tests.stagedinstall", 62 "com.android.tests.stagedinstall.StagedInstallTest", 63 phase)).isTrue(); 64 } 65 66 // We do not assert the success of cleanup phase since it might fail due to flaky reasons. cleanUp()67 private void cleanUp() throws Exception { 68 try { 69 runDeviceTests("com.android.tests.stagedinstall", 70 "com.android.tests.stagedinstall.StagedInstallTest", 71 "cleanUp"); 72 } catch (AssertionError e) { 73 Log.e(TAG, e); 74 } 75 } 76 77 @Before setUp()78 public void setUp() throws Exception { 79 cleanUp(); 80 uninstallShimApexIfNecessary(); 81 } 82 83 @After tearDown()84 public void tearDown() throws Exception { 85 cleanUp(); 86 uninstallShimApexIfNecessary(); 87 } 88 89 /** 90 * Tests for staged install involving only one apk. 91 */ 92 @Test 93 @LargeTest testInstallStagedApk()94 public void testInstallStagedApk() throws Exception { 95 assumeSystemUser(); 96 runPhase("testInstallStagedApk_Commit"); 97 getDevice().reboot(); 98 runPhase("testInstallStagedApk_VerifyPostReboot"); 99 runPhase("testInstallStagedApk_AbandonSessionIsNoop"); 100 } 101 102 @Test testFailInstallIfNoPermission()103 public void testFailInstallIfNoPermission() throws Exception { 104 runPhase("testFailInstallIfNoPermission"); 105 } 106 107 @Test 108 @LargeTest testAbandonStagedApkBeforeReboot()109 public void testAbandonStagedApkBeforeReboot() throws Exception { 110 runPhase("testAbandonStagedApkBeforeReboot_CommitAndAbandon"); 111 getDevice().reboot(); 112 runPhase("testAbandonStagedApkBeforeReboot_VerifyPostReboot"); 113 } 114 115 @Test 116 @LargeTest testInstallMultipleStagedApks()117 public void testInstallMultipleStagedApks() throws Exception { 118 assumeSystemUser(); 119 runPhase("testInstallMultipleStagedApks_Commit"); 120 getDevice().reboot(); 121 runPhase("testInstallMultipleStagedApks_VerifyPostReboot"); 122 } 123 assumeSystemUser()124 private void assumeSystemUser() throws Exception { 125 String systemUser = "0"; 126 assumeThat("Current user is not system user", 127 getDevice().executeShellCommand("am get-current-user").trim(), equalTo(systemUser)); 128 } 129 130 @Test testGetActiveStagedSessions()131 public void testGetActiveStagedSessions() throws Exception { 132 assumeTrue(isCheckpointSupported()); 133 runPhase("testGetActiveStagedSessions"); 134 } 135 136 /** 137 * Verifies that active staged session fulfils conditions stated at 138 * {@link PackageInstaller.SessionInfo#isStagedSessionActive} 139 */ 140 @Test testIsStagedSessionActive()141 public void testIsStagedSessionActive() throws Exception { 142 runPhase("testIsStagedSessionActive"); 143 } 144 145 @Test testGetActiveStagedSessionsNoSessionActive()146 public void testGetActiveStagedSessionsNoSessionActive() throws Exception { 147 runPhase("testGetActiveStagedSessionsNoSessionActive"); 148 } 149 150 @Test testGetActiveStagedSessions_MultiApkSession()151 public void testGetActiveStagedSessions_MultiApkSession() throws Exception { 152 assumeTrue(isCheckpointSupported()); 153 runPhase("testGetActiveStagedSessions_MultiApkSession"); 154 } 155 156 @Test testStagedInstallDowngrade_DowngradeNotRequested_Fails()157 public void testStagedInstallDowngrade_DowngradeNotRequested_Fails() throws Exception { 158 runPhase("testStagedInstallDowngrade_DowngradeNotRequested_Fails_Commit"); 159 } 160 161 @Test 162 @LargeTest testStagedInstallDowngrade_DowngradeRequested_DebugBuild()163 public void testStagedInstallDowngrade_DowngradeRequested_DebugBuild() throws Exception { 164 assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user"))); 165 166 runPhase("testStagedInstallDowngrade_DowngradeRequested_Commit"); 167 getDevice().reboot(); 168 runPhase("testStagedInstallDowngrade_DowngradeRequested_DebugBuild_VerifyPostReboot"); 169 } 170 171 @Test testStagedInstallDowngrade_DowngradeRequested_UserBuild()172 public void testStagedInstallDowngrade_DowngradeRequested_UserBuild() throws Exception { 173 assumeThat(getDevice().getBuildFlavor(), endsWith("-user")); 174 runPhase("testStagedInstallDowngrade_DowngradeRequested_Fails_Commit"); 175 } 176 177 @Test testShimApexShouldPreInstalledIfUpdatingApexIsSupported()178 public void testShimApexShouldPreInstalledIfUpdatingApexIsSupported() throws Exception { 179 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 180 181 final ITestDevice.ApexInfo shimApex = getShimApex(); 182 assertThat(shimApex.versionCode).isEqualTo(1); 183 } 184 185 @Test 186 @LargeTest testInstallStagedApex()187 public void testInstallStagedApex() throws Exception { 188 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 189 190 runPhase("testInstallStagedApex_Commit"); 191 getDevice().reboot(); 192 runPhase("testInstallStagedApex_VerifyPostReboot"); 193 } 194 195 @Test testInstallStagedApexAndApk()196 public void testInstallStagedApexAndApk() throws Exception { 197 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 198 199 runPhase("testInstallStagedApexAndApk_Commit"); 200 getDevice().reboot(); 201 runPhase("testInstallStagedApexAndApk_VerifyPostReboot"); 202 } 203 204 @Test testsFailsNonStagedApexInstall()205 public void testsFailsNonStagedApexInstall() throws Exception { 206 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 207 208 runPhase("testsFailsNonStagedApexInstall"); 209 } 210 211 @Test testInstallStagedNonPreInstalledApex_Fails()212 public void testInstallStagedNonPreInstalledApex_Fails() throws Exception { 213 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 214 215 runPhase("testInstallStagedNonPreInstalledApex_Fails"); 216 } 217 218 @Test 219 @LargeTest testStageApkWithSameNameAsApexShouldFail()220 public void testStageApkWithSameNameAsApexShouldFail() throws Exception { 221 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 222 223 runPhase("testStageApkWithSameNameAsApexShouldFail_Commit"); 224 getDevice().reboot(); 225 runPhase("testStageApkWithSameNameAsApexShouldFail_VerifyPostReboot"); 226 } 227 228 @Test testNonStagedInstallApkWithSameNameAsApexShouldFail()229 public void testNonStagedInstallApkWithSameNameAsApexShouldFail() throws Exception { 230 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 231 runPhase("testNonStagedInstallApkWithSameNameAsApexShouldFail"); 232 } 233 234 @Test 235 @LargeTest testStagedInstallDowngradeApex_DowngradeNotRequested_Fails()236 public void testStagedInstallDowngradeApex_DowngradeNotRequested_Fails() throws Exception { 237 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 238 239 installV3Apex(); 240 runPhase("testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_Commit"); 241 getDevice().reboot(); 242 runPhase("testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_VerifyPostReboot"); 243 } 244 245 @Test 246 @LargeTest testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild()247 public void testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild() throws Exception { 248 assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user"))); 249 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 250 251 installV3Apex(); 252 runPhase("testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_Commit"); 253 getDevice().reboot(); 254 runPhase("testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_VerifyPostReboot"); 255 } 256 257 @Test 258 @LargeTest testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails()259 public void testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails() 260 throws Exception { 261 assumeThat(getDevice().getBuildFlavor(), endsWith("-user")); 262 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 263 264 installV3Apex(); 265 runPhase("testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_Commit"); 266 getDevice().reboot(); 267 runPhase("testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_" 268 + "VerifyPostReboot"); 269 } 270 271 @Test 272 @LargeTest testStagedInstallDowngradeApexToSystemVersion_DebugBuild()273 public void testStagedInstallDowngradeApexToSystemVersion_DebugBuild() throws Exception { 274 assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user"))); 275 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 276 277 installV2Apex(); 278 runPhase("testStagedInstallDowngradeApexToSystemVersion_DebugBuild_Commit"); 279 getDevice().reboot(); 280 runPhase("testStagedInstallDowngradeApexToSystemVersion_DebugBuild_VerifyPostReboot"); 281 } 282 283 @Test 284 @LargeTest testInstallStagedApex_SameGrade()285 public void testInstallStagedApex_SameGrade() throws Exception { 286 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 287 288 installV3Apex(); 289 installV3Apex(); 290 } 291 292 @Test testInstallApex_DeviceDoesNotSupportApex_Fails()293 public void testInstallApex_DeviceDoesNotSupportApex_Fails() throws Exception { 294 assumeFalse("Device supports updating APEX", isUpdatingApexSupported()); 295 296 runPhase("testInstallApex_DeviceDoesNotSupportApex_Fails"); 297 } 298 installV2Apex()299 private void installV2Apex()throws Exception { 300 runPhase("testInstallV2Apex_Commit"); 301 getDevice().reboot(); 302 runPhase("testInstallV2Apex_VerifyPostReboot"); 303 } 304 installV2SignedBobApex()305 private void installV2SignedBobApex() throws Exception { 306 runPhase("testInstallV2SignedBobApex_Commit"); 307 getDevice().reboot(); 308 runPhase("testInstallV2SignedBobApex_VerifyPostReboot"); 309 } 310 installV3Apex()311 private void installV3Apex()throws Exception { 312 runPhase("testInstallV3Apex_Commit"); 313 getDevice().reboot(); 314 runPhase("testInstallV3Apex_VerifyPostReboot"); 315 } 316 317 @Test testFailsInvalidApexInstall()318 public void testFailsInvalidApexInstall() throws Exception { 319 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 320 runPhase("testFailsInvalidApexInstall_Commit"); 321 runPhase("testFailsInvalidApexInstall_AbandonSessionIsNoop"); 322 } 323 324 @Test testStagedApkSessionCallbacks()325 public void testStagedApkSessionCallbacks() throws Exception { 326 runPhase("testStagedApkSessionCallbacks"); 327 } 328 329 @Test 330 @LargeTest testInstallStagedApexWithoutApexSuffix()331 public void testInstallStagedApexWithoutApexSuffix() throws Exception { 332 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 333 334 runPhase("testInstallStagedApexWithoutApexSuffix_Commit"); 335 getDevice().reboot(); 336 runPhase("testInstallStagedApexWithoutApexSuffix_VerifyPostReboot"); 337 } 338 339 @Test 340 @LargeTest testInstallStagedNoHashtreeApex()341 public void testInstallStagedNoHashtreeApex() throws Exception { 342 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 343 344 runPhase("testInstallStagedNoHashtreeApex_Commit"); 345 getDevice().reboot(); 346 runPhase("testInstallStagedNoHashtreeApex_VerifyPostReboot"); 347 } 348 349 @Test testRejectsApexDifferentCertificate()350 public void testRejectsApexDifferentCertificate() throws Exception { 351 runPhase("testRejectsApexDifferentCertificate"); 352 } 353 354 /** 355 * Tests for staged install involving rotated keys. 356 * 357 * Here alice means the original default key that cts.shim.v1 package was signed with and 358 * bob is the new key alice rotates to. Where ambiguous, we will refer keys as alice and bob 359 * instead of "old key" and "new key". 360 * 361 * By default, rotated keys have rollback capability enabled for old keys. When we remove 362 * rollback capability from a key, it is called "Distrusting Event" and the distrusted key can 363 * not update the app anymore. 364 */ 365 366 // Should not be able to update with a key that has not been rotated. 367 @Test testUpdateWithDifferentKeyButNoRotation()368 public void testUpdateWithDifferentKeyButNoRotation() throws Exception { 369 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 370 371 runPhase("testUpdateWithDifferentKeyButNoRotation"); 372 } 373 374 // Should be able to update with a key that has been rotated. 375 @Test 376 @LargeTest testUpdateWithDifferentKey()377 public void testUpdateWithDifferentKey() throws Exception { 378 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 379 380 runPhase("testUpdateWithDifferentKey_Commit"); 381 getDevice().reboot(); 382 runPhase("testUpdateWithDifferentKey_VerifyPostReboot"); 383 } 384 385 // Should not be able to update with a key that is no longer trusted (i.e, has no 386 // rollback capability) 387 @Test 388 @LargeTest testUntrustedOldKeyIsRejected()389 public void testUntrustedOldKeyIsRejected() throws Exception { 390 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 391 392 installV2SignedBobApex(); 393 runPhase("testUntrustedOldKeyIsRejected"); 394 } 395 396 // Should be able to update with an old key which is trusted 397 @Test 398 @LargeTest testTrustedOldKeyIsAccepted()399 public void testTrustedOldKeyIsAccepted() throws Exception { 400 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 401 402 runPhase("testTrustedOldKeyIsAccepted_Commit"); 403 getDevice().reboot(); 404 runPhase("testTrustedOldKeyIsAccepted_CommitPostReboot"); 405 getDevice().reboot(); 406 runPhase("testTrustedOldKeyIsAccepted_VerifyPostReboot"); 407 } 408 409 // Should be able to update further with rotated key 410 @Test 411 @LargeTest testAfterRotationNewKeyCanUpdateFurther()412 public void testAfterRotationNewKeyCanUpdateFurther() throws Exception { 413 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 414 415 installV2SignedBobApex(); 416 runPhase("testAfterRotationNewKeyCanUpdateFurther_CommitPostReboot"); 417 getDevice().reboot(); 418 runPhase("testAfterRotationNewKeyCanUpdateFurther_VerifyPostReboot"); 419 } 420 421 @Test 422 @LargeTest testAfterRotationNewKeyCanUpdateFurtherWithoutLineage()423 public void testAfterRotationNewKeyCanUpdateFurtherWithoutLineage() throws Exception { 424 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 425 426 installV2SignedBobApex(); 427 runPhase("testAfterRotationNewKeyCanUpdateFurtherWithoutLineage"); 428 } 429 430 /** 431 * Tests for staging and installing multiple staged sessions. 432 */ 433 434 // Should fail to stage multiple sessions when check-point is not available 435 @Test testFailStagingMultipleSessionsIfNoCheckPoint()436 public void testFailStagingMultipleSessionsIfNoCheckPoint() throws Exception { 437 assumeFalse(isCheckpointSupported()); 438 runPhase("testFailStagingMultipleSessionsIfNoCheckPoint"); 439 } 440 441 @Test testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk()442 public void testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk() throws Exception { 443 runPhase("testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk"); 444 } 445 446 @Test testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk()447 public void testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk() 448 throws Exception { 449 assumeTrue(isCheckpointSupported()); 450 runPhase("testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk"); 451 } 452 453 @Test testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk()454 public void testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk() throws Exception { 455 assumeTrue(isCheckpointSupported()); 456 runPhase("testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk"); 457 } 458 459 // Test for installing multiple staged sessions at the same time 460 @Test 461 @LargeTest testMultipleStagedInstall_ApkOnly()462 public void testMultipleStagedInstall_ApkOnly() throws Exception { 463 assumeTrue(isCheckpointSupported()); 464 runPhase("testMultipleStagedInstall_ApkOnly_Commit"); 465 getDevice().reboot(); 466 runPhase("testMultipleStagedInstall_ApkOnly_VerifyPostReboot"); 467 } 468 469 // If apk installation fails in one staged session, then all staged session should fail. 470 @Test 471 @LargeTest testInstallMultipleStagedSession_PartialFail_ApkOnly()472 public void testInstallMultipleStagedSession_PartialFail_ApkOnly() throws Exception { 473 assumeTrue(isCheckpointSupported()); 474 runPhase("testInstallMultipleStagedSession_PartialFail_ApkOnly_Commit"); 475 getDevice().reboot(); 476 runPhase("testInstallMultipleStagedSession_PartialFail_ApkOnly_VerifyPostReboot"); 477 } 478 479 @Test 480 @LargeTest testSamegradeSystemApex()481 public void testSamegradeSystemApex() throws Exception { 482 assumeTrue("Device does not support updating APEX", isUpdatingApexSupported()); 483 484 runPhase("testSamegradeSystemApex_Commit"); 485 getDevice().reboot(); 486 runPhase("testSamegradeSystemApex_VerifyPostReboot"); 487 } 488 489 @Test testInstallApkChangingFingerprint()490 public void testInstallApkChangingFingerprint() throws Exception { 491 assumeThat(getDevice().getBuildFlavor(), not(endsWith("-user"))); 492 493 try { 494 getDevice().executeShellCommand("setprop persist.pm.mock-upgrade true"); 495 runPhase("testInstallApkChangingFingerprint"); 496 getDevice().reboot(); 497 runPhase("testInstallApkChangingFingerprint_VerifyAborted"); 498 } finally { 499 getDevice().executeShellCommand("setprop persist.pm.mock-upgrade false"); 500 } 501 } 502 isUpdatingApexSupported()503 private boolean isUpdatingApexSupported() throws Exception { 504 final String updatable = getDevice().getProperty("ro.apex.updatable"); 505 return updatable != null && updatable.equals("true"); 506 } 507 508 /** 509 * Uninstalls a shim apex only if it's latest version is installed on /data partition (i.e. 510 * it has a version higher than {@code 1}). 511 * 512 * <p>This is purely to optimize tests run time. Since uninstalling an apex requires a reboot, 513 * and only a small subset of tests successfully install an apex, this code avoids ~10 514 * unnecessary reboots. 515 */ uninstallShimApexIfNecessary()516 private void uninstallShimApexIfNecessary() throws Exception { 517 if (!isUpdatingApexSupported()) { 518 // Device doesn't support updating apex. Nothing to uninstall. 519 return; 520 } 521 final ITestDevice.ApexInfo shimApex = getShimApex(); 522 if (shimApex.versionCode == 1) { 523 // System version is active, skipping uninstalling active apex and rebooting the device. 524 return; 525 } 526 // Non system version is active, need to uninstall it and reboot the device. 527 final String errorMessage = getDevice().uninstallPackage(SHIM_APEX_PACKAGE_NAME); 528 Log.i(TAG, "Uninstalling shim apex " + shimApex); 529 if (errorMessage != null) { 530 throw new AssertionError("Failed to uninstall " + shimApex); 531 } 532 getDevice().reboot(); 533 assertThat(getShimApex().versionCode).isEqualTo(1L); 534 } 535 getShimApex()536 private ITestDevice.ApexInfo getShimApex() throws DeviceNotAvailableException { 537 return getDevice().getActiveApexes().stream().filter( 538 apex -> apex.name.equals(SHIM_APEX_PACKAGE_NAME)).findAny().orElseThrow( 539 () -> new AssertionError("Can't find " + SHIM_APEX_PACKAGE_NAME)); 540 } 541 542 private static final class FailedTestLogHook extends TestWatcher { 543 544 private final BaseHostJUnit4Test mInstance; 545 private String mStagedSessionsBeforeTest; 546 FailedTestLogHook(BaseHostJUnit4Test instance)547 private FailedTestLogHook(BaseHostJUnit4Test instance) { 548 this.mInstance = instance; 549 } 550 551 @Override failed(Throwable e, Description description)552 protected void failed(Throwable e, Description description) { 553 String stagedSessionsAfterTest = getStagedSessions(); 554 Log.e(TAG, "Test " + description + " failed.\n" 555 + "Staged sessions before test started:\n" + mStagedSessionsBeforeTest + "\n" 556 + "Staged sessions after test failed:\n" + stagedSessionsAfterTest); 557 } 558 559 @Override starting(Description description)560 protected void starting(Description description) { 561 mStagedSessionsBeforeTest = getStagedSessions(); 562 } 563 getStagedSessions()564 private String getStagedSessions() { 565 try { 566 return mInstance.getDevice().executeShellV2Command("pm get-stagedsessions").getStdout(); 567 } catch (DeviceNotAvailableException e) { 568 Log.e(TAG, e); 569 return "Failed to get staged sessions"; 570 } 571 } 572 } 573 isCheckpointSupported()574 private boolean isCheckpointSupported() throws Exception { 575 try { 576 runPhase("isCheckpointSupported"); 577 return true; 578 } catch (AssertionError ignore) { 579 return false; 580 } 581 } 582 } 583