1 /* 2 * Copyright (C) 2020 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.stagedinstallinternal.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.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 import static org.junit.Assume.assumeTrue; 26 27 import android.cts.install.lib.host.InstallUtilsHost; 28 import android.platform.test.annotations.LargeTest; 29 30 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 31 import com.android.ddmlib.Log; 32 import com.android.tests.rollback.host.AbandonSessionsRule; 33 import com.android.tradefed.device.DeviceNotAvailableException; 34 import com.android.tradefed.device.PackageInfo; 35 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 36 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 37 import com.android.tradefed.util.CommandResult; 38 import com.android.tradefed.util.CommandStatus; 39 import com.android.tradefed.util.ProcessInfo; 40 41 import org.junit.After; 42 import org.junit.Before; 43 import org.junit.Rule; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 47 import java.io.BufferedWriter; 48 import java.io.File; 49 import java.io.FileWriter; 50 import java.util.List; 51 import java.util.stream.Collectors; 52 53 @RunWith(DeviceJUnit4ClassRunner.class) 54 public class StagedInstallInternalTest extends BaseHostJUnit4Test { 55 56 private static final String TAG = StagedInstallInternalTest.class.getSimpleName(); 57 private static final long SYSTEM_SERVER_TIMEOUT_MS = 60 * 1000; 58 59 @Rule 60 public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this); 61 private static final String SHIM_V2 = "com.android.apex.cts.shim.v2.apex"; 62 private static final String APEX_WRONG_SHA = "com.android.apex.cts.shim.v2_wrong_sha.apex"; 63 private static final String APK_A = "TestAppAv1.apk"; 64 private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test"; 65 private static final String APEXD_TEST_APEX = "apex.apexd_test.apex"; 66 private static final String FAKE_APEX_SYSTEM_SERVER_APEX = "test_com.android.server.apex"; 67 private static final String REBOOTLESS_V1 = "test.rebootless_apex_v1.apex"; 68 private static final String REBOOTLESS_V2 = "test.rebootless_apex_v2.apex"; 69 70 private static final String TEST_VENDOR_APEX_ALLOW_LIST = 71 "/vendor/etc/sysconfig/test-vendor-apex-allow-list.xml"; 72 73 private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); 74 75 /** 76 * Runs the given phase of a test by calling into the device. 77 * Throws an exception if the test phase fails. 78 * <p> 79 * For example, <code>runPhase("testApkOnlyEnableRollback");</code> 80 */ runPhase(String phase)81 private void runPhase(String phase) throws Exception { 82 assertTrue(runDeviceTests("com.android.tests.stagedinstallinternal", 83 "com.android.tests.stagedinstallinternal.StagedInstallInternalTest", 84 phase)); 85 } 86 87 // We do not assert the success of cleanup phase since it might fail due to flaky reasons. cleanUp()88 private void cleanUp() throws Exception { 89 try { 90 runDeviceTests("com.android.tests.stagedinstallinternal", 91 "com.android.tests.stagedinstallinternal.StagedInstallInternalTest", 92 "cleanUp"); 93 } catch (AssertionError e) { 94 Log.e(TAG, e); 95 } 96 deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex", 97 "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex", 98 "/data/apex/active/" + SHIM_APEX_PACKAGE_NAME + "*.apex", 99 "/system/apex/test.rebootless_apex_v*.apex", 100 "/vendor/apex/test.rebootless_apex_v*.apex", 101 "/data/apex/active/test.apex.rebootless*.apex", 102 "/system/app/TestApp/TestAppAv1.apk", 103 TEST_VENDOR_APEX_ALLOW_LIST); 104 } 105 106 @Before setUp()107 public void setUp() throws Exception { 108 cleanUp(); 109 } 110 111 @After tearDown()112 public void tearDown() throws Exception { 113 cleanUp(); 114 } 115 116 /** 117 * Deletes files and reboots the device if necessary. 118 * @param files the paths of files which might contain wildcards 119 */ deleteFiles(String... files)120 private void deleteFiles(String... files) throws Exception { 121 if (!getDevice().isAdbRoot()) { 122 getDevice().enableAdbRoot(); 123 } 124 125 boolean found = false; 126 boolean remountSystem = false; 127 boolean remountVendor = false; 128 for (String file : files) { 129 CommandResult result = getDevice().executeShellV2Command("ls " + file); 130 if (result.getStatus() == CommandStatus.SUCCESS) { 131 found = true; 132 if (file.startsWith("/system")) { 133 remountSystem = true; 134 } else if (file.startsWith("/vendor")) { 135 remountVendor = true; 136 } 137 } 138 } 139 140 if (found) { 141 if (remountSystem) { 142 getDevice().remountSystemWritable(); 143 } 144 if (remountVendor) { 145 getDevice().remountVendorWritable(); 146 } 147 for (String file : files) { 148 getDevice().executeShellCommand("rm -rf " + file); 149 } 150 getDevice().reboot(); 151 } 152 } 153 pushTestApex(String fileName)154 private void pushTestApex(String fileName) throws Exception { 155 pushTestApex(fileName, "system"); 156 } 157 pushTestApex(String fileName, String partition)158 private void pushTestApex(String fileName, String partition) throws Exception { 159 CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild()); 160 final File apex = buildHelper.getTestFile(fileName); 161 if (!getDevice().isAdbRoot()) { 162 getDevice().enableAdbRoot(); 163 } 164 if ("system".equals(partition)) { 165 getDevice().remountSystemWritable(); 166 } else if ("vendor".equals(partition)) { 167 getDevice().remountVendorWritable(); 168 } 169 assertTrue(getDevice().pushFile(apex, "/" + partition + "/apex/" + fileName)); 170 } 171 installTestApex(String fileName, String... extraArgs)172 private void installTestApex(String fileName, String... extraArgs) throws Exception { 173 CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild()); 174 final File apex = buildHelper.getTestFile(fileName); 175 getDevice().installPackage(apex, false, extraArgs); 176 } 177 pushTestVendorApexAllowList(String installerPackageName)178 private void pushTestVendorApexAllowList(String installerPackageName) throws Exception { 179 if (!getDevice().isAdbRoot()) { 180 getDevice().enableAdbRoot(); 181 } 182 getDevice().remountVendorWritable(); 183 File file = File.createTempFile("test-vendor-apex-allow-list", ".xml"); 184 try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { 185 final String fmt = 186 "<config>\n" 187 + " <allowed-vendor-apex package=\"test.apex.rebootless\" " 188 + " installerPackage=\"%s\" />\n" 189 + "</config>"; 190 writer.write(String.format(fmt, installerPackageName)); 191 } 192 getDevice().pushFile(file, TEST_VENDOR_APEX_ALLOW_LIST); 193 } 194 195 /** 196 * Tests app info flags are set correctly when updating a system app. 197 */ 198 @Test testUpdateSystemApp()199 public void testUpdateSystemApp() throws Exception { 200 // Push TestAppAv1.apk to /system 201 final File apkFile = mHostUtils.getTestFile(APK_A); 202 if (!getDevice().isAdbRoot()) { 203 getDevice().enableAdbRoot(); 204 } 205 getDevice().remountSystemWritable(); 206 assertTrue(getDevice().pushFile(apkFile, "/system/app/TestApp/" + apkFile.getName())); 207 getDevice().reboot(); 208 209 runPhase("testUpdateSystemApp_InstallV2"); 210 getDevice().reboot(); 211 runPhase("testUpdateSystemApp_PostInstallV2"); 212 } 213 214 /** 215 * Tests that duplicate packages in apk-in-apex and apk should fail to install. 216 */ 217 @Test 218 @LargeTest testDuplicateApkInApexShouldFail()219 public void testDuplicateApkInApexShouldFail() throws Exception { 220 pushTestApex(APK_IN_APEX_TESTAPEX_NAME + "_v1.apex"); 221 getDevice().reboot(); 222 223 runPhase("testDuplicateApkInApexShouldFail_Commit"); 224 getDevice().reboot(); 225 runPhase("testDuplicateApkInApexShouldFail_Verify"); 226 } 227 228 /** 229 * Tests the cache of apk-in-apex is pruned and rebuilt correctly. 230 */ 231 @Test 232 @LargeTest testApkInApexPruneCache()233 public void testApkInApexPruneCache() throws Exception { 234 final String apkInApexPackageName = "com.android.cts.install.lib.testapp.A"; 235 236 pushTestApex(APK_IN_APEX_TESTAPEX_NAME + "_v1.apex"); 237 getDevice().reboot(); 238 PackageInfo pi = getDevice().getAppPackageInfo(apkInApexPackageName); 239 assertThat(Integer.parseInt(pi.getVersionCode())).isEqualTo(1); 240 assertThat(pi.getCodePath()).startsWith("/apex/" + APK_IN_APEX_TESTAPEX_NAME); 241 242 installPackage(APK_IN_APEX_TESTAPEX_NAME + "_v2.apex", "--staged"); 243 getDevice().reboot(); 244 pi = getDevice().getAppPackageInfo(apkInApexPackageName); 245 // The version code of apk-in-apex will be stale if the cache is not rebuilt 246 assertThat(Integer.parseInt(pi.getVersionCode())).isEqualTo(2); 247 assertThat(pi.getCodePath()).startsWith("/apex/" + APK_IN_APEX_TESTAPEX_NAME); 248 } 249 250 @Test testSystemServerRestartDoesNotAffectStagedSessions()251 public void testSystemServerRestartDoesNotAffectStagedSessions() throws Exception { 252 runPhase("testSystemServerRestartDoesNotAffectStagedSessions_Commit"); 253 restartSystemServer(); 254 runPhase("testSystemServerRestartDoesNotAffectStagedSessions_Verify"); 255 } 256 257 // Test waiting time for staged session to be ready using adb staged install can be altered 258 @Test testAdbStagdReadyTimeoutFlagWorks()259 public void testAdbStagdReadyTimeoutFlagWorks() throws Exception { 260 assumeTrue("Device does not support updating APEX", 261 mHostUtils.isApexUpdateSupported()); 262 263 final File apexFile = mHostUtils.getTestFile(SHIM_V2); 264 final String output = getDevice().executeAdbCommand("install", "--staged", 265 "--staged-ready-timeout", "60000", apexFile.getAbsolutePath()); 266 assertThat(output).contains("Reboot device to apply staged session"); 267 final String sessionId = getDevice().executeShellCommand( 268 "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim(); 269 assertThat(sessionId).isNotEmpty(); 270 } 271 272 // Test adb staged installation wait for session to be ready by default 273 @Test testAdbStagedInstallWaitsTillReadyByDefault()274 public void testAdbStagedInstallWaitsTillReadyByDefault() throws Exception { 275 assumeTrue("Device does not support updating APEX", 276 mHostUtils.isApexUpdateSupported()); 277 278 final File apexFile = mHostUtils.getTestFile(SHIM_V2); 279 final String output = getDevice().executeAdbCommand("install", "--staged", 280 apexFile.getAbsolutePath()); 281 assertThat(output).contains("Reboot device to apply staged session"); 282 final String sessionId = getDevice().executeShellCommand( 283 "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim(); 284 assertThat(sessionId).isNotEmpty(); 285 } 286 287 // Test we can skip waiting for staged session to be ready 288 @Test testAdbStagedReadyWaitCanBeSkipped()289 public void testAdbStagedReadyWaitCanBeSkipped() throws Exception { 290 assumeTrue("Device does not support updating APEX", 291 mHostUtils.isApexUpdateSupported()); 292 293 final File apexFile = mHostUtils.getTestFile(SHIM_V2); 294 final String output = getDevice().executeAdbCommand("install", "--staged", 295 "--staged-ready-timeout", "0", apexFile.getAbsolutePath()); 296 assertThat(output).doesNotContain("Reboot device to apply staged session"); 297 assertThat(output).contains("Success"); 298 final String sessionId = getDevice().executeShellCommand( 299 "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim(); 300 assertThat(sessionId).isEmpty(); 301 } 302 303 // Test rollback-app command waits for staged sessions to be ready 304 @Test 305 @LargeTest testAdbRollbackAppWaitsForStagedReady()306 public void testAdbRollbackAppWaitsForStagedReady() throws Exception { 307 assumeTrue("Device does not support updating APEX", 308 mHostUtils.isApexUpdateSupported()); 309 310 final File apexFile = mHostUtils.getTestFile(SHIM_V2); 311 String output = getDevice().executeAdbCommand("install", "--staged", 312 "--enable-rollback", apexFile.getAbsolutePath()); 313 assertThat(output).contains("Reboot device to apply staged session"); 314 getDevice().reboot(); 315 output = getDevice().executeShellCommand("pm rollback-app " + SHIM_APEX_PACKAGE_NAME); 316 assertThat(output).contains("Reboot device to apply staged session"); 317 final String sessionId = getDevice().executeShellCommand( 318 "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim(); 319 assertThat(sessionId).isNotEmpty(); 320 } 321 322 @Test testAdbInstallMultiPackageCommandWorks()323 public void testAdbInstallMultiPackageCommandWorks() throws Exception { 324 assumeTrue("Device does not support updating APEX", 325 mHostUtils.isApexUpdateSupported()); 326 327 final File apexFile = mHostUtils.getTestFile(SHIM_V2); 328 final File apkFile = mHostUtils.getTestFile(APK_A); 329 final String output = getDevice().executeAdbCommand("install-multi-package", 330 apexFile.getAbsolutePath(), apkFile.getAbsolutePath()); 331 assertThat(output).contains("Created parent session"); 332 assertThat(output).contains("Created child session"); 333 assertThat(output).contains("Success. Reboot device to apply staged session"); 334 335 // Ensure there is only one parent session 336 String[] sessionIds = getDevice().executeShellCommand( 337 "pm get-stagedsessions --only-ready --only-parent --only-sessionid").split("\n"); 338 assertThat(sessionIds.length).isEqualTo(1); 339 // Ensure there are two children session 340 sessionIds = getDevice().executeShellCommand( 341 "pm get-stagedsessions --only-ready --only-sessionid").split("\n"); 342 assertThat(sessionIds.length).isEqualTo(3); 343 } 344 345 @Test testAbandonStagedSessionShouldCleanUp()346 public void testAbandonStagedSessionShouldCleanUp() throws Exception { 347 List<String> before = getStagingDirectories(); 348 runPhase("testAbandonStagedSessionShouldCleanUp"); 349 List<String> after = getStagingDirectories(); 350 // The staging directories generated during the test should be deleted 351 assertThat(after).isEqualTo(before); 352 } 353 354 @Test testStagedSessionShouldCleanUpOnVerificationFailure()355 public void testStagedSessionShouldCleanUpOnVerificationFailure() throws Exception { 356 assumeTrue("Device does not support updating APEX", 357 mHostUtils.isApexUpdateSupported()); 358 List<String> before = getStagingDirectories(); 359 runPhase("testStagedSessionShouldCleanUpOnVerificationFailure"); 360 List<String> after = getStagingDirectories(); 361 assertThat(after).isEqualTo(before); 362 } 363 364 @Test 365 @LargeTest testStagedSessionShouldCleanUpOnOnSuccess()366 public void testStagedSessionShouldCleanUpOnOnSuccess() throws Exception { 367 List<String> before = getStagingDirectories(); 368 runPhase("testStagedSessionShouldCleanUpOnOnSuccess_Commit"); 369 assertThat(getStagingDirectories()).isNotEqualTo(before); 370 getDevice().reboot(); 371 runPhase("testStagedSessionShouldCleanUpOnOnSuccess_Verify"); 372 List<String> after = getStagingDirectories(); 373 assertThat(after).isEqualTo(before); 374 } 375 376 @Test 377 @LargeTest testStagedSessionShouldCleanUpOnOnSuccessMultiPackage()378 public void testStagedSessionShouldCleanUpOnOnSuccessMultiPackage() throws Exception { 379 List<String> before = getStagingDirectories(); 380 runPhase("testStagedSessionShouldCleanUpOnOnSuccessMultiPackage_Commit"); 381 assertThat(getStagingDirectories()).isNotEqualTo(before); 382 getDevice().reboot(); 383 runPhase("testStagedSessionShouldCleanUpOnOnSuccess_Verify"); 384 List<String> after = getStagingDirectories(); 385 assertThat(after).isEqualTo(before); 386 } 387 388 @Test testStagedInstallationShouldCleanUpOnValidationFailure()389 public void testStagedInstallationShouldCleanUpOnValidationFailure() throws Exception { 390 List<String> before = getStagingDirectories(); 391 runPhase("testStagedInstallationShouldCleanUpOnValidationFailure"); 392 List<String> after = getStagingDirectories(); 393 assertThat(after).isEqualTo(before); 394 } 395 396 @Test testStagedInstallationShouldCleanUpOnValidationFailureMultiPackage()397 public void testStagedInstallationShouldCleanUpOnValidationFailureMultiPackage() 398 throws Exception { 399 List<String> before = getStagingDirectories(); 400 runPhase("testStagedInstallationShouldCleanUpOnValidationFailureMultiPackage"); 401 List<String> after = getStagingDirectories(); 402 assertThat(after).isEqualTo(before); 403 } 404 405 @Test 406 @LargeTest testOrphanedStagingDirectoryGetsCleanedUpOnReboot()407 public void testOrphanedStagingDirectoryGetsCleanedUpOnReboot() throws Exception { 408 //create random directories in /data/app-staging folder 409 getDevice().enableAdbRoot(); 410 getDevice().executeShellCommand("mkdir /data/app-staging/session_123"); 411 getDevice().executeShellCommand("mkdir /data/app-staging/session_456"); 412 getDevice().disableAdbRoot(); 413 414 assertThat(getStagingDirectories()).contains("session_123"); 415 assertThat(getStagingDirectories()).contains("session_456"); 416 getDevice().reboot(); 417 assertThat(getStagingDirectories()).doesNotContain("session_123"); 418 assertThat(getStagingDirectories()).doesNotContain("session_456"); 419 } 420 421 @Test 422 @LargeTest testFailStagedSessionIfStagingDirectoryDeleted()423 public void testFailStagedSessionIfStagingDirectoryDeleted() throws Exception { 424 // Create a staged session 425 runPhase("testFailStagedSessionIfStagingDirectoryDeleted_Commit"); 426 427 // Delete the staging directory 428 getDevice().enableAdbRoot(); 429 getDevice().executeShellCommand("rm -r /data/app-staging"); 430 getDevice().disableAdbRoot(); 431 432 getDevice().reboot(); 433 434 runPhase("testFailStagedSessionIfStagingDirectoryDeleted_Verify"); 435 } 436 437 @Test testApexActivationFailureIsCapturedInSession()438 public void testApexActivationFailureIsCapturedInSession() throws Exception { 439 // We initiate staging a normal apex update which passes pre-reboot verification. 440 // Then we replace the valid apex waiting in /data/app-staging with something 441 // that cannot be activated and reboot. The apex should fail to activate, which 442 // is what we want for this test. 443 runPhase("testApexActivationFailureIsCapturedInSession_Commit"); 444 final String sessionId = getDevice().executeShellCommand( 445 "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim(); 446 assertThat(sessionId).isNotEmpty(); 447 // Now replace the valid staged apex with something invalid 448 getDevice().enableAdbRoot(); 449 getDevice().executeShellCommand("rm /data/app-staging/session_" + sessionId + "/*"); 450 final File invalidApexFile = mHostUtils.getTestFile(APEX_WRONG_SHA); 451 getDevice().pushFile(invalidApexFile, 452 "/data/app-staging/session_" + sessionId + "/base.apex"); 453 getDevice().reboot(); 454 455 runPhase("testApexActivationFailureIsCapturedInSession_Verify"); 456 } 457 458 @Test testActiveApexIsRevertedOnCheckpointRollback()459 public void testActiveApexIsRevertedOnCheckpointRollback() throws Exception { 460 assumeTrue("Device does not support updating APEX", 461 mHostUtils.isApexUpdateSupported()); 462 assumeTrue("Device does not support file-system checkpoint", 463 mHostUtils.isCheckpointSupported()); 464 465 // Install something so that /data/apex/active is not empty 466 runPhase("testActiveApexIsRevertedOnCheckpointRollback_Prepare"); 467 getDevice().reboot(); 468 469 // Stage another session which will be installed during fs-rollback mode 470 runPhase("testActiveApexIsRevertedOnCheckpointRollback_Commit"); 471 472 // Set checkpoint to 0 so that we enter fs-rollback mode immediately on reboot 473 getDevice().enableAdbRoot(); 474 getDevice().executeShellCommand("vdc checkpoint startCheckpoint 0"); 475 getDevice().disableAdbRoot(); 476 getDevice().reboot(); 477 478 // Verify that session was reverted and we have fallen back to 479 // apex installed during preparation stage. 480 runPhase("testActiveApexIsRevertedOnCheckpointRollback_VerifyPostReboot"); 481 } 482 483 @Test testApexIsNotActivatedIfNotInCheckpointMode()484 public void testApexIsNotActivatedIfNotInCheckpointMode() throws Exception { 485 assumeTrue("Device does not support updating APEX", 486 mHostUtils.isApexUpdateSupported()); 487 assumeTrue("Device does not support file-system checkpoint", 488 mHostUtils.isCheckpointSupported()); 489 490 runPhase("testApexIsNotActivatedIfNotInCheckpointMode_Commit"); 491 // Delete checkpoint file in /metadata so that device thinks 492 // fs-checkpointing was never activated 493 getDevice().enableAdbRoot(); 494 getDevice().executeShellCommand("rm /metadata/vold/checkpoint"); 495 getDevice().disableAdbRoot(); 496 getDevice().reboot(); 497 // Verify that session was not installed when not in fs-checkpoint mode 498 runPhase("testApexIsNotActivatedIfNotInCheckpointMode_VerifyPostReboot"); 499 } 500 501 @Test testApexInstallerNotInAllowListCanNotInstall()502 public void testApexInstallerNotInAllowListCanNotInstall() throws Exception { 503 assumeTrue("Device does not support updating APEX", 504 mHostUtils.isApexUpdateSupported()); 505 506 runPhase("testApexInstallerNotInAllowListCanNotInstall_staged"); 507 runPhase("testApexInstallerNotInAllowListCanNotInstall_nonStaged"); 508 } 509 510 @Test 511 @LargeTest testApexNotInAllowListCanNotInstall()512 public void testApexNotInAllowListCanNotInstall() throws Exception { 513 assumeTrue("Device does not support updating APEX", 514 mHostUtils.isApexUpdateSupported()); 515 516 pushTestApex("test.rebootless_apex_v1.apex"); 517 getDevice().reboot(); 518 519 runPhase("testApexNotInAllowListCanNotInstall_staged"); 520 runPhase("testApexNotInAllowListCanNotInstall_nonStaged"); 521 } 522 523 @Test 524 @LargeTest testVendorApexWrongInstaller()525 public void testVendorApexWrongInstaller() throws Exception { 526 assumeTrue("Device does not support updating APEX", 527 mHostUtils.isApexUpdateSupported()); 528 529 pushTestVendorApexAllowList("com.wrong.installer"); 530 pushTestApex("test.rebootless_apex_v1.apex"); 531 getDevice().reboot(); 532 533 runPhase("testVendorApexWrongInstaller_staged"); 534 runPhase("testVendorApexWrongInstaller_nonStaged"); 535 } 536 537 @Test 538 @LargeTest testVendorApexCorrectInstaller()539 public void testVendorApexCorrectInstaller() throws Exception { 540 assumeTrue("Device does not support updating APEX", 541 mHostUtils.isApexUpdateSupported()); 542 543 pushTestVendorApexAllowList("com.android.tests.stagedinstallinternal"); 544 pushTestApex("test.rebootless_apex_v1.apex"); 545 getDevice().reboot(); 546 547 runPhase("testVendorApexCorrectInstaller_staged"); 548 runPhase("testVendorApexCorrectInstaller_nonStaged"); 549 } 550 551 /** 552 * Tests correctness of {@link android.content.pm.ApplicationInfo} for APEXes on /vendor. 553 */ 554 @Test 555 @LargeTest testVendorApex_Staged()556 public void testVendorApex_Staged() throws Exception { 557 pushTestApex(REBOOTLESS_V1, "vendor"); 558 getDevice().reboot(); 559 runPhase("testVendorApex_VerifyFactory"); 560 installTestApex(REBOOTLESS_V2, "--staged"); 561 getDevice().reboot(); 562 runPhase("testVendorApex_VerifyData"); 563 } 564 565 /** 566 * Tests correctness of {@link android.content.pm.ApplicationInfo} for APEXes on /vendor. 567 */ 568 @Test 569 @LargeTest testVendorApex_NonStaged()570 public void testVendorApex_NonStaged() throws Exception { 571 pushTestApex(REBOOTLESS_V1, "vendor"); 572 getDevice().reboot(); 573 runPhase("testVendorApex_VerifyFactory"); 574 installTestApex(REBOOTLESS_V2, "--force-non-staged"); 575 runPhase("testVendorApex_VerifyData"); 576 } 577 578 @Test testRebootlessUpdates()579 public void testRebootlessUpdates() throws Exception { 580 pushTestApex("test.rebootless_apex_v1.apex"); 581 getDevice().reboot(); 582 583 runPhase("testRebootlessUpdates"); 584 } 585 586 @Test testRebootlessUpdate_hasStagedSessionWithSameApex_fails()587 public void testRebootlessUpdate_hasStagedSessionWithSameApex_fails() throws Exception { 588 assumeTrue("Device does not support updating APEX", 589 mHostUtils.isApexUpdateSupported()); 590 591 runPhase("testRebootlessUpdate_hasStagedSessionWithSameApex_fails"); 592 } 593 594 @Test testGetStagedModuleNames()595 public void testGetStagedModuleNames() throws Exception { 596 assumeTrue("Device does not support updating APEX", 597 mHostUtils.isApexUpdateSupported()); 598 599 runPhase("testGetStagedModuleNames"); 600 } 601 602 @Test 603 @LargeTest testGetStagedApexInfo()604 public void testGetStagedApexInfo() throws Exception { 605 assumeTrue("Device does not support updating APEX", 606 mHostUtils.isApexUpdateSupported()); 607 608 pushTestApex(APEXD_TEST_APEX); 609 getDevice().reboot(); 610 611 runPhase("testGetStagedApexInfo"); 612 } 613 614 @Test 615 @LargeTest testGetAppInfo_flagTestOnlyIsSet()616 public void testGetAppInfo_flagTestOnlyIsSet() throws Exception { 617 assumeTrue("Device does not support updating APEX", 618 mHostUtils.isApexUpdateSupported()); 619 620 pushTestApex(FAKE_APEX_SYSTEM_SERVER_APEX); 621 getDevice().reboot(); 622 623 runPhase("testGetAppInfo_flagTestOnlyIsSet"); 624 } 625 626 @Test testStagedApexObserver()627 public void testStagedApexObserver() throws Exception { 628 assumeTrue("Device does not support updating APEX", 629 mHostUtils.isApexUpdateSupported()); 630 631 runPhase("testStagedApexObserver"); 632 } 633 634 @Test testRebootlessDowngrade()635 public void testRebootlessDowngrade() throws Exception { 636 pushTestApex("test.rebootless_apex_v2.apex"); 637 getDevice().reboot(); 638 runPhase("testRebootlessDowngrade"); 639 } 640 getStagingDirectories()641 private List<String> getStagingDirectories() throws DeviceNotAvailableException { 642 String baseDir = "/data/app-staging"; 643 try { 644 getDevice().enableAdbRoot(); 645 return getDevice().getFileEntry(baseDir).getChildren(false) 646 .stream().filter(entry -> entry.getName().matches("session_\\d+")) 647 .map(entry -> entry.getName()) 648 .collect(Collectors.toList()); 649 } finally { 650 getDevice().disableAdbRoot(); 651 } 652 } 653 restartSystemServer()654 private void restartSystemServer() throws Exception { 655 // Restart the system server 656 final ProcessInfo oldPs = getDevice().getProcessByName("system_server"); 657 658 getDevice().enableAdbRoot(); // Need root to restart system server 659 assertThat(getDevice().executeShellCommand("am restart")).contains("Restart the system"); 660 getDevice().disableAdbRoot(); 661 662 // Wait for new system server process to start 663 final long start = System.currentTimeMillis(); 664 while (System.currentTimeMillis() < start + SYSTEM_SERVER_TIMEOUT_MS) { 665 final ProcessInfo newPs = getDevice().getProcessByName("system_server"); 666 if (newPs != null) { 667 if (newPs.getPid() != oldPs.getPid()) { 668 getDevice().waitForDeviceAvailable(); 669 return; 670 } 671 } 672 Thread.sleep(500); 673 } 674 fail("Timed out in restarting system server"); 675 } 676 } 677