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 package com.android.microdroid.test.device; 17 18 import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE; 19 import static android.content.pm.PackageManager.FEATURE_LEANBACK; 20 import static android.content.pm.PackageManager.FEATURE_VIRTUALIZATION_FRAMEWORK; 21 import static android.content.pm.PackageManager.FEATURE_WATCH; 22 23 import static com.google.common.truth.Truth.assertThat; 24 import static com.google.common.truth.TruthJUnit.assume; 25 26 import static org.junit.Assume.assumeFalse; 27 import static org.junit.Assume.assumeTrue; 28 29 import android.app.Instrumentation; 30 import android.app.UiAutomation; 31 import android.content.Context; 32 import android.os.ParcelFileDescriptor; 33 import android.os.SystemProperties; 34 import android.system.Os; 35 import android.system.virtualmachine.VirtualMachine; 36 import android.system.virtualmachine.VirtualMachineCallback; 37 import android.system.virtualmachine.VirtualMachineConfig; 38 import android.system.virtualmachine.VirtualMachineException; 39 import android.system.virtualmachine.VirtualMachineManager; 40 import android.util.Log; 41 42 import androidx.annotation.CallSuper; 43 import androidx.test.core.app.ApplicationProvider; 44 import androidx.test.platform.app.InstrumentationRegistry; 45 46 import com.android.microdroid.test.common.DeviceProperties; 47 import com.android.microdroid.test.common.MetricsProcessor; 48 import com.android.microdroid.testservice.ITestService; 49 import com.android.virt.vm_attestation.testservice.IAttestationService; 50 import com.android.virt.vm_attestation.testservice.IAttestationService.SigningResult; 51 52 import java.io.BufferedReader; 53 import java.io.ByteArrayOutputStream; 54 import java.io.File; 55 import java.io.IOException; 56 import java.io.InputStream; 57 import java.io.InputStreamReader; 58 import java.util.Arrays; 59 import java.util.Collections; 60 import java.util.HashSet; 61 import java.util.OptionalLong; 62 import java.util.Set; 63 import java.util.concurrent.CompletableFuture; 64 import java.util.concurrent.ExecutorService; 65 import java.util.concurrent.Executors; 66 import java.util.concurrent.TimeUnit; 67 68 public abstract class MicrodroidDeviceTestBase { 69 private static final String TAG = "MicrodroidDeviceTestBase"; 70 private final String MAX_PERFORMANCE_TASK_PROFILE = "CPUSET_SP_TOP_APP"; 71 72 protected static final String KERNEL_VERSION = SystemProperties.get("ro.kernel.version"); 73 protected static final Set<String> SUPPORTED_OSES = 74 Collections.unmodifiableSet( 75 new HashSet<>( 76 Arrays.asList( 77 "microdroid", 78 "microdroid_16k", 79 "microdroid_gki-android15-6.6"))); 80 isCuttlefish()81 public static boolean isCuttlefish() { 82 return getDeviceProperties().isCuttlefish(); 83 } 84 isCuttlefishArm64()85 private static boolean isCuttlefishArm64() { 86 return getDeviceProperties().isCuttlefishArm64(); 87 } 88 isGoldfish()89 public static boolean isGoldfish() { 90 return getDeviceProperties().isGoldfish(); 91 } 92 isGoldfishArm64()93 private static boolean isGoldfishArm64() { 94 return getDeviceProperties().isGoldfishArm64(); 95 } 96 isHwasan()97 public static boolean isHwasan() { 98 return getDeviceProperties().isHwasan(); 99 } 100 isUserBuild()101 public static boolean isUserBuild() { 102 return getDeviceProperties().isUserBuild(); 103 } 104 getMetricPrefix()105 public static String getMetricPrefix() { 106 return MetricsProcessor.getMetricPrefix(getDeviceProperties().getMetricsTag()); 107 } 108 getDeviceProperties()109 private static DeviceProperties getDeviceProperties() { 110 return DeviceProperties.create(SystemProperties::get); 111 } 112 grantPermission(String permission)113 protected final void grantPermission(String permission) { 114 Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); 115 UiAutomation uiAutomation = instrumentation.getUiAutomation(); 116 uiAutomation.grantRuntimePermission( 117 instrumentation.getContext().getPackageName(), permission); 118 } 119 revokePermission(String permission)120 protected final void revokePermission(String permission) { 121 Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); 122 UiAutomation uiAutomation = instrumentation.getUiAutomation(); 123 uiAutomation.revokeRuntimePermission( 124 instrumentation.getContext().getPackageName(), permission); 125 } 126 setMaxPerformanceTaskProfile()127 protected final void setMaxPerformanceTaskProfile() throws IOException { 128 Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); 129 UiAutomation uiAutomation = instrumentation.getUiAutomation(); 130 String cmd = "settaskprofile " + Os.gettid() + " " + MAX_PERFORMANCE_TASK_PROFILE; 131 String out = runInShell(TAG, uiAutomation, cmd).trim(); 132 String expect = "Profile " + MAX_PERFORMANCE_TASK_PROFILE + " is applied successfully!"; 133 if (!expect.equals(out)) { 134 throw new IOException("Could not apply max performance task profile: " + out); 135 } 136 } 137 138 private final Context mCtx = ApplicationProvider.getApplicationContext(); 139 private boolean mProtectedVm; 140 private String mOs; 141 getContext()142 protected Context getContext() { 143 return mCtx; 144 } 145 getVirtualMachineManager()146 public VirtualMachineManager getVirtualMachineManager() { 147 return mCtx.getSystemService(VirtualMachineManager.class); 148 } 149 newVmConfigBuilderWithPayloadConfig(String configPath)150 public VirtualMachineConfig.Builder newVmConfigBuilderWithPayloadConfig(String configPath) { 151 return new VirtualMachineConfig.Builder(mCtx) 152 .setProtectedVm(mProtectedVm) 153 .setOs(os()) 154 .setPayloadConfigPath(configPath); 155 } 156 newVmConfigBuilderWithPayloadBinary(String binaryPath)157 public VirtualMachineConfig.Builder newVmConfigBuilderWithPayloadBinary(String binaryPath) { 158 return new VirtualMachineConfig.Builder(mCtx) 159 .setProtectedVm(mProtectedVm) 160 .setOs(os()) 161 .setPayloadBinaryName(binaryPath); 162 } 163 isProtectedVm()164 protected final boolean isProtectedVm() { 165 return mProtectedVm; 166 } 167 os()168 protected final String os() { 169 return mOs; 170 } 171 172 /** 173 * Creates a new virtual machine, potentially removing an existing virtual machine with given 174 * name. 175 */ forceCreateNewVirtualMachine(String name, VirtualMachineConfig config)176 public VirtualMachine forceCreateNewVirtualMachine(String name, VirtualMachineConfig config) 177 throws VirtualMachineException { 178 final VirtualMachineManager vmm = getVirtualMachineManager(); 179 deleteVirtualMachineIfExists(name); 180 return vmm.create(name, config); 181 } 182 deleteVirtualMachineIfExists(String name)183 protected void deleteVirtualMachineIfExists(String name) throws VirtualMachineException { 184 VirtualMachineManager vmm = getVirtualMachineManager(); 185 boolean deleteExisting; 186 try { 187 deleteExisting = vmm.get(name) != null; 188 } catch (VirtualMachineException e) { 189 // VM exists, i.e. there are some files for it, but they could not be successfully 190 // loaded. 191 deleteExisting = true; 192 } 193 if (deleteExisting) { 194 vmm.delete(name); 195 } 196 } 197 prepareTestSetup(boolean protectedVm, String os)198 public void prepareTestSetup(boolean protectedVm, String os) { 199 assumeFeatureVirtualizationFramework(); 200 201 mProtectedVm = protectedVm; 202 mOs = os; 203 204 int capabilities = getVirtualMachineManager().getCapabilities(); 205 if (protectedVm) { 206 assume().withMessage("Skip where protected VMs aren't supported") 207 .that(capabilities & VirtualMachineManager.CAPABILITY_PROTECTED_VM) 208 .isNotEqualTo(0); 209 assume().withMessage("Testing protected VMs on GSI isn't supported. b/272443823") 210 .that(isGsi()) 211 .isFalse(); 212 // TODO(b/376870129): remove this 213 assume().withMessage("pVMs with 16k kernel are not supported yet :(") 214 .that(mOs) 215 .doesNotContain("_16k"); 216 } else { 217 assume().withMessage("Skip where VMs aren't supported") 218 .that(capabilities & VirtualMachineManager.CAPABILITY_NON_PROTECTED_VM) 219 .isNotEqualTo(0); 220 } 221 222 try { 223 assume().withMessage("Skip where requested OS \"" + os() + "\" isn't supported") 224 .that(os()) 225 .isIn(getVirtualMachineManager().getSupportedOSList()); 226 } catch (VirtualMachineException e) { 227 Log.e(TAG, "Error getting supported OS list", e); 228 throw new RuntimeException("Failed to get supported OS list.", e); 229 } 230 } 231 assumeFeatureVirtualizationFramework()232 protected void assumeFeatureVirtualizationFramework() { 233 assume().withMessage("Device doesn't support AVF") 234 .that(mCtx.getPackageManager().hasSystemFeature(FEATURE_VIRTUALIZATION_FRAMEWORK)) 235 .isTrue(); 236 int vendorApiLevel = getVendorApiLevel(); 237 boolean isGsi = isGsi(); 238 Log.i(TAG, "isGsi = " + isGsi + ", vendor api level = " + vendorApiLevel); 239 assume().withMessage("GSI with vendor API level < 202404 may not support AVF") 240 .that(isGsi && vendorApiLevel < 202404) 241 .isFalse(); 242 } 243 244 protected void assumeVsrCompliant() { 245 boolean featureCheck = 246 mCtx.getPackageManager().hasSystemFeature(FEATURE_WATCH) 247 || mCtx.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE) 248 || mCtx.getPackageManager().hasSystemFeature(FEATURE_LEANBACK); 249 assume().withMessage("This device is not VSR compliant").that(featureCheck).isFalse(); 250 } 251 252 protected boolean isGsi() { 253 return new File("/system/system_ext/etc/init/init.gsi.rc").exists(); 254 } 255 256 protected static int getVendorApiLevel() { 257 return SystemProperties.getInt("ro.board.api_level", 0); 258 } 259 260 protected void assumeSupportedDevice() { 261 assume().withMessage("Skip on 5.4 kernel. b/218303240") 262 .that(KERNEL_VERSION) 263 .isNotEqualTo("5.4"); 264 265 // Cuttlefish/Goldfish on Arm 64 doesn't and cannot support any form of virtualization, 266 // so there's no point running any of these tests. 267 assume().withMessage( 268 "Virtualization not supported on Arm64 Cuttlefish/Goldfish." 269 + " b/341889915") 270 .that(isCuttlefishArm64() || isGoldfishArm64()) 271 .isFalse(); 272 } 273 274 protected void assumeNoUpdatableVmSupport() throws VirtualMachineException { 275 assume().withMessage("Secretkeeper not supported") 276 .that(getVirtualMachineManager().isUpdatableVmSupported()) 277 .isFalse(); 278 } 279 280 public abstract static class VmEventListener implements VirtualMachineCallback { 281 private ExecutorService mExecutorService = Executors.newSingleThreadExecutor(); 282 private OptionalLong mVcpuStartedNanoTime = OptionalLong.empty(); 283 private OptionalLong mKernelStartedNanoTime = OptionalLong.empty(); 284 private OptionalLong mInitStartedNanoTime = OptionalLong.empty(); 285 private OptionalLong mPayloadStartedNanoTime = OptionalLong.empty(); 286 private StringBuilder mConsoleOutput = new StringBuilder(); 287 private StringBuilder mLogOutput = new StringBuilder(); 288 private boolean mProcessedBootTimeMetrics = false; 289 290 private synchronized void processBootTimeMetrics(String log) { 291 if (!mVcpuStartedNanoTime.isPresent()) { 292 mVcpuStartedNanoTime = OptionalLong.of(System.nanoTime()); 293 } 294 if (log.contains("Starting payload...") && !mKernelStartedNanoTime.isPresent()) { 295 mKernelStartedNanoTime = OptionalLong.of(System.nanoTime()); 296 } 297 if (log.contains("Run /init as init process") && !mInitStartedNanoTime.isPresent()) { 298 mInitStartedNanoTime = OptionalLong.of(System.nanoTime()); 299 } 300 if (log.contains("microdroid_manager") 301 && log.contains("executing main task") 302 && !mPayloadStartedNanoTime.isPresent()) { 303 mPayloadStartedNanoTime = OptionalLong.of(System.nanoTime()); 304 } 305 } 306 307 private void logVmOutputAndMonitorBootTimeMetrics( 308 String tag, InputStream vmOutputStream, String name, StringBuilder result) { 309 mProcessedBootTimeMetrics = true; 310 new Thread( 311 () -> { 312 try { 313 BufferedReader reader = 314 new BufferedReader( 315 new InputStreamReader(vmOutputStream)); 316 String line; 317 while ((line = reader.readLine()) != null 318 && !Thread.interrupted()) { 319 processBootTimeMetrics(line); 320 Log.i(tag, name + ": " + line); 321 result.append(line + "\n"); 322 } 323 } catch (Exception e) { 324 Log.w(tag, name, e); 325 } 326 }) 327 .start(); 328 } 329 runToFinish(String logTag, VirtualMachine vm)330 public void runToFinish(String logTag, VirtualMachine vm) 331 throws VirtualMachineException, InterruptedException { 332 vm.setCallback(mExecutorService, this); 333 vm.run(); 334 if (vm.getConfig().isVmOutputCaptured()) { 335 logVmOutputAndMonitorBootTimeMetrics( 336 logTag, vm.getConsoleOutput(), "Console", mConsoleOutput); 337 logVmOutputAndMonitorBootTimeMetrics(logTag, vm.getLogOutput(), "Log", mLogOutput); 338 } 339 mExecutorService.awaitTermination(300, TimeUnit.SECONDS); 340 } 341 getVcpuStartedNanoTime()342 public OptionalLong getVcpuStartedNanoTime() { 343 return mVcpuStartedNanoTime; 344 } 345 getKernelStartedNanoTime()346 public OptionalLong getKernelStartedNanoTime() { 347 return mKernelStartedNanoTime; 348 } 349 getInitStartedNanoTime()350 public OptionalLong getInitStartedNanoTime() { 351 return mInitStartedNanoTime; 352 } 353 getPayloadStartedNanoTime()354 public OptionalLong getPayloadStartedNanoTime() { 355 return mPayloadStartedNanoTime; 356 } 357 getConsoleOutput()358 public String getConsoleOutput() { 359 return mConsoleOutput.toString(); 360 } 361 getLogOutput()362 public String getLogOutput() { 363 return mLogOutput.toString(); 364 } 365 hasProcessedBootTimeMetrics()366 public boolean hasProcessedBootTimeMetrics() { 367 return mProcessedBootTimeMetrics; 368 } 369 forceStop(VirtualMachine vm)370 protected void forceStop(VirtualMachine vm) { 371 try { 372 vm.stop(); 373 } catch (VirtualMachineException e) { 374 throw new RuntimeException(e); 375 } 376 } 377 378 @Override onPayloadStarted(VirtualMachine vm)379 public void onPayloadStarted(VirtualMachine vm) {} 380 381 @Override onPayloadReady(VirtualMachine vm)382 public void onPayloadReady(VirtualMachine vm) {} 383 384 @Override onPayloadFinished(VirtualMachine vm, int exitCode)385 public void onPayloadFinished(VirtualMachine vm, int exitCode) {} 386 387 @Override onError(VirtualMachine vm, int errorCode, String message)388 public void onError(VirtualMachine vm, int errorCode, String message) {} 389 390 @Override 391 @CallSuper onStopped(VirtualMachine vm, int reason)392 public void onStopped(VirtualMachine vm, int reason) { 393 vm.clearCallback(); 394 mExecutorService.shutdown(); 395 } 396 } 397 398 public enum BootTimeMetric { 399 TOTAL, 400 VM_START, 401 BOOTLOADER, 402 KERNEL, 403 USERSPACE, 404 } 405 406 public static class BootResult { 407 public final boolean payloadStarted; 408 public final int deathReason; 409 public final long apiCallNanoTime; 410 public final long endToEndNanoTime; 411 412 public final boolean processedBootTimeMetrics; 413 public final OptionalLong vcpuStartedNanoTime; 414 public final OptionalLong kernelStartedNanoTime; 415 public final OptionalLong initStartedNanoTime; 416 public final OptionalLong payloadStartedNanoTime; 417 418 public final String consoleOutput; 419 public final String logOutput; 420 BootResult( boolean payloadStarted, int deathReason, long apiCallNanoTime, long endToEndNanoTime, boolean processedBootTimeMetrics, OptionalLong vcpuStartedNanoTime, OptionalLong kernelStartedNanoTime, OptionalLong initStartedNanoTime, OptionalLong payloadStartedNanoTime, String consoleOutput, String logOutput)421 BootResult( 422 boolean payloadStarted, 423 int deathReason, 424 long apiCallNanoTime, 425 long endToEndNanoTime, 426 boolean processedBootTimeMetrics, 427 OptionalLong vcpuStartedNanoTime, 428 OptionalLong kernelStartedNanoTime, 429 OptionalLong initStartedNanoTime, 430 OptionalLong payloadStartedNanoTime, 431 String consoleOutput, 432 String logOutput) { 433 this.apiCallNanoTime = apiCallNanoTime; 434 this.payloadStarted = payloadStarted; 435 this.deathReason = deathReason; 436 this.endToEndNanoTime = endToEndNanoTime; 437 this.processedBootTimeMetrics = processedBootTimeMetrics; 438 this.vcpuStartedNanoTime = vcpuStartedNanoTime; 439 this.kernelStartedNanoTime = kernelStartedNanoTime; 440 this.initStartedNanoTime = initStartedNanoTime; 441 this.payloadStartedNanoTime = payloadStartedNanoTime; 442 this.consoleOutput = consoleOutput; 443 this.logOutput = logOutput; 444 } 445 getVcpuStartedNanoTime()446 private long getVcpuStartedNanoTime() { 447 return vcpuStartedNanoTime.getAsLong(); 448 } 449 getKernelStartedNanoTime()450 private long getKernelStartedNanoTime() { 451 // pvmfw emits log at the end which is used to estimate the kernelStart time. 452 // In case of no pvmfw run(non-protected mode), use vCPU started time instead. 453 return kernelStartedNanoTime.orElse(vcpuStartedNanoTime.getAsLong()); 454 } 455 getInitStartedNanoTime()456 private long getInitStartedNanoTime() { 457 return initStartedNanoTime.getAsLong(); 458 } 459 getPayloadStartedNanoTime()460 private long getPayloadStartedNanoTime() { 461 return payloadStartedNanoTime.getAsLong(); 462 } 463 getVMStartingElapsedNanoTime()464 public long getVMStartingElapsedNanoTime() { 465 return getVcpuStartedNanoTime() - apiCallNanoTime; 466 } 467 getBootloaderElapsedNanoTime()468 public long getBootloaderElapsedNanoTime() { 469 return getKernelStartedNanoTime() - getVcpuStartedNanoTime(); 470 } 471 getKernelElapsedNanoTime()472 public long getKernelElapsedNanoTime() { 473 return getInitStartedNanoTime() - getKernelStartedNanoTime(); 474 } 475 getUserspaceElapsedNanoTime()476 public long getUserspaceElapsedNanoTime() { 477 return getPayloadStartedNanoTime() - getInitStartedNanoTime(); 478 } 479 getBootTimeMetricNanoTime(BootTimeMetric metric)480 public OptionalLong getBootTimeMetricNanoTime(BootTimeMetric metric) { 481 if (metric == BootTimeMetric.TOTAL) { 482 return OptionalLong.of(endToEndNanoTime); 483 } 484 485 if (processedBootTimeMetrics) { 486 switch (metric) { 487 case VM_START: 488 return OptionalLong.of(getVMStartingElapsedNanoTime()); 489 case BOOTLOADER: 490 return OptionalLong.of(getBootloaderElapsedNanoTime()); 491 case KERNEL: 492 return OptionalLong.of(getKernelElapsedNanoTime()); 493 case USERSPACE: 494 return OptionalLong.of(getUserspaceElapsedNanoTime()); 495 } 496 } 497 498 return OptionalLong.empty(); 499 } 500 } 501 tryBootVm(String logTag, String vmName)502 public BootResult tryBootVm(String logTag, String vmName) 503 throws VirtualMachineException, InterruptedException { 504 VirtualMachine vm = getVirtualMachineManager().get(vmName); 505 final CompletableFuture<Boolean> payloadStarted = new CompletableFuture<>(); 506 final CompletableFuture<Integer> deathReason = new CompletableFuture<>(); 507 final CompletableFuture<Long> endTime = new CompletableFuture<>(); 508 VmEventListener listener = 509 new VmEventListener() { 510 @Override 511 public void onPayloadStarted(VirtualMachine vm) { 512 endTime.complete(System.nanoTime()); 513 payloadStarted.complete(true); 514 forceStop(vm); 515 } 516 517 @Override 518 public void onStopped(VirtualMachine vm, int reason) { 519 deathReason.complete(reason); 520 super.onStopped(vm, reason); 521 } 522 }; 523 long apiCallNanoTime = System.nanoTime(); 524 listener.runToFinish(logTag, vm); 525 return new BootResult( 526 payloadStarted.getNow(false), 527 deathReason.getNow(VmEventListener.STOP_REASON_INFRASTRUCTURE_ERROR), 528 apiCallNanoTime, 529 endTime.getNow(apiCallNanoTime) - apiCallNanoTime, 530 listener.hasProcessedBootTimeMetrics(), 531 listener.getVcpuStartedNanoTime(), 532 listener.getKernelStartedNanoTime(), 533 listener.getInitStartedNanoTime(), 534 listener.getPayloadStartedNanoTime(), 535 listener.getConsoleOutput(), 536 listener.getLogOutput()); 537 } 538 539 /** Execute a command. Returns stdout. */ runInShell(String tag, UiAutomation uiAutomation, String command)540 protected String runInShell(String tag, UiAutomation uiAutomation, String command) { 541 try (InputStream is = 542 new ParcelFileDescriptor.AutoCloseInputStream( 543 uiAutomation.executeShellCommand(command)); 544 ByteArrayOutputStream out = new ByteArrayOutputStream()) { 545 is.transferTo(out); 546 String stdout = out.toString("UTF-8"); 547 Log.i(tag, "Got stdout : " + stdout); 548 return stdout; 549 } catch (IOException e) { 550 Log.e(tag, "Error executing: " + command, e); 551 throw new RuntimeException("Failed to run the command.", e); 552 } 553 } 554 555 /** Execute a command. Returns the concatenation of stdout and stderr. */ runInShellWithStderr(String tag, UiAutomation uiAutomation, String command)556 protected String runInShellWithStderr(String tag, UiAutomation uiAutomation, String command) { 557 ParcelFileDescriptor[] files = uiAutomation.executeShellCommandRwe(command); 558 try (InputStream stdout = new ParcelFileDescriptor.AutoCloseInputStream(files[0]); 559 InputStream stderr = new ParcelFileDescriptor.AutoCloseInputStream(files[2]); 560 ByteArrayOutputStream out = new ByteArrayOutputStream()) { 561 files[1].close(); // The command's stdin 562 stdout.transferTo(out); 563 stderr.transferTo(out); 564 String output = out.toString("UTF-8"); 565 Log.i(tag, "Got output : " + stdout); 566 return output; 567 } catch (IOException e) { 568 Log.e(tag, "Error executing: " + command, e); 569 throw new RuntimeException("Failed to run the command.", e); 570 } 571 } 572 573 protected static class TestResults { 574 public Exception mException; 575 public Integer mAddInteger; 576 public String mAppRunProp; 577 public String mSublibRunProp; 578 public String mExtraApkTestProp; 579 public String mApkContentsPath; 580 public String mEncryptedStoragePath; 581 public String[] mEffectiveCapabilities; 582 public int mUid; 583 public String mFileContent; 584 public byte[] mBcc; 585 public long[] mTimings; 586 public int mFileMode; 587 public int mMountFlags; 588 public String mConsoleInput; 589 public byte[] mInstanceSecret; 590 public int mPageSize; 591 assertNoException()592 public void assertNoException() { 593 if (mException != null) { 594 // Rethrow, wrapped in a new exception, so we get stack traces of the original 595 // failure as well as the body of the test. 596 throw new RuntimeException(mException); 597 } 598 } 599 } 600 runVmAttestationService( String logTag, VirtualMachine vm, byte[] challenge, byte[] messageToSign)601 protected SigningResult runVmAttestationService( 602 String logTag, VirtualMachine vm, byte[] challenge, byte[] messageToSign) 603 throws Exception { 604 605 CompletableFuture<Exception> exception = new CompletableFuture<>(); 606 CompletableFuture<Boolean> payloadReady = new CompletableFuture<>(); 607 CompletableFuture<SigningResult> signingResultFuture = new CompletableFuture<>(); 608 VmEventListener listener = 609 new VmEventListener() { 610 @Override 611 public void onPayloadReady(VirtualMachine vm) { 612 payloadReady.complete(true); 613 try { 614 IAttestationService service = 615 IAttestationService.Stub.asInterface( 616 vm.connectToVsockServer(IAttestationService.PORT)); 617 signingResultFuture.complete( 618 service.signWithAttestationKey(challenge, messageToSign)); 619 } catch (Exception e) { 620 exception.complete(e); 621 } finally { 622 forceStop(vm); 623 } 624 } 625 }; 626 listener.runToFinish(TAG, vm); 627 628 assertThat(payloadReady.getNow(false)).isTrue(); 629 assertThat(exception.getNow(null)).isNull(); 630 SigningResult signingResult = signingResultFuture.getNow(null); 631 assertThat(signingResult).isNotNull(); 632 return signingResult; 633 } 634 runVmTestService( String logTag, VirtualMachine vm, RunTestsAgainstTestService testsToRun)635 protected TestResults runVmTestService( 636 String logTag, VirtualMachine vm, RunTestsAgainstTestService testsToRun) 637 throws Exception { 638 CompletableFuture<Boolean> payloadStarted = new CompletableFuture<>(); 639 CompletableFuture<Boolean> payloadReady = new CompletableFuture<>(); 640 CompletableFuture<Boolean> payloadFinished = new CompletableFuture<>(); 641 TestResults testResults = new TestResults(); 642 VmEventListener listener = 643 new VmEventListener() { 644 ITestService mTestService = null; 645 646 private void initializeTestService(VirtualMachine vm) { 647 try { 648 mTestService = 649 ITestService.Stub.asInterface( 650 vm.connectToVsockServer(ITestService.PORT)); 651 // Make sure linkToDeath works, and include it in the log in case it's 652 // helpful. 653 mTestService 654 .asBinder() 655 .linkToDeath( 656 () -> Log.i(logTag, "ITestService binder died"), 0); 657 } catch (Exception e) { 658 testResults.mException = e; 659 } 660 } 661 662 private void testVMService(VirtualMachine vm) { 663 try { 664 if (mTestService == null) initializeTestService(vm); 665 testsToRun.runTests(mTestService, testResults); 666 } catch (Exception e) { 667 testResults.mException = e; 668 } 669 } 670 671 private void quitVMService() { 672 try { 673 mTestService.quit(); 674 } catch (Exception e) { 675 testResults.mException = e; 676 } 677 } 678 679 @Override 680 public void onPayloadReady(VirtualMachine vm) { 681 Log.i(logTag, "onPayloadReady"); 682 payloadReady.complete(true); 683 testVMService(vm); 684 quitVMService(); 685 } 686 687 @Override 688 public void onPayloadStarted(VirtualMachine vm) { 689 Log.i(logTag, "onPayloadStarted"); 690 payloadStarted.complete(true); 691 } 692 693 @Override 694 public void onPayloadFinished(VirtualMachine vm, int exitCode) { 695 Log.i(logTag, "onPayloadFinished: " + exitCode); 696 payloadFinished.complete(true); 697 forceStop(vm); 698 } 699 }; 700 701 listener.runToFinish(logTag, vm); 702 assertThat(payloadStarted.getNow(false)).isTrue(); 703 assertThat(payloadReady.getNow(false)).isTrue(); 704 assertThat(payloadFinished.getNow(false)).isTrue(); 705 return testResults; 706 } 707 708 @FunctionalInterface 709 protected interface RunTestsAgainstTestService { runTests(ITestService testService, TestResults testResults)710 void runTests(ITestService testService, TestResults testResults) throws Exception; 711 } 712 assumeFeatureEnabled(String featureName)713 protected void assumeFeatureEnabled(String featureName) throws Exception { 714 assumeTrue(featureName + " not enabled", isFeatureEnabled(featureName)); 715 } 716 isFeatureEnabled(String featureName)717 protected boolean isFeatureEnabled(String featureName) throws Exception { 718 return getVirtualMachineManager().isFeatureEnabled(featureName); 719 } 720 assumeProtectedVM()721 protected void assumeProtectedVM() { 722 assumeTrue("Skip on non-protected VM", mProtectedVm); 723 } 724 assumeNonProtectedVM()725 protected void assumeNonProtectedVM() { 726 assumeFalse("Skip on protected VM", mProtectedVm); 727 } 728 } 729