Merge "Remove old key management"
diff --git a/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb b/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
index 0f60384..85f86d0 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: "8194384"
     target: "u-boot_pvmfw"
     source_file: "pvmfw.img"
   }
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index dcacb0f..63a6fd6 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -112,10 +112,8 @@
                 .context("Failed to create system log file")?;
             let console_fd = ParcelFileDescriptor::new(console_fd);
             let log_fd = ParcelFileDescriptor::new(log_fd);
-            // Full debug is not available in a protected VM
-            let debug_level = if protected_vm { DebugLevel::APP_ONLY } else { DebugLevel::FULL };
-            info!("Debug mode is {:?}", debug_level);
-            (Some(console_fd), Some(log_fd), debug_level)
+            info!("Running in debug mode");
+            (Some(console_fd), Some(log_fd), DebugLevel::FULL)
         } else {
             (None, None, DebugLevel::NONE)
         };
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..395513f 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..30f5933 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,19 +45,20 @@
 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;
 import java.io.RandomAccessFile;
 import java.nio.file.Files;
+import java.util.OptionalLong;
+import java.util.UUID;
 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 +70,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 +93,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 +114,9 @@
         if (!mPkvmSupported) {
             return;
         }
+        if (mInner == null) {
+            return;
+        }
         if (mInner.mVm == null) {
             return;
         }
@@ -142,7 +161,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 +172,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 +188,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 +253,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 +277,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 +307,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);
     }
 
@@ -352,6 +358,35 @@
         assertThat(vm_secret_first_boot).isEqualTo(vm_secret_second_boot);
     }
 
+    private static final UUID MICRODROID_PARTITION_UUID =
+            UUID.fromString("cf9afe9a-0662-11ec-a329-c32663a09d75");
+    private static final long BLOCK_SIZE = 512;
+
+    // Find the starting offset which holds the data of a partition having UUID.
+    // This is a kind of hack; rather than parsing QCOW2 we exploit the fact that the cluster size
+    // is normally greater than 512. It implies that the partition data should exist at a block
+    // which follows the header block
+    private OptionalLong findPartitionDataOffset(RandomAccessFile file, UUID uuid)
+            throws IOException {
+        // For each 512-byte block in file, check header
+        long fileSize = file.length();
+
+        for (long idx = 0; idx + BLOCK_SIZE < fileSize; idx += BLOCK_SIZE) {
+            file.seek(idx);
+            long high = file.readLong();
+            long low = file.readLong();
+            if (uuid.equals(new UUID(high, low))) return OptionalLong.of(idx + BLOCK_SIZE);
+        }
+        return OptionalLong.empty();
+    }
+
+    private void flipBit(RandomAccessFile file, long offset) throws IOException {
+        file.seek(offset);
+        int b = file.readByte();
+        file.seek(offset);
+        file.writeByte(b ^ 1);
+    }
+
     @Test
     public void bootFailsWhenInstanceDiskIsCompromised()
             throws VirtualMachineException, InterruptedException, IOException {
@@ -361,6 +396,7 @@
 
         VirtualMachineConfig config =
                 new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json")
+                        .protectedVm(mProtectedVm)
                         .debugLevel(DebugLevel.NONE)
                         .build();
 
@@ -370,23 +406,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.
@@ -395,40 +425,24 @@
         File instanceImgPath = new File(vmDir, "instance.img");
         RandomAccessFile instanceFile = new RandomAccessFile(instanceImgPath, "rw");
 
-        // microdroid data partition starts at 0x60200, actual data at 0x60400, based on experiment
-        // TODO: parse image file (QEMU qcow2) correctly?
-        long headerOffset = 0x60400;
-        instanceFile.seek(headerOffset);
-        int b = instanceFile.readByte();
-        instanceFile.seek(headerOffset);
-        instanceFile.writeByte(b ^ 1);
-        instanceFile.close();
+        // microdroid data partition must exist.
+        OptionalLong microdroidPartitionOffset =
+                findPartitionDataOffset(instanceFile, MICRODROID_PARTITION_UUID);
+        assertThat(microdroidPartitionOffset.isPresent()).isTrue();
 
+        flipBit(instanceFile, microdroidPartitionOffset.getAsLong());
         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();
+        flipBit(instanceFile, microdroidPartitionOffset.getAsLong());
     }
 }