Add InputDevice in VirtualMachineRawConfig

Starts with single touch, evdev.

Bug: 331191129
Test: build
Change-Id: I68639dec230f2979e145e261d8584813ee1645d7
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java b/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
index a8f318c..d267763 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -40,6 +40,7 @@
 import android.sysprop.HypervisorProperties;
 import android.system.virtualizationservice.DiskImage;
 import android.system.virtualizationservice.Partition;
+import android.system.virtualizationservice.InputDevice;
 import android.system.virtualizationservice.VirtualMachineAppConfig;
 import android.system.virtualizationservice.VirtualMachinePayloadConfig;
 import android.system.virtualizationservice.VirtualMachineRawConfig;
@@ -650,6 +651,7 @@
         config.cpuTopology = (byte) this.mCpuTopology;
         config.devices = EMPTY_STRING_ARRAY;
         config.platformVersion = "~1.0";
+        config.inputDevices = new InputDevice[0];
         return config;
     }
 
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index fa43faf..1f3fb4a 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -17,7 +17,7 @@
 use crate::{get_calling_pid, get_calling_uid};
 use crate::atom::{write_vm_booted_stats, write_vm_creation_stats};
 use crate::composite::make_composite_image;
-use crate::crosvm::{CrosvmConfig, DiskFile, DisplayConfig, PayloadState, VmContext, VmInstance, VmState};
+use crate::crosvm::{CrosvmConfig, DiskFile, DisplayConfig, InputDeviceOption, PayloadState, VmContext, VmInstance, VmState};
 use crate::debug_config::DebugConfig;
 use crate::dt_overlay::{create_device_tree_overlay, VM_DT_OVERLAY_MAX_SIZE, VM_DT_OVERLAY_PATH};
 use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images, add_microdroid_vendor_image};
@@ -32,6 +32,7 @@
     AssignableDevice::AssignableDevice,
     CpuTopology::CpuTopology,
     DiskImage::DiskImage,
+    InputDevice::InputDevice,
     IVirtualMachine::{BnVirtualMachine, IVirtualMachine},
     IVirtualMachineCallback::IVirtualMachineCallback,
     IVirtualizationService::IVirtualizationService,
@@ -592,6 +593,17 @@
             None
         };
 
+        let input_device_options = if cfg!(paravirtualized_devices) {
+            config
+                .inputDevices
+                .iter()
+                .map(to_input_device_option_from)
+                .collect::<Result<Vec<InputDeviceOption>, _>>()
+                .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?
+        } else {
+            vec![]
+        };
+
         // Actually start the VM.
         let crosvm_config = CrosvmConfig {
             cid,
@@ -618,6 +630,7 @@
             dtbo,
             device_tree_overlay,
             display_config,
+            input_device_options,
         };
         let instance = Arc::new(
             VmInstance::new(
@@ -723,6 +736,23 @@
     (result / granularity) * granularity
 }
 
+fn to_input_device_option_from(input_device: &InputDevice) -> Result<InputDeviceOption> {
+    Ok(match input_device {
+        InputDevice::SingleTouch(single_touch) => InputDeviceOption::SingleTouch {
+            file: clone_file(single_touch.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?)?,
+            height: u32::try_from(single_touch.height)?,
+            width: u32::try_from(single_touch.width)?,
+            name: if !single_touch.name.is_empty() {
+                Some(single_touch.name.clone())
+            } else {
+                None
+            },
+        },
+        InputDevice::EvDev(evdev) => InputDeviceOption::EvDev(clone_file(
+            evdev.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?,
+        )?),
+    })
+}
 /// Given the configuration for a disk image, assembles the `DiskFile` to pass to crosvm.
 ///
 /// This may involve assembling a composite disk from a set of partition images.
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 99ad65e..2e4c517 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -120,6 +120,7 @@
     pub dtbo: Option<File>,
     pub device_tree_overlay: Option<File>,
     pub display_config: Option<DisplayConfig>,
+    pub input_device_options: Vec<InputDeviceOption>,
 }
 
 #[derive(Debug)]
@@ -154,6 +155,14 @@
     pub writable: bool,
 }
 
+/// virtio-input device configuration from `external/crosvm/src/crosvm/config.rs`
+#[derive(Debug)]
+#[allow(dead_code)]
+pub enum InputDeviceOption {
+    EvDev(File),
+    SingleTouch { file: File, width: u32, height: u32, name: Option<String> },
+}
+
 type VfioDevice = Strong<dyn IBoundDevice>;
 
 /// The lifecycle state which the payload in the VM has reported itself to be in.
@@ -966,6 +975,23 @@
         }
     }
 
+    if cfg!(paravirtualized_devices) {
+        for input_device_option in config.input_device_options.iter() {
+            command.arg("--input");
+            command.arg(match input_device_option {
+                InputDeviceOption::EvDev(file) => {
+                    format!("evdev[path={}]", add_preserved_fd(&mut preserved_fds, file))
+                }
+                InputDeviceOption::SingleTouch { file, width, height, name } => format!(
+                    "single-touch[path={},width={},height={}{}]",
+                    add_preserved_fd(&mut preserved_fds, file),
+                    width,
+                    height,
+                    name.as_ref().map_or("".into(), |n| format!(",name={}", n))
+                ),
+            });
+        }
+    }
     append_platform_devices(&mut command, &mut preserved_fds, &config)?;
 
     debug!("Preserving FDs {:?}", preserved_fds);
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/InputDevice.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/InputDevice.aidl
new file mode 100644
index 0000000..fe12291
--- /dev/null
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/InputDevice.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.system.virtualizationservice;
+
+// Refer to https://crosvm.dev/book/devices/input.html
+union InputDevice {
+    // Add a single-touch touchscreen virtio-input device.
+    parcelable SingleTouch {
+        ParcelFileDescriptor pfd;
+        // Default values come from https://crosvm.dev/book/devices/input.html#single-touch
+        int width = 1280;
+        int height = 1080;
+        @utf8InCpp String name = "";
+    }
+    // Passes an event device node into the VM. The device will be grabbed (unusable from the host)
+    // and made available to the guest with the same configuration it shows on the host.
+    parcelable EvDev {
+        ParcelFileDescriptor pfd;
+    }
+
+    SingleTouch singleTouch;
+    EvDev evDev;
+}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index 1a18bf8..86e26da 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -18,6 +18,7 @@
 import android.system.virtualizationservice.CpuTopology;
 import android.system.virtualizationservice.DiskImage;
 import android.system.virtualizationservice.DisplayConfig;
+import android.system.virtualizationservice.InputDevice;
 
 /** Raw configuration for running a VM. */
 parcelable VirtualMachineRawConfig {
@@ -73,4 +74,7 @@
     String[] devices;
 
     @nullable DisplayConfig displayConfig;
+
+    /** List of input devices to the VM */
+    InputDevice[] inputDevices;
 }