1 /* 2 * Copyright (C) 2022 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 android.avf.test; 18 19 import static com.android.tradefed.device.TestDevice.MicrodroidBuilder; 20 import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics; 21 22 import static com.google.common.truth.Truth.assertThat; 23 import static com.google.common.truth.Truth.assertWithMessage; 24 import static com.google.common.truth.TruthJUnit.assume; 25 26 import static org.junit.Assert.assertNotNull; 27 import static org.junit.Assume.assumeFalse; 28 import static org.junit.Assume.assumeTrue; 29 30 import android.platform.test.annotations.RootPermissionTest; 31 32 import com.android.microdroid.test.common.MetricsProcessor; 33 import com.android.microdroid.test.host.CommandRunner; 34 import com.android.microdroid.test.host.KvmHypTracer; 35 import com.android.microdroid.test.host.MicrodroidHostTestCaseBase; 36 import com.android.tradefed.device.DeviceNotAvailableException; 37 import com.android.tradefed.device.ITestDevice; 38 import com.android.tradefed.device.TestDevice; 39 import com.android.tradefed.log.LogUtil.CLog; 40 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 41 import com.android.tradefed.util.CommandResult; 42 import com.android.tradefed.util.SimpleStats; 43 44 import org.junit.After; 45 import org.junit.Before; 46 import org.junit.Rule; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 50 import java.util.ArrayList; 51 import java.util.Collections; 52 import java.util.List; 53 import java.util.Map; 54 import java.util.regex.Matcher; 55 import java.util.regex.Pattern; 56 57 @RootPermissionTest 58 @RunWith(DeviceJUnit4ClassRunner.class) 59 public final class AVFHostTestCase extends MicrodroidHostTestCaseBase { 60 61 private static final String COMPOSD_CMD_BIN = "/apex/com.android.compos/bin/composd_cmd"; 62 63 // Files that define the "test" instance of CompOS 64 private static final String COMPOS_TEST_ROOT = "/data/misc/apexdata/com.android.compos/test/"; 65 66 private static final String BOOTLOADER_TIME_PROP_NAME = "ro.boot.boottime"; 67 private static final String BOOTLOADER_PREFIX = "bootloader-"; 68 private static final String BOOTLOADER_TIME = "bootloader_time"; 69 private static final String BOOTLOADER_PHASE_SW = "SW"; 70 71 /** Boot time test related variables */ 72 private static final int REINSTALL_APEX_RETRY_INTERVAL_MS = 5 * 1000; 73 74 private static final int REINSTALL_APEX_TIMEOUT_SEC = 15; 75 private static final int COMPILE_STAGED_APEX_RETRY_INTERVAL_MS = 10 * 1000; 76 private static final int COMPILE_STAGED_APEX_TIMEOUT_SEC = 540; 77 private static final int BOOT_COMPLETE_TIMEOUT_MS = 10 * 60 * 1000; 78 private static final int ROUND_COUNT = 5; 79 private static final int ROUND_IGNORE_STARTUP_TIME = 3; 80 private static final String APK_NAME = "MicrodroidTestApp.apk"; 81 private static final String PACKAGE_NAME = "com.android.microdroid.test"; 82 83 private MetricsProcessor mMetricsProcessor; 84 @Rule public TestMetrics mMetrics = new TestMetrics(); 85 86 private boolean mNeedTearDown = false; 87 88 @Before setUp()89 public void setUp() throws Exception { 90 mNeedTearDown = false; 91 92 assumeDeviceIsCapable(getDevice()); 93 mNeedTearDown = true; 94 95 getDevice().installPackage(findTestFile(APK_NAME), /* reinstall */ false); 96 97 mMetricsProcessor = new MetricsProcessor(getMetricPrefix() + "hostside/"); 98 } 99 100 @After tearDown()101 public void tearDown() throws Exception { 102 if (!mNeedTearDown) { 103 // If we skipped setUp, we don't need to undo it, and that avoids potential exceptions 104 // incompatible hardware. (Note that tests can change what assumeDeviceIsCapable() 105 // sees, so we can't rely on that - b/268688303.) 106 return; 107 } 108 109 CommandRunner android = new CommandRunner(getDevice()); 110 111 // Clear up any CompOS instance files we created. 112 android.tryRun("rm", "-rf", COMPOS_TEST_ROOT); 113 } 114 115 @Test testBootWithCompOS()116 public void testBootWithCompOS() throws Exception { 117 composTestHelper(true, "microdroid"); 118 } 119 120 @Test testBootWithCompOS_os_android15_66()121 public void testBootWithCompOS_os_android15_66() throws Exception { 122 composTestHelper(true, "android15_66"); 123 } 124 125 @Test testBootWithCompOS_os_microdroid_16k()126 public void testBootWithCompOS_os_microdroid_16k() throws Exception { 127 composTestHelper(true, "microdroid_16k"); 128 } 129 130 @Test testBootWithoutCompOS()131 public void testBootWithoutCompOS() throws Exception { 132 composTestHelper(false, null); 133 } 134 135 @Test testNoLongHypSections()136 public void testNoLongHypSections() throws Exception { 137 noLongHypSectionsHelper("microdroid"); 138 } 139 140 @Test testNoLongHypSections_os_android15_66()141 public void testNoLongHypSections_os_android15_66() throws Exception { 142 noLongHypSectionsHelper("android15_66"); 143 } 144 145 @Test testNoLongHypSections_os_microdroid_16k()146 public void testNoLongHypSections_os_microdroid_16k() throws Exception { 147 noLongHypSectionsHelper("microdroid_16k"); 148 } 149 150 @Test testPsciMemProtect()151 public void testPsciMemProtect() throws Exception { 152 psciMemProtectHelper("microdroid"); 153 } 154 155 @Test testPsciMemProtect_os_android15_66()156 public void testPsciMemProtect_os_android15_66() throws Exception { 157 psciMemProtectHelper("android15_66"); 158 } 159 160 @Test testPsciMemProtect_os_microdroid_16k()161 public void testPsciMemProtect_os_microdroid_16k() throws Exception { 162 psciMemProtectHelper("microdroid_16k"); 163 } 164 165 @Test testCameraAppStartupTime()166 public void testCameraAppStartupTime() throws Exception { 167 String[] launchIntentPackages = { 168 "com.android.camera2", 169 "com.google.android.GoogleCamera/com.android.camera.CameraLauncher" 170 }; 171 String launchIntentPackage = findSupportedPackage(launchIntentPackages); 172 assume().withMessage("No supported camera package").that(launchIntentPackage).isNotNull(); 173 appStartupHelper(launchIntentPackage); 174 } 175 176 @Test testSettingsAppStartupTime()177 public void testSettingsAppStartupTime() throws Exception { 178 String[] launchIntentPackages = {"com.android.settings"}; 179 String launchIntentPackage = findSupportedPackage(launchIntentPackages); 180 assume().withMessage("No supported settings package").that(launchIntentPackage).isNotNull(); 181 appStartupHelper(launchIntentPackage); 182 } 183 noLongHypSectionsHelper(String osKey)184 private void noLongHypSectionsHelper(String osKey) throws Exception { 185 assumeKernelSupported(osKey); 186 assumeVmTypeSupported(osKey, true); 187 String os = SUPPORTED_OSES.get(osKey); 188 189 String[] hypEvents = {"hyp_enter", "hyp_exit"}; 190 191 assumeTrue( 192 "Skip without hypervisor tracing", 193 KvmHypTracer.isSupported(getDevice(), hypEvents)); 194 195 KvmHypTracer tracer = new KvmHypTracer(getDevice(), hypEvents); 196 String result = tracer.run(COMPOSD_CMD_BIN + " test-compile --os " + os); 197 assertWithMessage("Failed to test compilation VM.") 198 .that(result) 199 .ignoringCase() 200 .contains("all ok"); 201 202 SimpleStats stats = tracer.getDurationStats(); 203 reportMetric(stats.getData(), "hyp_sections", "s"); 204 CLog.i("Hypervisor traces parsed successfully."); 205 } 206 psciMemProtectHelper(String osKey)207 public void psciMemProtectHelper(String osKey) throws Exception { 208 assumeKernelSupported(osKey); 209 assumeVmTypeSupported(osKey, true); 210 String os = SUPPORTED_OSES.get(osKey); 211 212 String[] hypEvents = {"psci_mem_protect"}; 213 214 assumeTrue( 215 "Skip without hypervisor tracing", 216 KvmHypTracer.isSupported(getDevice(), hypEvents)); 217 KvmHypTracer tracer = new KvmHypTracer(getDevice(), hypEvents); 218 219 /* We need to wait for crosvm to die so all the VM pages are reclaimed */ 220 String result = 221 tracer.run( 222 COMPOSD_CMD_BIN 223 + " test-compile --os " 224 + os 225 + " && killall -w crosvm || true"); 226 assertWithMessage("Failed to test compilation VM.") 227 .that(result) 228 .ignoringCase() 229 .contains("all ok"); 230 231 List<Integer> values = tracer.getPsciMemProtect(); 232 233 assertWithMessage("PSCI MEM_PROTECT events not recorded") 234 .that(values.size()) 235 .isGreaterThan(2); 236 237 assertWithMessage("PSCI MEM_PROTECT counter not starting from 0") 238 .that(values.get(0)) 239 .isEqualTo(0); 240 241 assertWithMessage("PSCI MEM_PROTECT counter not ending with 0") 242 .that(values.get(values.size() - 1)) 243 .isEqualTo(0); 244 245 assertWithMessage("PSCI MEM_PROTECT counter didn't increment") 246 .that(Collections.max(values)) 247 .isGreaterThan(0); 248 } 249 appStartupHelper(String launchIntentPackage)250 private void appStartupHelper(String launchIntentPackage) throws Exception { 251 assumeTrue( 252 "Skip on non-protected VMs", 253 ((TestDevice) getDevice()).supportsMicrodroid(/* protectedVm= */ true)); 254 255 StartupTimeMetricCollection mCollection = 256 new StartupTimeMetricCollection(getPackageName(launchIntentPackage), ROUND_COUNT); 257 getAppStartupTime(launchIntentPackage, mCollection); 258 259 reportMetric( 260 mCollection.mAppBeforeVmRunTotalTime, 261 "app_startup/" + mCollection.getPkgName() + "/total_time/before_vm", 262 "ms"); 263 reportMetric( 264 mCollection.mAppBeforeVmRunWaitTime, 265 "app_startup/" + mCollection.getPkgName() + "/wait_time/before_vm", 266 "ms"); 267 reportMetric( 268 mCollection.mAppDuringVmRunTotalTime, 269 "app_startup/" + mCollection.getPkgName() + "/total_time/during_vm", 270 "ms"); 271 reportMetric( 272 mCollection.mAppDuringVmRunWaitTime, 273 "app_startup/" + mCollection.getPkgName() + "/wait_time/during_vm", 274 "ms"); 275 reportMetric( 276 mCollection.mAppAfterVmRunTotalTime, 277 "app_startup/" + mCollection.getPkgName() + "/total_time/after_vm", 278 "ms"); 279 reportMetric( 280 mCollection.mAppAfterVmRunWaitTime, 281 "app_startup/" + mCollection.getPkgName() + "/wait_time/after_vm", 282 "ms"); 283 } 284 getPackageName(String launchIntentPackage)285 private String getPackageName(String launchIntentPackage) { 286 String appPkg = launchIntentPackage; 287 288 // Does the appPkgName contain the intent ? 289 if (launchIntentPackage != null && launchIntentPackage.contains("/")) { 290 appPkg = launchIntentPackage.split("/")[0]; 291 } 292 return appPkg; 293 } 294 findSupportedPackage(String[] pkgNameList)295 private String findSupportedPackage(String[] pkgNameList) throws Exception { 296 CommandRunner android = new CommandRunner(getDevice()); 297 298 for (String pkgName : pkgNameList) { 299 String appPkg = getPackageName(pkgName); 300 String hasPackage = 301 android.run( 302 "pm list package | grep -w " + appPkg + " 1> /dev/null" + "; echo $?"); 303 assertNotNull(hasPackage); 304 305 if (hasPackage.equals("0")) { 306 return pkgName; 307 } 308 } 309 return null; 310 } 311 getColdRunStartupTimes(CommandRunner android, String pkgName)312 private AmStartupTimeCmdParser getColdRunStartupTimes(CommandRunner android, String pkgName) 313 throws DeviceNotAvailableException, InterruptedException { 314 unlockScreen(android); 315 // Ensure we are killing the app to get the cold app startup time 316 android.run("am force-stop " + pkgName); 317 android.run("echo 3 > /proc/sys/vm/drop_caches"); 318 String vmStartAppLog = android.run("am", "start -W -S " + pkgName); 319 assertNotNull(vmStartAppLog); 320 assumeFalse(vmStartAppLog.isEmpty()); 321 return new AmStartupTimeCmdParser(vmStartAppLog); 322 } 323 324 // Returns an array of two elements containing the delta between the initial app startup time 325 // and the time measured after running the VM. getAppStartupTime(String pkgName, StartupTimeMetricCollection metricColector)326 private void getAppStartupTime(String pkgName, StartupTimeMetricCollection metricColector) 327 throws Exception { 328 TestDevice device = (TestDevice) getDevice(); 329 330 // 1. Reboot the device to run the test without stage2 fragmentation 331 getDevice().rebootUntilOnline(); 332 waitForBootCompleted(); 333 334 // 2. Start the app and ignore first runs to warm up caches 335 CommandRunner android = new CommandRunner(getDevice()); 336 for (int i = 0; i < ROUND_IGNORE_STARTUP_TIME; i++) { 337 getColdRunStartupTimes(android, pkgName); 338 } 339 340 // 3. Run the app before the VM run and collect app startup time statistics 341 for (int i = 0; i < ROUND_COUNT; i++) { 342 AmStartupTimeCmdParser beforeVmStartApp = getColdRunStartupTimes(android, pkgName); 343 metricColector.addStartupTimeMetricBeforeVmRun(beforeVmStartApp); 344 } 345 346 // Clear up any test dir 347 android.tryRun("rm", "-rf", MicrodroidHostTestCaseBase.TEST_ROOT); 348 349 // Donate 80% of the available device memory to the VM 350 final String configPath = "assets/vm_config.json"; 351 final int vm_mem_mb = getFreeMemoryInfoMb(android) * 80 / 100; 352 ITestDevice microdroidDevice = 353 MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath) 354 .debugLevel("full") 355 .memoryMib(vm_mem_mb) 356 .cpuTopology("match_host") 357 .build(device); 358 try { 359 microdroidDevice.waitForBootComplete(30000); 360 microdroidDevice.enableAdbRoot(); 361 362 CommandRunner microdroid = new CommandRunner(microdroidDevice); 363 364 microdroid.run("mkdir -p /mnt/ramdisk && chmod 777 /mnt/ramdisk"); 365 microdroid.run("mount -t tmpfs -o size=32G tmpfs /mnt/ramdisk"); 366 367 // Allocate memory for the VM until it fails and make sure that we touch 368 // the allocated memory in the guest to be able to create stage2 fragmentation. 369 try { 370 microdroid.tryRun( 371 String.format( 372 "cd /mnt/ramdisk && truncate -s %dM sprayMemory" 373 + " && dd if=/dev/zero of=sprayMemory bs=1MB count=%d", 374 vm_mem_mb, vm_mem_mb)); 375 } catch (Exception expected) { 376 } 377 378 // Run the app during the VM run and collect cold startup time. 379 for (int i = 0; i < ROUND_COUNT; i++) { 380 AmStartupTimeCmdParser duringVmStartApp = getColdRunStartupTimes(android, pkgName); 381 metricColector.addStartupTimeMetricDuringVmRun(duringVmStartApp); 382 } 383 } finally { 384 device.shutdownMicrodroid(microdroidDevice); 385 } 386 387 // Run the app after the VM run and collect cold startup time. 388 for (int i = 0; i < ROUND_COUNT; i++) { 389 AmStartupTimeCmdParser afterVmStartApp = getColdRunStartupTimes(android, pkgName); 390 metricColector.addStartupTimerMetricAfterVmRun(afterVmStartApp); 391 } 392 } 393 394 static class AmStartupTimeCmdParser { 395 private int mTotalTime; 396 private int mWaitTime; 397 AmStartupTimeCmdParser(String startAppLog)398 AmStartupTimeCmdParser(String startAppLog) { 399 String[] lines = startAppLog.split("[\r\n]+"); 400 mTotalTime = mWaitTime = 0; 401 402 for (String line : lines) { 403 if (line.contains("TotalTime:")) { 404 mTotalTime = Integer.parseInt(line.replaceAll("\\D+", "")); 405 } 406 if (line.contains("WaitTime:")) { 407 mWaitTime = Integer.parseInt(line.replaceAll("\\D+", "")); 408 } 409 } 410 } 411 } 412 413 static class StartupTimeMetricCollection { 414 List<Double> mAppBeforeVmRunTotalTime; 415 List<Double> mAppBeforeVmRunWaitTime; 416 417 List<Double> mAppDuringVmRunTotalTime; 418 List<Double> mAppDuringVmRunWaitTime; 419 420 List<Double> mAppAfterVmRunTotalTime; 421 List<Double> mAppAfterVmRunWaitTime; 422 423 private final String mPkgName; 424 StartupTimeMetricCollection(String pkgName, int size)425 StartupTimeMetricCollection(String pkgName, int size) { 426 mAppBeforeVmRunTotalTime = new ArrayList<>(size); 427 mAppBeforeVmRunWaitTime = new ArrayList<>(size); 428 429 mAppDuringVmRunTotalTime = new ArrayList<>(size); 430 mAppDuringVmRunWaitTime = new ArrayList<>(size); 431 432 mAppAfterVmRunTotalTime = new ArrayList<>(size); 433 mAppAfterVmRunWaitTime = new ArrayList<>(size); 434 mPkgName = pkgName; 435 } 436 addStartupTimeMetricBeforeVmRun(AmStartupTimeCmdParser m)437 public void addStartupTimeMetricBeforeVmRun(AmStartupTimeCmdParser m) { 438 mAppBeforeVmRunTotalTime.add((double) m.mTotalTime); 439 mAppBeforeVmRunWaitTime.add((double) m.mWaitTime); 440 } 441 addStartupTimeMetricDuringVmRun(AmStartupTimeCmdParser m)442 public void addStartupTimeMetricDuringVmRun(AmStartupTimeCmdParser m) { 443 mAppDuringVmRunTotalTime.add((double) m.mTotalTime); 444 mAppDuringVmRunWaitTime.add((double) m.mWaitTime); 445 } 446 addStartupTimerMetricAfterVmRun(AmStartupTimeCmdParser m)447 public void addStartupTimerMetricAfterVmRun(AmStartupTimeCmdParser m) { 448 mAppAfterVmRunTotalTime.add((double) m.mTotalTime); 449 mAppAfterVmRunWaitTime.add((double) m.mWaitTime); 450 } 451 getPkgName()452 public String getPkgName() { 453 return this.mPkgName; 454 } 455 } 456 getFreeMemoryInfoMb(CommandRunner android)457 private int getFreeMemoryInfoMb(CommandRunner android) 458 throws DeviceNotAvailableException, IllegalArgumentException { 459 int freeMemory = 0; 460 String content = android.runForResult("cat /proc/meminfo").getStdout().trim(); 461 String[] lines = content.split("[\r\n]+"); 462 463 for (String line : lines) { 464 if (line.contains("MemFree:")) { 465 freeMemory = Integer.parseInt(line.replaceAll("\\D+", "")) / 1024; 466 return freeMemory; 467 } 468 } 469 470 throw new IllegalArgumentException(); 471 } 472 unlockScreen(CommandRunner android)473 private void unlockScreen(CommandRunner android) 474 throws DeviceNotAvailableException, InterruptedException { 475 android.run("input keyevent", "KEYCODE_WAKEUP"); 476 Thread.sleep(500); 477 final String ret = 478 android.runForResult("dumpsys nfc | grep 'mScreenState='").getStdout().trim(); 479 if (ret != null && ret.contains("ON_LOCKED")) { 480 android.run("input keyevent", "KEYCODE_MENU"); 481 } 482 } 483 updateBootloaderTimeInfo(Map<String, List<Double>> bootloaderTime)484 private void updateBootloaderTimeInfo(Map<String, List<Double>> bootloaderTime) 485 throws Exception { 486 487 String bootLoaderVal = getDevice().getProperty(BOOTLOADER_TIME_PROP_NAME); 488 // Sample Output : 1BLL:89,1BLE:590,2BLL:0,2BLE:1344,SW:6734,KL:1193 489 if (bootLoaderVal != null) { 490 String[] bootLoaderPhases = bootLoaderVal.split(","); 491 double bootLoaderTotalTime = 0d; 492 for (String bootLoaderPhase : bootLoaderPhases) { 493 String[] bootKeyVal = bootLoaderPhase.split(":"); 494 String key = String.format("%s%s", BOOTLOADER_PREFIX, bootKeyVal[0]); 495 496 bootloaderTime 497 .computeIfAbsent(key, k -> new ArrayList<>()) 498 .add(Double.parseDouble(bootKeyVal[1])); 499 // SW is the time spent on the warning screen. So ignore it in 500 // final boot time calculation. 501 if (BOOTLOADER_PHASE_SW.equalsIgnoreCase(bootKeyVal[0])) { 502 continue; 503 } 504 bootLoaderTotalTime += Double.parseDouble(bootKeyVal[1]); 505 } 506 bootloaderTime 507 .computeIfAbsent(BOOTLOADER_TIME, k -> new ArrayList<>()) 508 .add(bootLoaderTotalTime); 509 } 510 } 511 getDmesgBootTime()512 private Double getDmesgBootTime() throws Exception { 513 514 CommandRunner android = new CommandRunner(getDevice()); 515 String result = android.run("dmesg"); 516 Pattern pattern = Pattern.compile("\\[(.*)].*sys.boot_completed=1.*"); 517 for (String line : result.split("[\r\n]+")) { 518 Matcher matcher = pattern.matcher(line); 519 if (matcher.find()) { 520 return Double.valueOf(matcher.group(1)); 521 } 522 } 523 throw new IllegalArgumentException("Failed to get boot time info."); 524 } 525 composTestHelper(boolean isWithCompos, String osKey)526 private void composTestHelper(boolean isWithCompos, String osKey) throws Exception { 527 assumeFalse("Skip on CF; too slow", isCuttlefish()); 528 if (isWithCompos) { 529 assumeKernelSupported(osKey); 530 assumeVmTypeSupported(osKey, true); 531 } else { 532 assertThat(osKey).isNull(); 533 } 534 535 List<Double> bootDmesgTime = new ArrayList<>(ROUND_COUNT); 536 537 for (int round = 0; round < ROUND_COUNT; ++round) { 538 reInstallApex(REINSTALL_APEX_TIMEOUT_SEC); 539 try { 540 if (isWithCompos) { 541 String os = SUPPORTED_OSES.get(osKey); 542 compileStagedApex(COMPILE_STAGED_APEX_TIMEOUT_SEC, os); 543 } 544 } finally { 545 // If compilation fails, we still have a staged APEX, and we need to reboot to 546 // clean that up for further tests. 547 getDevice().nonBlockingReboot(); 548 waitForBootCompleted(); 549 } 550 551 double elapsedSec = getDmesgBootTime(); 552 bootDmesgTime.add(elapsedSec); 553 } 554 555 String suffix = ""; 556 if (isWithCompos) { 557 suffix = "with_compos"; 558 } else { 559 suffix = "without_compos"; 560 } 561 562 reportMetric(bootDmesgTime, "dmesg_boot_time_" + suffix, "s"); 563 } 564 reportMetric(List<Double> data, String name, String unit)565 private void reportMetric(List<Double> data, String name, String unit) { 566 CLog.d("Report metric " + name + "(" + unit + ") : " + data.toString()); 567 Map<String, Double> stats = mMetricsProcessor.computeStats(data, name, unit); 568 for (Map.Entry<String, Double> entry : stats.entrySet()) { 569 CLog.d("Add test metrics " + entry.getKey() + " : " + entry.getValue().toString()); 570 mMetrics.addTestMetric(entry.getKey(), entry.getValue().toString()); 571 } 572 } 573 waitForBootCompleted()574 private void waitForBootCompleted() throws Exception { 575 getDevice().waitForDeviceOnline(BOOT_COMPLETE_TIMEOUT_MS); 576 getDevice().waitForBootComplete(BOOT_COMPLETE_TIMEOUT_MS); 577 getDevice().enableAdbRoot(); 578 } 579 compileStagedApex(int timeoutSec, String os)580 private void compileStagedApex(int timeoutSec, String os) throws Exception { 581 582 long timeStart = System.currentTimeMillis(); 583 long timeEnd = timeStart + timeoutSec * 1000L; 584 585 while (true) { 586 587 try { 588 CommandRunner android = new CommandRunner(getDevice()); 589 590 String result = 591 android.runWithTimeout( 592 3 * 60 * 1000, COMPOSD_CMD_BIN + " staged-apex-compile --os " + os); 593 assertWithMessage("Failed to compile staged APEX. Reason: " + result) 594 .that(result) 595 .ignoringCase() 596 .contains("all ok"); 597 598 CLog.i("Success to compile staged APEX. Result: " + result); 599 600 break; 601 } catch (AssertionError e) { 602 CLog.i("Gets AssertionError when compile staged APEX. Detail: " + e); 603 } 604 605 if (System.currentTimeMillis() > timeEnd) { 606 CLog.e("Try to compile staged APEX several times but all fail."); 607 throw new AssertionError("Failed to compile staged APEX."); 608 } 609 610 Thread.sleep(COMPILE_STAGED_APEX_RETRY_INTERVAL_MS); 611 } 612 } 613 reInstallApex(int timeoutSec)614 private void reInstallApex(int timeoutSec) throws Exception { 615 616 long timeStart = System.currentTimeMillis(); 617 long timeEnd = timeStart + timeoutSec * 1000L; 618 619 while (true) { 620 621 try { 622 CommandRunner android = new CommandRunner(getDevice()); 623 624 String packagesOutput = android.run("pm list packages -f --apex-only"); 625 626 Pattern p = 627 Pattern.compile( 628 "package:(.*)=(com(?:\\.google)?\\.android\\.art)$", 629 Pattern.MULTILINE); 630 Matcher m = p.matcher(packagesOutput); 631 assertWithMessage("ART module not found. Packages are:\n" + packagesOutput) 632 .that(m.find()) 633 .isTrue(); 634 635 String artApexPath = m.group(1); 636 637 CommandResult result = android.runForResult("pm install --apex " + artApexPath); 638 assertWithMessage("Failed to install APEX. Reason: " + result) 639 .that(result.getExitCode()) 640 .isEqualTo(0); 641 642 CLog.i("Success to install APEX. Result: " + result); 643 644 break; 645 } catch (AssertionError e) { 646 CLog.i("Gets AssertionError when reinstall art APEX. Detail: " + e); 647 } 648 649 if (System.currentTimeMillis() > timeEnd) { 650 CLog.e("Try to reinstall art APEX several times but all fail."); 651 throw new AssertionError("Failed to reinstall art APEX."); 652 } 653 654 Thread.sleep(REINSTALL_APEX_RETRY_INTERVAL_MS); 655 } 656 } 657 } 658