Support multi-file partitions for composite disk

This is a follow-up to 1fee0d8da6bb440127861e278a7dad09b7f506fd.

Bug: 190503456
Test: MicrodroidHostTestCases
Change-Id: If43bf3d10fc773877bba8b02a3088cd26a814ef9
diff --git a/compositediskconfig/src/lib.rs b/compositediskconfig/src/lib.rs
index dc199e4..54f26e5 100644
--- a/compositediskconfig/src/lib.rs
+++ b/compositediskconfig/src/lib.rs
@@ -30,7 +30,11 @@
     /// A label for the partition.
     pub label: String,
     /// The filename of the partition image.
-    pub path: PathBuf,
+    #[serde(default)]
+    pub path: Option<PathBuf>,
+    /// The filename of the partition image.
+    #[serde(default)]
+    pub paths: Vec<PathBuf>,
     /// Whether the partition should be writable.
     #[serde(default)]
     pub writable: bool,
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
index 825c3da..9b8658b 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
@@ -20,8 +20,8 @@
     /** A label for the partition. */
     @utf8InCpp String label;
 
-    /** The backing file descriptor of the partition image. */
-    ParcelFileDescriptor image;
+    /** The backing file descriptors of the partition images. */
+    ParcelFileDescriptor[] images;
 
     /** Whether the partition should be writable by the VM. */
     boolean writable;
diff --git a/virtualizationservice/src/composite.rs b/virtualizationservice/src/composite.rs
index 5f792fd..7b5a258 100644
--- a/virtualizationservice/src/composite.rs
+++ b/virtualizationservice/src/composite.rs
@@ -336,9 +336,9 @@
     Ok((composite_image, files))
 }
 
-/// Given the AIDL config containing a list of partitions, with a [`ParcelFileDescriptor`] for each
-/// partition, return the list of file descriptors which must be passed to the mk_cdisk child
-/// process and the composite disk image partition configuration for it.
+/// Given the AIDL config containing a list of partitions, with [`ParcelFileDescriptor`]s for each
+/// partition, return the list of file descriptors which must be passed to the composite disk image
+/// partition configuration for it.
 fn convert_partitions(partitions: &[Partition]) -> Result<(Vec<PartitionInfo>, Vec<File>), Error> {
     // File descriptors to pass to child process.
     let mut files = vec![];
@@ -346,24 +346,27 @@
     let partitions = partitions
         .iter()
         .map(|partition| {
-            // TODO(b/187187765): This shouldn't be an Option.
-            let file = partition
-                .image
-                .as_ref()
-                .context("Invalid partition image file descriptor")?
-                .as_ref()
-                .try_clone()
-                .context("Failed to clone partition image file descriptor")?;
-            let size = get_partition_size(&file)?;
-            let fd = file.as_raw_fd();
-            files.push(file);
+            let image_files = partition
+                .images
+                .iter()
+                .map(|image| {
+                    let file = image
+                        .as_ref()
+                        .try_clone()
+                        .context("Failed to clone partition image file descriptor")?;
+
+                    let size = get_partition_size(&file)?;
+                    let fd = file.as_raw_fd();
+                    let partition_info_file =
+                        PartitionFileInfo { path: format!("/proc/self/fd/{}", fd).into(), size };
+                    files.push(file);
+                    Ok(partition_info_file)
+                })
+                .collect::<Result<Vec<_>, Error>>()?;
 
             Ok(PartitionInfo {
                 label: partition.label.to_owned(),
-                files: vec![PartitionFileInfo {
-                    path: format!("/proc/self/fd/{}", fd).into(),
-                    size,
-                }],
+                files: image_files,
                 partition_type: ImagePartitionType::LinuxFilesystem,
                 writable: partition.writable,
             })
diff --git a/vm/src/config.rs b/vm/src/config.rs
index 8ea0d8f..c36c714 100644
--- a/vm/src/config.rs
+++ b/vm/src/config.rs
@@ -115,11 +115,30 @@
 }
 
 fn partition_to_parcelable(partition: &Partition) -> Result<AidlPartition, Error> {
-    Ok(AidlPartition {
-        image: Some(open_parcel_file(&partition.path, partition.writable)?),
-        writable: partition.writable,
-        label: partition.label.to_owned(),
-    })
+    if !partition.paths.is_empty() {
+        if partition.path.is_some() {
+            bail!("Partition {} contains both path/paths", &partition.label);
+        }
+        let images = partition
+            .paths
+            .iter()
+            .map(|path| open_parcel_file(&path, partition.writable))
+            .collect::<Result<Vec<_>, _>>()?;
+        Ok(AidlPartition {
+            images,
+            writable: partition.writable,
+            label: partition.label.to_owned(),
+        })
+    } else {
+        let path = partition.path.as_ref().ok_or_else(|| {
+            Error::msg(format!("Partition {} doesn't set path/paths", &partition.label))
+        })?;
+        Ok(AidlPartition {
+            images: vec![open_parcel_file(&path, partition.writable)?],
+            writable: partition.writable,
+            label: partition.label.to_owned(),
+        })
+    }
 }
 
 /// Try to open the given file and wrap it in a [`ParcelFileDescriptor`].