Merge "Pass serial device to VM to report failure reason."
diff --git a/authfs/fd_server/src/aidl.rs b/authfs/fd_server/src/aidl.rs
index f13f249..2975aa4 100644
--- a/authfs/fd_server/src/aidl.rs
+++ b/authfs/fd_server/src/aidl.rs
@@ -174,7 +174,7 @@
                     } else {
                         Err(new_binder_exception(
                             ExceptionCode::SERVICE_SPECIFIC,
-                            "metadata doesn't contain a signature".to_string(),
+                            "metadata doesn't contain a signature",
                         ))
                     }
                 } else {
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index fa9da9d..dfc2f2b 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -10,7 +10,6 @@
         "general-tests",
     ],
     libs: [
-        "gson-prebuilt-jar",
         "tradefed",
     ],
     static_libs: [
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index 58935d8..51d62cb 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -35,16 +35,15 @@
 import com.android.tradefed.util.FileUtil;
 import com.android.tradefed.util.RunUtil;
 
-import com.google.gson.Gson;
-import com.google.gson.annotations.SerializedName;
-
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.io.File;
-import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -115,34 +114,8 @@
                 .contains("android.permission.MANAGE_VIRTUAL_MACHINE permission"));
     }
 
-    // Helper classes for (de)serialization of VM raw configs
-    static class VmRawConfig {
-        String bootloader;
-        List<Disk> disks;
-        int memory_mib;
-        @SerializedName("protected")
-        boolean isProtected;
-        String platform_version;
-    }
-
-    static class Disk {
-        List<Partition> partitions;
-        boolean writable;
-        public void addPartition(String label, String path) {
-            if (partitions == null) {
-                partitions = new ArrayList<Partition>();
-            }
-            Partition partition = new Partition();
-            partition.label = label;
-            partition.path = path;
-            partitions.add(partition);
-        }
-    }
-
-    static class Partition {
-        String label;
-        String path;
-        boolean writable;
+    private static JSONObject newPartition(String label, String path) {
+        return new JSONObject(Map.of("label", label, "path", path));
     }
 
     private void resignVirtApex(File virtApexDir, File signingKey, Map<String, File> keyOverrides) {
@@ -195,7 +168,7 @@
 
     private String runMicrodroidWithResignedImages(File key, Map<String, File> keyOverrides,
             boolean isProtected, boolean daemonize, String consolePath)
-            throws DeviceNotAvailableException, IOException {
+            throws DeviceNotAvailableException, IOException, JSONException {
         CommandRunner android = new CommandRunner(getDevice());
 
         File virtApexDir = FileUtil.createTempDir("virt_apex");
@@ -244,44 +217,45 @@
         //   - its idsig
 
         // Load etc/microdroid.json
-        Gson gson = new Gson();
         File microdroidConfigFile = new File(virtApexEtcDir, "microdroid.json");
-        VmRawConfig config = gson.fromJson(new FileReader(microdroidConfigFile),
-                VmRawConfig.class);
+        JSONObject config = new JSONObject(FileUtil.readStringFromFile(microdroidConfigFile));
 
         // Replace paths so that the config uses re-signed images from TEST_ROOT
-        config.bootloader = config.bootloader.replace(VIRT_APEX, TEST_ROOT);
-        for (Disk disk : config.disks) {
-            for (Partition part : disk.partitions) {
-                part.path = part.path.replace(VIRT_APEX, TEST_ROOT);
+        config.put("bootloader", config.getString("bootloader").replace(VIRT_APEX, TEST_ROOT));
+        JSONArray disks = config.getJSONArray("disks");
+        for (int diskIndex = 0; diskIndex < disks.length(); diskIndex++) {
+            JSONObject disk = disks.getJSONObject(diskIndex);
+            JSONArray partitions = disk.getJSONArray("partitions");
+            for (int partIndex = 0; partIndex < partitions.length(); partIndex++) {
+                JSONObject part = partitions.getJSONObject(partIndex);
+                part.put("path", part.getString("path").replace(VIRT_APEX, TEST_ROOT));
             }
         }
 
         // Add partitions to the second disk
-        Disk secondDisk = config.disks.get(1);
-        secondDisk.addPartition("vbmeta",
-                TEST_ROOT + "etc/fs/microdroid_vbmeta_bootconfig.img");
-        secondDisk.addPartition("bootconfig",
-                TEST_ROOT + "etc/microdroid_bootconfig.full_debuggable");
-        secondDisk.addPartition("vm-instance", instanceImgPath);
+        final String vbmetaPath = TEST_ROOT + "etc/fs/microdroid_vbmeta_bootconfig.img";
+        final String bootconfigPath = TEST_ROOT + "etc/microdroid_bootconfig.full_debuggable";
+        disks.getJSONObject(1).getJSONArray("partitions")
+                .put(newPartition("vbmeta", vbmetaPath))
+                .put(newPartition("bootconfig", bootconfigPath))
+                .put(newPartition("vm-instance", instanceImgPath));
 
         // Add payload image disk with partitions:
         // - payload-metadata
         // - apexes: com.android.os.statsd, com.android.adbd
         // - apk and idsig
-        Disk payloadDisk = new Disk();
-        payloadDisk.addPartition("payload-metadata", payloadMetadataPath);
-        payloadDisk.addPartition("microdroid-apex-0", statsdApexPath);
-        payloadDisk.addPartition("microdroid-apex-1", adbdApexPath);
-        payloadDisk.addPartition("microdroid-apk", apkPath);
-        payloadDisk.addPartition("microdroid-apk-idsig", idSigPath);
-        config.disks.add(payloadDisk);
+        disks.put(new JSONObject().put("writable", false).put("partitions", new JSONArray()
+                .put(newPartition("payload-metadata", payloadMetadataPath))
+                .put(newPartition("microdroid-apex-0", statsdApexPath))
+                .put(newPartition("microdroid-apex-1", adbdApexPath))
+                .put(newPartition("microdroid-apk", apkPath))
+                .put(newPartition("microdroid-apk-idsig", idSigPath))));
 
-        config.isProtected = isProtected;
+        config.put("protected", isProtected);
 
         // Write updated raw config
         final String configPath = TEST_ROOT + "raw_config.json";
-        getDevice().pushString(gson.toJson(config), configPath);
+        getDevice().pushString(config.toString(), configPath);
 
         final String logPath = TEST_ROOT + "log";
         final String ret = android.runWithTimeout(
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 27e1846..8df853d 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -443,6 +443,8 @@
             UUID.fromString("7e8221e7-03e6-4969-948b-73a4c809a4f2");
     private static final UUID U_BOOT_ENV_PARTITION_UUID =
             UUID.fromString("0ab72d30-86ae-4d05-81b2-c1760be2b1f9");
+    private static final UUID PVM_FW_PARTITION_UUID =
+            UUID.fromString("90d2174a-038a-4bc6-adf3-824848fc5825");
     private static final long BLOCK_SIZE = 512;
 
     // Find the starting offset which holds the data of a partition having UUID.
@@ -486,42 +488,98 @@
         return payloadStarted.getNow(false);
     }
 
-    @Test
-    public void bootFailsWhenInstanceDiskIsCompromised()
+    private RandomAccessFile prepareInstanceImage(String vmName)
             throws VirtualMachineException, InterruptedException, IOException {
-        assume().withMessage("Skip on Cuttlefish. b/195765441")
-                .that(android.os.Build.DEVICE)
-                .isNotEqualTo("vsoc_x86_64");
-
         VirtualMachineConfig config = mInner.newVmConfigBuilder("assets/vm_config.json")
                 .debugLevel(DebugLevel.NONE)
                 .build();
 
         // Remove any existing VM so we can start from scratch
-        VirtualMachine oldVm = mInner.mVmm.getOrCreate("test_vm_integrity", config);
+        VirtualMachine oldVm = mInner.mVmm.getOrCreate(vmName, config);
         oldVm.delete();
-        mInner.mVmm.getOrCreate("test_vm_integrity", config);
+        mInner.mVmm.getOrCreate(vmName, config);
 
-        assertThat(tryBootVm("test_vm_integrity")).isTrue();
+        assertThat(tryBootVm(vmName)).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.
         File vmRoot = new File(mInner.mContext.getFilesDir(), "vm");
-        File vmDir = new File(vmRoot, "test_vm_integrity");
+        File vmDir = new File(vmRoot, vmName);
         File instanceImgPath = new File(vmDir, "instance.img");
-        RandomAccessFile instanceFile = new RandomAccessFile(instanceImgPath, "rw");
+        return new RandomAccessFile(instanceImgPath, "rw");
 
-        // partitions may or may not exist
-        for (UUID uuid :
-                new UUID[] {
-                    MICRODROID_PARTITION_UUID, U_BOOT_AVB_PARTITION_UUID, U_BOOT_ENV_PARTITION_UUID
-                }) {
-            OptionalLong offset = findPartitionDataOffset(instanceFile, uuid);
-            if (!offset.isPresent()) continue;
+    }
 
-            flipBit(instanceFile, offset.getAsLong());
-            assertThat(tryBootVm("test_vm_integrity")).isFalse();
-            flipBit(instanceFile, offset.getAsLong());
+    private void assertThatPartitionIsMissing(UUID partitionUuid)
+            throws VirtualMachineException, InterruptedException, IOException {
+        RandomAccessFile instanceFile = prepareInstanceImage("test_vm_integrity");
+        assertThat(findPartitionDataOffset(instanceFile, partitionUuid).isPresent())
+                .isFalse();
+    }
+
+    // Flips a bit of given partition, and then see if boot fails.
+    private void assertThatBootFailsAfterCompromisingPartition(UUID partitionUuid)
+            throws VirtualMachineException, InterruptedException, IOException {
+        RandomAccessFile instanceFile = prepareInstanceImage("test_vm_integrity");
+        OptionalLong offset = findPartitionDataOffset(instanceFile, partitionUuid);
+        assertThat(offset.isPresent()).isTrue();
+
+        flipBit(instanceFile, offset.getAsLong());
+        assertThat(tryBootVm("test_vm_integrity")).isFalse();
+    }
+
+    @Test
+    public void bootFailsWhenMicrodroidDataIsCompromised()
+            throws VirtualMachineException, InterruptedException, IOException {
+        assume().withMessage("Skip on Cuttlefish. b/195765441")
+                .that(android.os.Build.DEVICE)
+                .isNotEqualTo("vsoc_x86_64");
+
+        assertThatBootFailsAfterCompromisingPartition(MICRODROID_PARTITION_UUID);
+    }
+
+    @Test
+    public void bootFailsWhenUBootAvbDataIsCompromised()
+            throws VirtualMachineException, InterruptedException, IOException {
+        assume().withMessage("Skip on Cuttlefish. b/195765441")
+                .that(android.os.Build.DEVICE)
+                .isNotEqualTo("vsoc_x86_64");
+
+        if (mProtectedVm) {
+            // TODO(b/218461230): uncomment this after u-boot update
+            // assertThatBootFailsAfterCompromisingPartition(U_BOOT_AVB_PARTITION_UUID);
+        } else {
+            // non-protected VM shouldn't have u-boot avb data
+            assertThatPartitionIsMissing(U_BOOT_AVB_PARTITION_UUID);
+        }
+    }
+
+    @Test
+    public void bootFailsWhenUBootEnvDataIsCompromised()
+            throws VirtualMachineException, InterruptedException, IOException {
+        assume().withMessage("Skip on Cuttlefish. b/195765441")
+                .that(android.os.Build.DEVICE)
+                .isNotEqualTo("vsoc_x86_64");
+
+        if (mProtectedVm) {
+            // TODO(b/218461230): uncomment this after u-boot update
+            // assertThatBootFailsAfterCompromisingPartition(U_BOOT_ENV_PARTITION_UUID);
+        } else {
+            // non-protected VM shouldn't have u-boot env data
+            assertThatPartitionIsMissing(U_BOOT_ENV_PARTITION_UUID);
+        }
+    }
+
+    @Test
+    public void bootFailsWhenPvmFwDataIsCompromised()
+            throws VirtualMachineException, InterruptedException, IOException {
+        assume().withMessage("Skip on Cuttlefish. b/195765441")
+                .that(android.os.Build.DEVICE)
+                .isNotEqualTo("vsoc_x86_64");
+
+        if (mProtectedVm) {
+            assertThatBootFailsAfterCompromisingPartition(PVM_FW_PARTITION_UUID);
+        } else {
+            // non-protected VM shouldn't have pvmfw data
+            assertThatPartitionIsMissing(PVM_FW_PARTITION_UUID);
         }
     }
 }