Support console input to VM

createVm accepts a new ParcelFileDescriptor which when set is used as
the console input in the VM. The `vm` tool sets this to stdin unless
explicitly specified via `--console-in`. This will be useful for
bringing up custom OSes.

Note that Java APIs for this is not added in this CL.

Bug: 263360203
Test: Start a vm using the `vm` tool. Type something. Then do `cat
/dev/console` in the VM. It shows the typed characters.
Change-Id: I5d0dfcc4852387ec50897a8dcae2935dd4a2dd9a
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 86c8596..06274c8 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -172,11 +172,18 @@
     fn createVm(
         &self,
         config: &VirtualMachineConfig,
-        console_fd: Option<&ParcelFileDescriptor>,
+        console_out_fd: Option<&ParcelFileDescriptor>,
+        console_in_fd: Option<&ParcelFileDescriptor>,
         log_fd: Option<&ParcelFileDescriptor>,
     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
         let mut is_protected = false;
-        let ret = self.create_vm_internal(config, console_fd, log_fd, &mut is_protected);
+        let ret = self.create_vm_internal(
+            config,
+            console_out_fd,
+            console_in_fd,
+            log_fd,
+            &mut is_protected,
+        );
         write_vm_creation_stats(config, is_protected, &ret);
         ret
     }
@@ -292,7 +299,8 @@
     fn create_vm_internal(
         &self,
         config: &VirtualMachineConfig,
-        console_fd: Option<&ParcelFileDescriptor>,
+        console_out_fd: Option<&ParcelFileDescriptor>,
+        console_in_fd: Option<&ParcelFileDescriptor>,
         log_fd: Option<&ParcelFileDescriptor>,
         is_protected: &mut bool,
     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
@@ -341,8 +349,9 @@
         };
 
         let state = &mut *self.state.lock().unwrap();
-        let console_fd =
-            clone_or_prepare_logger_fd(&debug_config, console_fd, format!("Console({})", cid))?;
+        let console_out_fd =
+            clone_or_prepare_logger_fd(&debug_config, console_out_fd, format!("Console({})", cid))?;
+        let console_in_fd = console_in_fd.map(clone_file).transpose()?;
         let log_fd = clone_or_prepare_logger_fd(&debug_config, log_fd, format!("Log({})", cid))?;
 
         // Counter to generate unique IDs for temporary image files.
@@ -446,7 +455,8 @@
             cpus,
             host_cpu_topology,
             task_profiles: config.taskProfiles.clone(),
-            console_fd,
+            console_out_fd,
+            console_in_fd,
             log_fd,
             ramdump,
             indirect_files,
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 856ff1e..13367c3 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -107,7 +107,8 @@
     pub cpus: Option<NonZeroU32>,
     pub host_cpu_topology: bool,
     pub task_profiles: Vec<String>,
-    pub console_fd: Option<File>,
+    pub console_out_fd: Option<File>,
+    pub console_in_fd: Option<File>,
     pub log_fd: Option<File>,
     pub ramdump: Option<File>,
     pub indirect_files: Vec<File>,
@@ -776,21 +777,29 @@
     //
     // When [console|log]_fd is not specified, the devices are attached to sink, which means what's
     // written there is discarded.
-    let console_arg = format_serial_arg(&mut preserved_fds, &config.console_fd);
-    let log_arg = format_serial_arg(&mut preserved_fds, &config.log_fd);
+    let console_out_arg = format_serial_out_arg(&mut preserved_fds, &config.console_out_fd);
+    let console_in_arg = config
+        .console_in_fd
+        .as_ref()
+        .map(|fd| format!(",input={}", add_preserved_fd(&mut preserved_fds, fd)))
+        .unwrap_or_default();
+    let log_arg = format_serial_out_arg(&mut preserved_fds, &config.log_fd);
     let failure_serial_path = add_preserved_fd(&mut preserved_fds, &failure_pipe_write);
-    let ramdump_arg = format_serial_arg(&mut preserved_fds, &config.ramdump);
+    let ramdump_arg = format_serial_out_arg(&mut preserved_fds, &config.ramdump);
 
     // Warning: Adding more serial devices requires you to shift the PCI device ID of the boot
     // disks in bootconfig.x86_64. This is because x86 crosvm puts serial devices and the block
     // devices in the same PCI bus and serial devices comes before the block devices. Arm crosvm
     // doesn't have the issue.
     // /dev/ttyS0
-    command.arg(format!("--serial={},hardware=serial,num=1", &console_arg));
+    command.arg(format!("--serial={}{},hardware=serial,num=1", &console_out_arg, &console_in_arg));
     // /dev/ttyS1
     command.arg(format!("--serial=type=file,path={},hardware=serial,num=2", &failure_serial_path));
     // /dev/hvc0
-    command.arg(format!("--serial={},hardware=virtio-console,num=1", &console_arg));
+    command.arg(format!(
+        "--serial={}{},hardware=virtio-console,num=1",
+        &console_out_arg, &console_in_arg
+    ));
     // /dev/hvc1
     command.arg(format!("--serial={},hardware=virtio-console,num=2", &ramdump_arg));
     // /dev/hvc2
@@ -890,7 +899,7 @@
 
 /// Adds the file descriptor for `file` (if any) to `preserved_fds`, and returns the appropriate
 /// string for a crosvm `--serial` flag. If `file` is none, creates a dummy sink device.
-fn format_serial_arg(preserved_fds: &mut Vec<RawFd>, file: &Option<File>) -> String {
+fn format_serial_out_arg(preserved_fds: &mut Vec<RawFd>, file: &Option<File>) -> String {
     if let Some(file) = file {
         format!("type=file,path={}", add_preserved_fd(preserved_fds, file))
     } else {