1 /* 2 * Copyright (C) 2021 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.cts_root.rollback.host; 18 19 import static com.android.cts.shim.lib.ShimPackage.SHIM_APEX_PACKAGE_NAME; 20 import static com.android.cts.shim.lib.ShimPackage.SHIM_PACKAGE_NAME; 21 import static com.android.cts_root.rollback.host.WatchdogEventLogger.Subject.assertThat; 22 23 import static com.google.common.truth.Truth.assertThat; 24 import static com.google.common.truth.Truth.assertWithMessage; 25 26 import static org.junit.Assert.fail; 27 import static org.junit.Assume.assumeTrue; 28 29 import android.cts.install.lib.host.InstallUtilsHost; 30 31 import com.android.ddmlib.Log; 32 import com.android.tradefed.device.DeviceNotAvailableException; 33 import com.android.tradefed.device.IFileEntry; 34 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 35 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 36 37 import org.junit.After; 38 import org.junit.Before; 39 import org.junit.Test; 40 import org.junit.runner.RunWith; 41 42 import java.time.Instant; 43 import java.util.Collections; 44 import java.util.Date; 45 import java.util.List; 46 import java.util.concurrent.TimeUnit; 47 import java.util.stream.Collectors; 48 49 /** 50 * CTS-root host tests for RollbackManager APIs. 51 */ 52 @RunWith(DeviceJUnit4ClassRunner.class) 53 public class RollbackManagerHostTest extends BaseHostJUnit4Test { 54 private static final String TAG = "RollbackManagerHostTest"; 55 56 private static final int NATIVE_CRASHES_THRESHOLD = 5; 57 58 private static final String REASON_APP_CRASH = "REASON_APP_CRASH"; 59 private static final String REASON_NATIVE_CRASH = "REASON_NATIVE_CRASH"; 60 private static final String ROLLBACK_INITIATE = "ROLLBACK_INITIATE"; 61 private static final String ROLLBACK_BOOT_TRIGGERED = "ROLLBACK_BOOT_TRIGGERED"; 62 private static final String ROLLBACK_SUCCESS = "ROLLBACK_SUCCESS"; 63 64 private static final String TESTAPP_A = "com.android.cts.install.lib.testapp.A"; 65 private static final String TEST_SUBDIR = "/subdir/"; 66 private static final String TEST_FILENAME_1 = "test_file.txt"; 67 private static final String TEST_STRING_1 = "hello this is a test"; 68 private static final String TEST_FILENAME_2 = "another_file.txt"; 69 private static final String TEST_STRING_2 = "this is a different file"; 70 private static final String TEST_FILENAME_3 = "also.xyz"; 71 private static final String TEST_STRING_3 = "also\n a\n test\n string"; 72 private static final String TEST_FILENAME_4 = "one_more.test"; 73 private static final String TEST_STRING_4 = "once more unto the test"; 74 75 // Expiry time for staged sessions that have not changed state in this time 76 private static final long MAX_TIME_SINCE_UPDATE_MILLIS = TimeUnit.DAYS.toMillis(21); 77 78 private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); 79 private WatchdogEventLogger mLogger = new WatchdogEventLogger(); 80 run(String method)81 private void run(String method) throws Exception { 82 assertThat(runDeviceTests("com.android.cts_root.rollback.host.app", 83 "com.android.cts_root.rollback.host.app.HostTestHelper", 84 method)).isTrue(); 85 } 86 87 @Before 88 @After cleanUp()89 public void cleanUp() throws Exception { 90 getDevice().enableAdbRoot(); 91 getDevice().executeShellCommand("for i in $(pm list staged-sessions --only-sessionid " 92 + "--only-parent); do pm install-abandon $i; done"); 93 getDevice().uninstallPackage("com.android.cts.install.lib.testapp.A"); 94 getDevice().uninstallPackage("com.android.cts.install.lib.testapp.B"); 95 getDevice().uninstallPackage("com.android.cts.install.lib.testapp.C"); 96 run("cleanUp"); 97 mHostUtils.uninstallShimApexIfNecessary(); 98 } 99 100 @Before setUp()101 public void setUp() throws Exception { 102 getDevice().enableAdbRoot(); 103 mLogger.start(getDevice()); 104 } 105 106 @After tearDown()107 public void tearDown() throws Exception { 108 getDevice().enableAdbRoot(); 109 mLogger.stop(); 110 } 111 112 /** 113 * Tests user data is restored according to the preset rollback data policy. 114 */ 115 @Test testRollbackDataPolicy()116 public void testRollbackDataPolicy() throws Exception { 117 List<String> before = getSnapshotDirectories("/data/misc_ce/0/rollback"); 118 119 run("testRollbackDataPolicy_Phase1_Install"); 120 getDevice().reboot(); 121 run("testRollbackDataPolicy_Phase2_Rollback"); 122 getDevice().reboot(); 123 run("testRollbackDataPolicy_Phase3_VerifyRollback"); 124 125 // Verify snapshots are deleted after restoration 126 List<String> after = getSnapshotDirectories("/data/misc_ce/0/rollback"); 127 // Only check directories newly created during the test 128 after.removeAll(before); 129 // There should be only one /data/misc_ce/0/rollback/<rollbackId> created during test 130 assertThat(after).hasSize(1); 131 assertDirectoryIsEmpty(after.get(0)); 132 } 133 134 /** 135 * Tests that userdata of apk-in-apex is restored when apex is rolled back. 136 */ 137 @Test testRollbackApkInApexDataDirectories_Ce()138 public void testRollbackApkInApexDataDirectories_Ce() throws Exception { 139 // Push files to apk data directory 140 String oldFilePath1 = apkDataDirCe( 141 SHIM_PACKAGE_NAME, 0) + "/" + TEST_FILENAME_1; 142 String oldFilePath2 = apkDataDirCe( 143 SHIM_PACKAGE_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2; 144 pushString(TEST_STRING_1, oldFilePath1); 145 pushString(TEST_STRING_2, oldFilePath2); 146 147 // Install new version of the APEX with rollback enabled 148 run("testRollbackApexDataDirectories_Phase1_Install"); 149 getDevice().reboot(); 150 151 // Replace files in data directory 152 String newFilePath3 = apkDataDirCe( 153 SHIM_PACKAGE_NAME, 0) + "/" + TEST_FILENAME_3; 154 String newFilePath4 = apkDataDirCe( 155 SHIM_PACKAGE_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4; 156 getDevice().deleteFile(oldFilePath1); 157 getDevice().deleteFile(oldFilePath2); 158 pushString(TEST_STRING_3, newFilePath3); 159 pushString(TEST_STRING_4, newFilePath4); 160 161 // Roll back the APEX 162 getDevice().executeShellCommand("pm rollback-app " + SHIM_APEX_PACKAGE_NAME); 163 getDevice().reboot(); 164 165 // Verify that old files have been restored and new files are gone 166 assertFileContents(TEST_STRING_1, oldFilePath1); 167 assertFileContents(TEST_STRING_2, oldFilePath2); 168 assertFileNotExists(newFilePath3); 169 assertFileNotExists(newFilePath4); 170 } 171 172 /** 173 * Tests that data in DE apk data directory is restored when apk is rolled back. 174 */ 175 @Test testRollbackApkDataDirectories_De()176 public void testRollbackApkDataDirectories_De() throws Exception { 177 // Install version 1 of TESTAPP_A 178 run("testRollbackApkDataDirectories_Phase1_InstallV1"); 179 180 // Push files to apk data directory 181 String oldFilePath1 = apkDataDirDe(TESTAPP_A, 0) + "/" + TEST_FILENAME_1; 182 String oldFilePath2 = apkDataDirDe(TESTAPP_A, 0) + TEST_SUBDIR + TEST_FILENAME_2; 183 pushString(TEST_STRING_1, oldFilePath1); 184 pushString(TEST_STRING_2, oldFilePath2); 185 186 // Install version 2 of TESTAPP_A with rollback enabled 187 run("testRollbackApkDataDirectories_Phase2_InstallV2"); 188 getDevice().reboot(); 189 190 // Replace files in data directory 191 String newFilePath3 = apkDataDirDe(TESTAPP_A, 0) + "/" + TEST_FILENAME_3; 192 String newFilePath4 = apkDataDirDe(TESTAPP_A, 0) + TEST_SUBDIR + TEST_FILENAME_4; 193 getDevice().deleteFile(oldFilePath1); 194 getDevice().deleteFile(oldFilePath2); 195 pushString(TEST_STRING_3, newFilePath3); 196 pushString(TEST_STRING_4, newFilePath4); 197 198 // Roll back the APK 199 run("testRollbackApkDataDirectories_Phase3_Rollback"); 200 getDevice().reboot(); 201 202 // Verify that old files have been restored and new files are gone 203 assertFileContents(TEST_STRING_1, oldFilePath1); 204 assertFileContents(TEST_STRING_2, oldFilePath2); 205 assertFileNotExists(newFilePath3); 206 assertFileNotExists(newFilePath4); 207 } 208 209 /** 210 * Tests that data in CE apex data directory is restored when apex is rolled back. 211 */ 212 @Test testRollbackApexDataDirectories_Ce()213 public void testRollbackApexDataDirectories_Ce() throws Exception { 214 List<String> before = getSnapshotDirectories("/data/misc_ce/0/apexrollback"); 215 216 // Push files to apex data directory 217 String oldFilePath1 = apexDataDirCe( 218 SHIM_APEX_PACKAGE_NAME, 0) + "/" + TEST_FILENAME_1; 219 String oldFilePath2 = apexDataDirCe( 220 SHIM_APEX_PACKAGE_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2; 221 pushString(TEST_STRING_1, oldFilePath1); 222 pushString(TEST_STRING_2, oldFilePath2); 223 224 // Install new version of the APEX with rollback enabled 225 run("testRollbackApexDataDirectories_Phase1_Install"); 226 getDevice().reboot(); 227 228 // Replace files in data directory 229 String newFilePath3 = apexDataDirCe( 230 SHIM_APEX_PACKAGE_NAME, 0) + "/" + TEST_FILENAME_3; 231 String newFilePath4 = apexDataDirCe( 232 SHIM_APEX_PACKAGE_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4; 233 getDevice().deleteFile(oldFilePath1); 234 getDevice().deleteFile(oldFilePath2); 235 pushString(TEST_STRING_3, newFilePath3); 236 pushString(TEST_STRING_4, newFilePath4); 237 238 // Roll back the APEX 239 getDevice().executeShellCommand("pm rollback-app " + SHIM_APEX_PACKAGE_NAME); 240 getDevice().reboot(); 241 242 // Verify that old files have been restored and new files are gone 243 assertFileContents(TEST_STRING_1, oldFilePath1); 244 assertFileContents(TEST_STRING_2, oldFilePath2); 245 assertFileNotExists(newFilePath3); 246 assertFileNotExists(newFilePath4); 247 248 // Verify snapshots are deleted after restoration 249 List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback"); 250 // Only check directories newly created during the test 251 after.removeAll(before); 252 // There should be only one /data/misc_ce/0/apexrollback/<rollbackId> created during test 253 assertThat(after).hasSize(1); 254 assertDirectoryIsEmpty(after.get(0)); 255 } 256 257 /** 258 * Tests that data in DE (user) apex data directory is restored when apex is rolled back. 259 */ 260 @Test testRollbackApexDataDirectories_DeUser()261 public void testRollbackApexDataDirectories_DeUser() throws Exception { 262 List<String> before = getSnapshotDirectories("/data/misc_de/0/apexrollback"); 263 264 // Push files to apex data directory 265 String oldFilePath1 = apexDataDirDeUser( 266 SHIM_APEX_PACKAGE_NAME, 0) + "/" + TEST_FILENAME_1; 267 String oldFilePath2 = apexDataDirDeUser( 268 SHIM_APEX_PACKAGE_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2; 269 pushString(TEST_STRING_1, oldFilePath1); 270 pushString(TEST_STRING_2, oldFilePath2); 271 272 // Install new version of the APEX with rollback enabled 273 run("testRollbackApexDataDirectories_Phase1_Install"); 274 getDevice().reboot(); 275 276 // Replace files in data directory 277 String newFilePath3 = apexDataDirDeUser( 278 SHIM_APEX_PACKAGE_NAME, 0) + "/" + TEST_FILENAME_3; 279 String newFilePath4 = apexDataDirDeUser( 280 SHIM_APEX_PACKAGE_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4; 281 getDevice().deleteFile(oldFilePath1); 282 getDevice().deleteFile(oldFilePath2); 283 pushString(TEST_STRING_3, newFilePath3); 284 pushString(TEST_STRING_4, newFilePath4); 285 286 // Roll back the APEX 287 getDevice().executeShellCommand("pm rollback-app " + SHIM_APEX_PACKAGE_NAME); 288 getDevice().reboot(); 289 290 // Verify that old files have been restored and new files are gone 291 assertFileContents(TEST_STRING_1, oldFilePath1); 292 assertFileContents(TEST_STRING_2, oldFilePath2); 293 assertFileNotExists(newFilePath3); 294 assertFileNotExists(newFilePath4); 295 296 // Verify snapshots are deleted after restoration 297 List<String> after = getSnapshotDirectories("/data/misc_de/0/apexrollback"); 298 // Only check directories newly created during the test 299 after.removeAll(before); 300 // There should be only one /data/misc_de/0/apexrollback/<rollbackId> created during test 301 assertThat(after).hasSize(1); 302 assertDirectoryIsEmpty(after.get(0)); 303 } 304 305 /** 306 * Tests that data in DE_sys apex data directory is restored when apex is rolled back. 307 */ 308 @Test testRollbackApexDataDirectories_DeSys()309 public void testRollbackApexDataDirectories_DeSys() throws Exception { 310 List<String> before = getSnapshotDirectories("/data/misc/apexrollback"); 311 312 // Push files to apex data directory 313 String oldFilePath1 = apexDataDirDeSys( 314 SHIM_APEX_PACKAGE_NAME) + "/" + TEST_FILENAME_1; 315 String oldFilePath2 = apexDataDirDeSys( 316 SHIM_APEX_PACKAGE_NAME) + TEST_SUBDIR + TEST_FILENAME_2; 317 pushString(TEST_STRING_1, oldFilePath1); 318 pushString(TEST_STRING_2, oldFilePath2); 319 320 // Install new version of the APEX with rollback enabled 321 run("testRollbackApexDataDirectories_Phase1_Install"); 322 getDevice().reboot(); 323 324 // Replace files in data directory 325 String newFilePath3 = apexDataDirDeSys( 326 SHIM_APEX_PACKAGE_NAME) + "/" + TEST_FILENAME_3; 327 String newFilePath4 = apexDataDirDeSys( 328 SHIM_APEX_PACKAGE_NAME) + TEST_SUBDIR + TEST_FILENAME_4; 329 getDevice().deleteFile(oldFilePath1); 330 getDevice().deleteFile(oldFilePath2); 331 pushString(TEST_STRING_3, newFilePath3); 332 pushString(TEST_STRING_4, newFilePath4); 333 334 // Roll back the APEX 335 getDevice().executeShellCommand("pm rollback-app " + SHIM_APEX_PACKAGE_NAME); 336 getDevice().reboot(); 337 338 // Verify that old files have been restored and new files are gone 339 assertFileContents(TEST_STRING_1, oldFilePath1); 340 assertFileContents(TEST_STRING_2, oldFilePath2); 341 assertFileNotExists(newFilePath3); 342 assertFileNotExists(newFilePath4); 343 344 // Verify snapshots are deleted after restoration 345 List<String> after = getSnapshotDirectories("/data/misc/apexrollback"); 346 // Only check directories newly created during the test 347 after.removeAll(before); 348 // There should be only one /data/misc/apexrollback/<rollbackId> created during test 349 assertThat(after).hasSize(1); 350 assertDirectoryIsEmpty(after.get(0)); 351 } 352 353 /** 354 * Tests that apex CE snapshots are deleted when its rollback is deleted. 355 */ 356 @Test testExpireApexRollback()357 public void testExpireApexRollback() throws Exception { 358 List<String> before = getSnapshotDirectories("/data/misc_ce/0/apexrollback"); 359 360 // Push files to apex data directory 361 String oldFilePath1 = apexDataDirCe( 362 SHIM_APEX_PACKAGE_NAME, 0) + "/" + TEST_FILENAME_1; 363 String oldFilePath2 = apexDataDirCe( 364 SHIM_APEX_PACKAGE_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2; 365 pushString(TEST_STRING_1, oldFilePath1); 366 pushString(TEST_STRING_2, oldFilePath2); 367 368 // Install new version of the APEX with rollback enabled 369 run("testRollbackApexDataDirectories_Phase1_Install"); 370 getDevice().reboot(); 371 372 List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback"); 373 // Only check directories newly created during the test 374 after.removeAll(before); 375 // There should be only one /data/misc_ce/0/apexrollback/<rollbackId> created during test 376 assertThat(after).hasSize(1); 377 // Expire all rollbacks and check CE snapshot directories are deleted 378 run("cleanUp"); 379 assertFileNotExists(after.get(0)); 380 } 381 382 /** 383 * Tests an available rollback shouldn't be deleted when its session expires. 384 */ 385 @Test testExpireSession()386 public void testExpireSession() throws Exception { 387 run("testExpireSession_Phase1_Install"); 388 getDevice().reboot(); 389 run("testExpireSession_Phase2_VerifyInstall"); 390 391 // Advance system clock by MAX_TIME_SINCE_UPDATE_MILLIS to expire the staged session 392 Instant t1 = Instant.ofEpochMilli(getDevice().getDeviceDate()); 393 Instant t2 = t1.plusMillis(MAX_TIME_SINCE_UPDATE_MILLIS); 394 395 try { 396 getDevice().setDate(Date.from(t2)); 397 // Send the broadcast to ensure the time change is properly propagated 398 getDevice().executeShellCommand("am broadcast -a android.intent.action.TIME_SET"); 399 // TODO(b/197298469): Time change will be lost after reboot and sessions won't be 400 // expired correctly. We restart system server so sessions will be reloaded and expired 401 // as a workaround. 402 getDevice().executeShellCommand("stop"); 403 getDevice().executeShellCommand("start"); 404 getDevice().waitForDeviceAvailable(); 405 run("testExpireSession_Phase3_VerifyRollback"); 406 } finally { 407 // Restore system clock 408 getDevice().setDate(Date.from(t1)); 409 getDevice().executeShellCommand("am broadcast -a android.intent.action.TIME_SET"); 410 } 411 } 412 413 /** 414 * Tests watchdog triggered staged rollbacks involving only apks. 415 */ 416 @Test testBadApkOnly()417 public void testBadApkOnly() throws Exception { 418 run("testBadApkOnly_Phase1_Install"); 419 getDevice().reboot(); 420 run("testBadApkOnly_Phase2_VerifyInstall"); 421 422 // Launch the app to crash to trigger rollback 423 startActivity(TESTAPP_A); 424 // Wait for reboot to happen 425 waitForDeviceNotAvailable(2, TimeUnit.MINUTES); 426 427 getDevice().waitForDeviceAvailable(); 428 429 run("testBadApkOnly_Phase3_VerifyRollback"); 430 431 assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_APP_CRASH, TESTAPP_A); 432 assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null); 433 assertThat(mLogger).eventOccurred(ROLLBACK_SUCCESS, null, null, null); 434 } 435 436 /** 437 * Tests rollbacks triggered by the native watchdog. 438 */ 439 @Test testNativeWatchdogTriggersRollback()440 public void testNativeWatchdogTriggersRollback() throws Exception { 441 run("testNativeWatchdogTriggersRollback_Phase1_Install"); 442 getDevice().reboot(); 443 run("testNativeWatchdogTriggersRollback_Phase2_VerifyInstall"); 444 445 // crash system_server enough times to trigger a rollback 446 crashProcess("system_server", NATIVE_CRASHES_THRESHOLD); 447 448 // Rollback should be committed automatically now. 449 // Give time for rollback to be committed. This could take a while, 450 // because we need all of the following to happen: 451 // 1. system_server comes back up and boot completes. 452 // 2. Rollback health observer detects updatable crashing signal. 453 // 3. Staged rollback session becomes ready. 454 // 4. Device actually reboots. 455 // So we give a generous timeout here. 456 waitForDeviceNotAvailable(5, TimeUnit.MINUTES); 457 getDevice().waitForDeviceAvailable(); 458 459 // verify rollback committed 460 run("testNativeWatchdogTriggersRollback_Phase3_VerifyRollback"); 461 462 assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_NATIVE_CRASH, null); 463 assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null); 464 assertThat(mLogger).eventOccurred(ROLLBACK_SUCCESS, null, null, null); 465 } 466 467 /** 468 * Tests rollbacks triggered by the native watchdog. 469 */ 470 @Test testNativeWatchdogTriggersRollbackForAll()471 public void testNativeWatchdogTriggersRollbackForAll() throws Exception { 472 // This test requires committing multiple staged rollbacks 473 assumeTrue(isCheckpointSupported()); 474 475 // Install 2 packages with rollback enabled. 476 run("testNativeWatchdogTriggersRollbackForAll_Phase1_Install"); 477 getDevice().reboot(); 478 479 // Verify that we have 2 rollbacks available 480 run("testNativeWatchdogTriggersRollbackForAll_Phase2_VerifyInstall"); 481 482 // crash system_server enough times to trigger rollbacks 483 crashProcess("system_server", NATIVE_CRASHES_THRESHOLD); 484 485 // Rollback should be committed automatically now. 486 // Give time for rollback to be committed. This could take a while, 487 // because we need all of the following to happen: 488 // 1. system_server comes back up and boot completes. 489 // 2. Rollback health observer detects updatable crashing signal. 490 // 3. Staged rollback session becomes ready. 491 // 4. Device actually reboots. 492 // So we give a generous timeout here. 493 waitForDeviceNotAvailable(5, TimeUnit.MINUTES); 494 getDevice().waitForDeviceAvailable(); 495 496 // verify all available rollbacks have been committed 497 run("testNativeWatchdogTriggersRollbackForAll_Phase3_VerifyRollback"); 498 499 assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_NATIVE_CRASH, null); 500 assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null); 501 assertThat(mLogger).eventOccurred(ROLLBACK_SUCCESS, null, null, null); 502 } 503 getSnapshotDirectories(String baseDir)504 private List<String> getSnapshotDirectories(String baseDir) throws Exception { 505 IFileEntry f = getDevice().getFileEntry(baseDir); 506 if (f == null) { 507 Log.d(TAG, "baseDir doesn't exist: " + baseDir); 508 return Collections.EMPTY_LIST; 509 } 510 List<String> list = f.getChildren(false) 511 .stream().filter(entry -> entry.getName().matches("\\d+(-prerestore)?")) 512 .map(entry -> entry.getFullPath()) 513 .collect(Collectors.toList()); 514 Log.d(TAG, "getSnapshotDirectories=" + list); 515 return list; 516 } 517 assertDirectoryIsEmpty(String path)518 private void assertDirectoryIsEmpty(String path) { 519 try { 520 IFileEntry file = getDevice().getFileEntry(path); 521 assertWithMessage("Not a directory: " + path).that(file.isDirectory()).isTrue(); 522 assertWithMessage("Directory not empty: " + path) 523 .that(file.getChildren(false)).isEmpty(); 524 } catch (DeviceNotAvailableException e) { 525 fail("Can't access directory: " + path); 526 } 527 } 528 assertFileContents(String expectedContents, String path)529 private void assertFileContents(String expectedContents, String path) throws Exception { 530 String actualContents = getDevice().pullFileContents(path); 531 assertWithMessage("Failed to retrieve file=%s", path).that(actualContents).isNotNull(); 532 assertWithMessage("Mismatched file contents, path=%s", path) 533 .that(actualContents).isEqualTo(expectedContents); 534 } 535 assertFileNotExists(String path)536 private void assertFileNotExists(String path) throws Exception { 537 assertWithMessage("File shouldn't exist, path=%s", path) 538 .that(getDevice().getFileEntry(path)).isNull(); 539 } 540 apkDataDirCe(String apkName, int userId)541 private static String apkDataDirCe(String apkName, int userId) { 542 return String.format("/data/user/%d/%s", userId, apkName); 543 } 544 apkDataDirDe(String apkName, int userId)545 private static String apkDataDirDe(String apkName, int userId) { 546 return String.format("/data/user_de/%d/%s", userId, apkName); 547 } 548 apexDataDirCe(String apexName, int userId)549 private static String apexDataDirCe(String apexName, int userId) { 550 return String.format("/data/misc_ce/%d/apexdata/%s", userId, apexName); 551 } 552 apexDataDirDeUser(String apexName, int userId)553 private static String apexDataDirDeUser(String apexName, int userId) { 554 return String.format("/data/misc_de/%d/apexdata/%s", userId, apexName); 555 } 556 apexDataDirDeSys(String apexName)557 private static String apexDataDirDeSys(String apexName) { 558 return String.format("/data/misc/apexdata/%s", apexName); 559 } 560 pushString(String contents, String path)561 private void pushString(String contents, String path) throws Exception { 562 assertWithMessage("Failed to push file to device, content=%s path=%s", contents, path) 563 .that(getDevice().pushString(contents, path)).isTrue(); 564 } 565 waitForDeviceNotAvailable(long timeout, TimeUnit unit)566 private void waitForDeviceNotAvailable(long timeout, TimeUnit unit) { 567 assertWithMessage("waitForDeviceNotAvailable() timed out in %s %s", timeout, unit) 568 .that(getDevice().waitForDeviceNotAvailable(unit.toMillis(timeout))).isTrue(); 569 } 570 startActivity(String packageName)571 private void startActivity(String packageName) throws Exception { 572 String cmd = "am start -S -a android.intent.action.MAIN " 573 + "-c android.intent.category.LAUNCHER " + packageName; 574 getDevice().executeShellCommand(cmd); 575 } 576 crashProcess(String processName, int numberOfCrashes)577 private void crashProcess(String processName, int numberOfCrashes) throws Exception { 578 String pid = ""; 579 String lastPid = "invalid"; 580 for (int i = 0; i < numberOfCrashes; ++i) { 581 // This condition makes sure before we kill the process, the process is running AND 582 // the last crash was finished. 583 while ("".equals(pid) || lastPid.equals(pid)) { 584 pid = getDevice().executeShellCommand("pidof " + processName); 585 } 586 getDevice().executeShellCommand("kill " + pid); 587 lastPid = pid; 588 } 589 } 590 isCheckpointSupported()591 private boolean isCheckpointSupported() throws Exception { 592 try { 593 run("isCheckpointSupported"); 594 return true; 595 } catch (AssertionError ignore) { 596 return false; 597 } 598 } 599 } 600