Merge "Rewrite key management & signing"
diff --git a/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb b/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
index 0f60384..445f731 100644
--- a/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
+++ b/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "8160972"
+    build_id: "8191635"
     target: "u-boot_pvmfw"
     source_file: "pvmfw.img"
   }
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index c48c4fe..14ff111 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -223,7 +223,7 @@
     }
 
     /** Loads a virtual machine that is already created before. */
-    /* package */ static @NonNull VirtualMachine load(
+    /* package */ static @Nullable VirtualMachine load(
             @NonNull Context context, @NonNull String name) throws VirtualMachineException {
         File configFilePath = getConfigFilePath(context, name);
         VirtualMachineConfig config;
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
index 3654886..51fa51f 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
@@ -17,6 +17,7 @@
 package android.system.virtualmachine;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 
 import java.lang.ref.WeakReference;
@@ -72,7 +73,7 @@
      * Returns an existing {@link VirtualMachine} with the given name. Returns null if there is no
      * such virtual machine.
      */
-    public @NonNull VirtualMachine get(@NonNull String name) throws VirtualMachineException {
+    public @Nullable VirtualMachine get(@NonNull String name) throws VirtualMachineException {
         return VirtualMachine.load(mContext, name);
     }
 
diff --git a/pvmfw/pvmfw.img b/pvmfw/pvmfw.img
index 27c2d2b..5e99b5f 100644
--- a/pvmfw/pvmfw.img
+++ b/pvmfw/pvmfw.img
Binary files differ
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index 1e96246..10b90d3 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -86,7 +86,7 @@
         // Run MicrodroidTests#connectToVmService test, which should fail
         final DeviceTestRunOptions options = new DeviceTestRunOptions(PACKAGE_NAME)
                 .setTestClassName(PACKAGE_NAME + ".MicrodroidTests")
-                .setTestMethodName("connectToVmService")
+                .setTestMethodName("connectToVmService[protectedVm=false]")
                 .setCheckResults(false);
         assertFalse(runDeviceTests(options));
 
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index 63fdca1..e76fbc9 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -18,19 +18,15 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.TruthJUnit.assume;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeNoException;
 
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 
 import android.content.Context;
 import android.os.Build;
-import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemProperties;
+import android.sysprop.HypervisorProperties;
 import android.system.virtualmachine.VirtualMachine;
 import android.system.virtualmachine.VirtualMachineCallback;
 import android.system.virtualmachine.VirtualMachineConfig;
@@ -49,7 +45,7 @@
 import org.junit.Test;
 import org.junit.rules.Timeout;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+import org.junit.runners.Parameterized;
 
 import java.io.File;
 import java.io.IOException;
@@ -58,10 +54,9 @@
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
-@RunWith(JUnit4.class)
+@RunWith(Parameterized.class)
 public class MicrodroidTests {
     @Rule public Timeout globalTimeout = Timeout.seconds(300);
 
@@ -73,6 +68,14 @@
         public VirtualMachine mVm;
     }
 
+    @Parameterized.Parameters(name = "protectedVm={0}")
+    public static Object[] protectedVmConfigs() {
+        return new Object[] { false, true };
+    }
+
+    @Parameterized.Parameter
+    public boolean mProtectedVm;
+
     private boolean mPkvmSupported = false;
     private Inner mInner;
 
@@ -88,6 +91,17 @@
             assumeNoException(e);
             return;
         }
+        if (mProtectedVm) {
+            assume()
+                .withMessage("Skip where protected VMs aren't support")
+                .that(HypervisorProperties.hypervisor_protected_vm_supported().orElse(false))
+                .isTrue();
+        } else {
+            assume()
+                .withMessage("Skip where VMs aren't support")
+                .that(HypervisorProperties.hypervisor_vm_supported().orElse(false))
+                .isTrue();
+        }
         mInner = new Inner();
         mInner.mContext = ApplicationProvider.getApplicationContext();
         mInner.mVmm = VirtualMachineManager.getInstance(mInner.mContext);
@@ -98,6 +112,9 @@
         if (!mPkvmSupported) {
             return;
         }
+        if (mInner == null) {
+            return;
+        }
         if (mInner.mVm == null) {
             return;
         }
@@ -142,7 +159,7 @@
         }
     }
 
-    private static final int MIN_MEM_ARM64 = 135;
+    private static final int MIN_MEM_ARM64 = 145;
     private static final int MIN_MEM_X86_64 = 196;
 
     @Test
@@ -153,8 +170,8 @@
             .isNotEqualTo("5.4");
 
         VirtualMachineConfig.Builder builder =
-                new VirtualMachineConfig.Builder(mInner.mContext,
-                        "assets/vm_config_extra_apk.json");
+                new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config_extra_apk.json")
+                        .protectedVm(mProtectedVm);
         if (Build.SUPPORTED_ABIS.length > 0) {
             String primaryAbi = Build.SUPPORTED_ABIS[0];
             switch(primaryAbi) {
@@ -169,58 +186,55 @@
         VirtualMachineConfig config = builder.build();
 
         mInner.mVm = mInner.mVmm.getOrCreate("test_vm_extra_apk", config);
+
+        class TestResults {
+            Exception mException;
+            Integer mAddInteger;
+            String mAppRunProp;
+            String mSublibRunProp;
+            String mExtraApkTestProp;
+        }
+        final CompletableFuture<Boolean> payloadStarted = new CompletableFuture<>();
+        final CompletableFuture<Boolean> payloadReady = new CompletableFuture<>();
+        final TestResults testResults = new TestResults();
         VmEventListener listener =
                 new VmEventListener() {
-                    private boolean mPayloadReadyCalled = false;
-                    private boolean mPayloadStartedCalled = false;
-
-                    private void testVMService(Future<IBinder> service) {
+                    private void testVMService(VirtualMachine vm) {
                         try {
-                            IBinder binder = service.get();
-
-                            ITestService testService = ITestService.Stub.asInterface(binder);
-                            assertEquals(
-                                    testService.addInteger(123, 456),
-                                    123 + 456);
-                            assertEquals(
-                                    testService.readProperty("debug.microdroid.app.run"),
-                                    "true");
-                            assertEquals(
-                                    testService.readProperty("debug.microdroid.app.sublib.run"),
-                                    "true");
-                            assertEquals(
-                                    testService.readProperty("debug.microdroid.test.extra_apk"),
-                                    "PASS");
+                            ITestService testService = ITestService.Stub.asInterface(
+                                    vm.connectToVsockServer(ITestService.SERVICE_PORT).get());
+                            testResults.mAddInteger = testService.addInteger(123, 456);
+                            testResults.mAppRunProp =
+                                    testService.readProperty("debug.microdroid.app.run");
+                            testResults.mSublibRunProp =
+                                    testService.readProperty("debug.microdroid.app.sublib.run");
+                            testResults.mExtraApkTestProp =
+                                    testService.readProperty("debug.microdroid.test.extra_apk");
                         } catch (Exception e) {
-                            fail("Exception while testing service: " + e.toString());
+                            testResults.mException = e;
                         }
                     }
 
                     @Override
                     public void onPayloadReady(VirtualMachine vm) {
-                        mPayloadReadyCalled = true;
-                        try {
-                            testVMService(vm.connectToVsockServer(ITestService.SERVICE_PORT));
-                        } catch (Exception e) {
-                            fail("Exception while connecting to service: " + e.toString());
-                        }
-
+                        payloadReady.complete(true);
+                        testVMService(vm);
                         forceStop(vm);
                     }
 
                     @Override
                     public void onPayloadStarted(VirtualMachine vm, ParcelFileDescriptor stream) {
-                        mPayloadStartedCalled = true;
-                    }
-
-                    @Override
-                    public void onDied(VirtualMachine vm, @DeathReason int reason) {
-                        assertTrue(mPayloadReadyCalled);
-                        assertTrue(mPayloadStartedCalled);
-                        super.onDied(vm, reason);
+                        payloadStarted.complete(true);
                     }
                 };
         listener.runToFinish(mInner.mVm);
+        assertThat(payloadStarted.getNow(false)).isTrue();
+        assertThat(payloadReady.getNow(false)).isTrue();
+        assertThat(testResults.mException).isNull();
+        assertThat(testResults.mAddInteger).isEqualTo(123 + 456);
+        assertThat(testResults.mAppRunProp).isEqualTo("true");
+        assertThat(testResults.mSublibRunProp).isEqualTo("true");
+        assertThat(testResults.mExtraApkTestProp).isEqualTo("PASS");
     }
 
     @Test
@@ -237,7 +251,8 @@
             .isNotEqualTo("5.4");
 
         VirtualMachineConfig.Builder builder =
-                new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json");
+                new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json")
+                        .protectedVm(mProtectedVm);
         VirtualMachineConfig normalConfig = builder.debugLevel(DebugLevel.NONE).build();
         mInner.mVm = mInner.mVmm.getOrCreate("test_vm", normalConfig);
         VmEventListener listener =
@@ -260,40 +275,28 @@
         Files.copy(newVmConfig.toPath(), oldVmConfig.toPath(), REPLACE_EXISTING);
         newVm.delete();
         mInner.mVm = mInner.mVmm.get("test_vm"); // re-load with the copied-in config file.
+        final CompletableFuture<Boolean> payloadStarted = new CompletableFuture<>();
         listener =
                 new VmEventListener() {
-                    private boolean mPayloadStarted = false;
-                    private boolean mErrorOccurred = false;
-
                     @Override
                     public void onPayloadStarted(VirtualMachine vm, ParcelFileDescriptor stream) {
-                        mPayloadStarted = true;
+                        payloadStarted.complete(true);
                         forceStop(vm);
                     }
-
-                    @Override
-                    public void onError(VirtualMachine vm, int errorCode, String message) {
-                        mErrorOccurred = true;
-                        forceStop(vm);
-                    }
-
-                    @Override
-                    public void onDied(VirtualMachine vm, @DeathReason int reason) {
-                        assertFalse(mPayloadStarted);
-                        assertTrue(mErrorOccurred);
-                        super.onDied(vm, reason);
-                    }
                 };
         listener.runToFinish(mInner.mVm);
+        assertThat(payloadStarted.getNow(false)).isFalse();
     }
 
     private byte[] launchVmAndGetSecret(String instanceName)
             throws VirtualMachineException, InterruptedException {
         VirtualMachineConfig.Builder builder =
-                new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json");
+                new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json")
+                        .protectedVm(mProtectedVm);
         VirtualMachineConfig normalConfig = builder.debugLevel(DebugLevel.NONE).build();
         mInner.mVm = mInner.mVmm.getOrCreate(instanceName, normalConfig);
         final CompletableFuture<byte[]> secret = new CompletableFuture<>();
+        final CompletableFuture<Exception> exception = new CompletableFuture<>();
         VmEventListener listener =
                 new VmEventListener() {
                     @Override
@@ -302,13 +305,14 @@
                             ITestService testService = ITestService.Stub.asInterface(
                                     vm.connectToVsockServer(ITestService.SERVICE_PORT).get());
                             secret.complete(testService.insecurelyExposeSecret());
+                            forceStop(vm);
                         } catch (Exception e) {
-                            fail("Exception while connecting to service: " + e.toString());
+                            exception.complete(e);
                         }
-                        forceStop(vm);
                     }
                 };
         listener.runToFinish(mInner.mVm);
+        assertThat(exception.getNow(null)).isNull();
         return secret.getNow(null);
     }
 
@@ -361,6 +365,7 @@
 
         VirtualMachineConfig config =
                 new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json")
+                        .protectedVm(mProtectedVm)
                         .debugLevel(DebugLevel.NONE)
                         .build();
 
@@ -370,23 +375,17 @@
 
         mInner.mVm = mInner.mVmm.getOrCreate("test_vm_integrity", config);
 
+        final CompletableFuture<Boolean> payloadReady = new CompletableFuture<>();
         VmEventListener listener =
                 new VmEventListener() {
-                    private boolean mPayloadReadyCalled = false;
-
                     @Override
                     public void onPayloadReady(VirtualMachine vm) {
-                        mPayloadReadyCalled = true;
+                        payloadReady.complete(true);
                         forceStop(vm);
                     }
-
-                    @Override
-                    public void onDied(VirtualMachine vm, @DeathReason int reason) {
-                        assertTrue(mPayloadReadyCalled);
-                        super.onDied(vm, reason);
-                    }
                 };
         listener.runToFinish(mInner.mVm);
+        assertThat(payloadReady.getNow(false)).isTrue();
 
         // Launch the same VM after flipping a bit of the instance image.
         // Flip actual data, as flipping trivial bits like the magic string isn't interesting.
@@ -405,30 +404,16 @@
         instanceFile.close();
 
         mInner.mVm = mInner.mVmm.get("test_vm_integrity"); // re-load the vm with new instance disk
+        final CompletableFuture<Boolean> payloadStarted = new CompletableFuture<>();
         listener =
                 new VmEventListener() {
-                    private boolean mPayloadStarted = false;
-                    private boolean mErrorOccurred = false;
-
                     @Override
                     public void onPayloadStarted(VirtualMachine vm, ParcelFileDescriptor stream) {
-                        mPayloadStarted = true;
+                        payloadStarted.complete(true);
                         forceStop(vm);
                     }
-
-                    @Override
-                    public void onError(VirtualMachine vm, int errorCode, String message) {
-                        mErrorOccurred = true;
-                        forceStop(vm);
-                    }
-
-                    @Override
-                    public void onDied(VirtualMachine vm, @DeathReason int reason) {
-                        assertFalse(mPayloadStarted);
-                        assertTrue(mErrorOccurred);
-                        super.onDied(vm, reason);
-                    }
                 };
         listener.runToFinish(mInner.mVm);
+        assertThat(payloadStarted.getNow(false)).isFalse();
     }
 }