Add option to disable usb for AVF

Bug: 356447468
Test: boot microdroid, suspend/snapshot/resume, validate no usb.
Change-Id: Ica93d74774f0aa0bd4c80bd0da166d7d3c62e745
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index fb3d353..0acf4be 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -17,7 +17,7 @@
 use crate::{get_calling_pid, get_calling_uid, get_this_pid};
 use crate::atom::{write_vm_booted_stats, write_vm_creation_stats};
 use crate::composite::make_composite_image;
-use crate::crosvm::{AudioConfig, CrosvmConfig, DiskFile, DisplayConfig, GpuConfig, InputDeviceOption, PayloadState, VmContext, VmInstance, VmState};
+use crate::crosvm::{AudioConfig, CrosvmConfig, DiskFile, DisplayConfig, GpuConfig, InputDeviceOption, PayloadState, UsbConfig, 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};
@@ -584,6 +584,13 @@
             None
         };
 
+        let usb_config = config
+            .usbConfig
+            .as_ref()
+            .map(UsbConfig::new)
+            .unwrap_or(Ok(UsbConfig { controller: false }))
+            .or_binder_exception(ExceptionCode::BAD_PARCELABLE)?;
+
         // Actually start the VM.
         let crosvm_config = CrosvmConfig {
             cid,
@@ -623,6 +630,7 @@
             gpu_config,
             audio_config,
             no_balloon: config.noBalloon,
+            usb_config,
         };
         let instance = Arc::new(
             VmInstance::new(
diff --git a/android/virtmgr/src/crosvm.rs b/android/virtmgr/src/crosvm.rs
index 9d688a2..5886535 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -47,6 +47,7 @@
     AudioConfig::AudioConfig as AudioConfigParcelable,
     DisplayConfig::DisplayConfig as DisplayConfigParcelable,
     GpuConfig::GpuConfig as GpuConfigParcelable,
+    UsbConfig::UsbConfig as UsbConfigParcelable,
 };
 use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IGlobalVmContext::IGlobalVmContext;
 use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IBoundDevice::IBoundDevice;
@@ -134,6 +135,7 @@
     pub gpu_config: Option<GpuConfig>,
     pub audio_config: Option<AudioConfig>,
     pub no_balloon: bool,
+    pub usb_config: UsbConfig,
 }
 
 #[derive(Debug)]
@@ -149,6 +151,17 @@
 }
 
 #[derive(Debug)]
+pub struct UsbConfig {
+    pub controller: bool,
+}
+
+impl UsbConfig {
+    pub fn new(raw_config: &UsbConfigParcelable) -> Result<UsbConfig> {
+        Ok(UsbConfig { controller: raw_config.controller })
+    }
+}
+
+#[derive(Debug)]
 pub struct DisplayConfig {
     pub width: NonZeroU32,
     pub height: NonZeroU32,
@@ -900,6 +913,10 @@
         command.arg("--no-balloon");
     }
 
+    if !config.usb_config.controller {
+        command.arg("--no-usb");
+    }
+
     let mut memory_mib = config.memory_mib;
 
     if config.protected {
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/UsbConfig.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/UsbConfig.aidl
new file mode 100644
index 0000000..1889d2c
--- /dev/null
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/UsbConfig.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+parcelable UsbConfig {
+    /** Enable the USB controller */
+    boolean controller;
+}
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index 4ac401d..f559a71 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -21,6 +21,7 @@
 import android.system.virtualizationservice.DisplayConfig;
 import android.system.virtualizationservice.GpuConfig;
 import android.system.virtualizationservice.InputDevice;
+import android.system.virtualizationservice.UsbConfig;
 
 /** Raw configuration for running a VM. */
 parcelable VirtualMachineRawConfig {
@@ -102,4 +103,7 @@
     @nullable AudioConfig audioConfig;
 
     boolean noBalloon;
+
+    /** Enable or disable USB passthrough support */
+    @nullable UsbConfig usbConfig;
 }
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
index 644a85a..cb21ccf 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/libs/framework-virtualization/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.UsbConfig;
 import android.system.virtualizationservice.VirtualMachineAppConfig;
 import android.system.virtualizationservice.VirtualMachinePayloadConfig;
 import android.system.virtualizationservice.VirtualMachineRawConfig;
@@ -725,6 +726,15 @@
                         .map(ac -> ac.toParcelable())
                         .orElse(null);
         config.noBalloon = !customImageConfig.useAutoMemoryBalloon();
+        config.usbConfig =
+                Optional.ofNullable(customImageConfig.getUsbConfig())
+                        .map(
+                                uc -> {
+                                    UsbConfig usbConfig = new UsbConfig();
+                                    usbConfig.controller = uc.getUsbController();
+                                    return usbConfig;
+                                })
+                        .orElse(null);
         return config;
     }
 
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
index a38ee7f..9774585 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
+++ b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
@@ -46,6 +46,7 @@
     private static final String KEY_AUDIO_CONFIG = "audio_config";
     private static final String KEY_TRACKPAD = "trackpad";
     private static final String KEY_AUTO_MEMORY_BALLOON = "auto_memory_balloon";
+    private static final String KEY_USB_CONFIG = "usb_config";
 
     @Nullable private final String name;
     @Nullable private final String kernelPath;
@@ -63,6 +64,7 @@
     @Nullable private final GpuConfig gpuConfig;
     private final boolean trackpad;
     private final boolean autoMemoryBalloon;
+    @Nullable private final UsbConfig usbConfig;
 
     @Nullable
     public Disk[] getDisks() {
@@ -139,7 +141,8 @@
             GpuConfig gpuConfig,
             AudioConfig audioConfig,
             boolean trackpad,
-            boolean autoMemoryBalloon) {
+            boolean autoMemoryBalloon,
+            UsbConfig usbConfig) {
         this.name = name;
         this.kernelPath = kernelPath;
         this.initrdPath = initrdPath;
@@ -156,6 +159,7 @@
         this.audioConfig = audioConfig;
         this.trackpad = trackpad;
         this.autoMemoryBalloon = autoMemoryBalloon;
+        this.usbConfig = usbConfig;
     }
 
     static VirtualMachineCustomImageConfig from(PersistableBundle customImageConfigBundle) {
@@ -208,6 +212,9 @@
         builder.setAudioConfig(AudioConfig.from(audioConfigPb));
         builder.useTrackpad(customImageConfigBundle.getBoolean(KEY_TRACKPAD));
         builder.useAutoMemoryBalloon(customImageConfigBundle.getBoolean(KEY_AUTO_MEMORY_BALLOON));
+        PersistableBundle usbConfigPb =
+                customImageConfigBundle.getPersistableBundle(KEY_USB_CONFIG);
+        builder.setUsbConfig(UsbConfig.from(usbConfigPb));
         return builder.build();
     }
 
@@ -266,6 +273,9 @@
                 Optional.ofNullable(audioConfig).map(ac -> ac.toPersistableBundle()).orElse(null));
         pb.putBoolean(KEY_TRACKPAD, trackpad);
         pb.putBoolean(KEY_AUTO_MEMORY_BALLOON, autoMemoryBalloon);
+        pb.putPersistableBundle(
+                KEY_USB_CONFIG,
+                Optional.ofNullable(usbConfig).map(uc -> uc.toPersistableBundle()).orElse(null));
         return pb;
     }
 
@@ -284,6 +294,11 @@
         return gpuConfig;
     }
 
+    @Nullable
+    public UsbConfig getUsbConfig() {
+        return usbConfig;
+    }
+
     /** @hide */
     public static final class Disk {
         private final boolean writable;
@@ -362,6 +377,7 @@
         private boolean trackpad;
         // TODO(b/363985291): balloon breaks Linux VM behavior
         private boolean autoMemoryBalloon = false;
+        private UsbConfig usbConfig;
 
         /** @hide */
         public Builder() {}
@@ -463,6 +479,12 @@
         }
 
         /** @hide */
+        public Builder setUsbConfig(UsbConfig usbConfig) {
+            this.usbConfig = usbConfig;
+            return this;
+        }
+
+        /** @hide */
         public VirtualMachineCustomImageConfig build() {
             return new VirtualMachineCustomImageConfig(
                     this.name,
@@ -480,7 +502,63 @@
                     gpuConfig,
                     audioConfig,
                     trackpad,
-                    autoMemoryBalloon);
+                    autoMemoryBalloon,
+                    usbConfig);
+        }
+    }
+
+    /** @hide */
+    public static final class UsbConfig {
+        private static final String KEY_USE_CONTROLLER = "use_controller";
+        public final boolean controller;
+
+        public UsbConfig(boolean controller) {
+            this.controller = controller;
+        }
+
+        public boolean getUsbController() {
+            return this.controller;
+        }
+
+        android.system.virtualizationservice.UsbConfig toParceclable() {
+            android.system.virtualizationservice.UsbConfig parcelable =
+                    new android.system.virtualizationservice.UsbConfig();
+            parcelable.controller = this.controller;
+            return parcelable;
+        }
+
+        private static UsbConfig from(PersistableBundle pb) {
+            if (pb == null) {
+                return null;
+            }
+            Builder builder = new Builder();
+            builder.setController(pb.getBoolean(KEY_USE_CONTROLLER));
+            return builder.build();
+        }
+
+        private PersistableBundle toPersistableBundle() {
+            PersistableBundle pb = new PersistableBundle();
+            pb.putBoolean(KEY_USE_CONTROLLER, this.controller);
+            return pb;
+        }
+
+        /** @hide */
+        public static class Builder {
+            private boolean useController = false;
+
+            /** @hide */
+            public Builder() {}
+
+            /** @hide */
+            public Builder setController(boolean useController) {
+                this.useController = useController;
+                return this;
+            }
+
+            /** @hide */
+            public UsbConfig build() {
+                return new UsbConfig(useController);
+            }
         }
     }
 
diff --git a/libs/vmconfig/src/lib.rs b/libs/vmconfig/src/lib.rs
index ff115f3..ef932c2 100644
--- a/libs/vmconfig/src/lib.rs
+++ b/libs/vmconfig/src/lib.rs
@@ -18,6 +18,7 @@
     aidl::android::system::virtualizationservice::CpuTopology::CpuTopology,
     aidl::android::system::virtualizationservice::DiskImage::DiskImage as AidlDiskImage,
     aidl::android::system::virtualizationservice::Partition::Partition as AidlPartition,
+    aidl::android::system::virtualizationservice::UsbConfig::UsbConfig as AidlUsbConfig,
     aidl::android::system::virtualizationservice::VirtualMachineAppConfig::DebugLevel::DebugLevel,
     aidl::android::system::virtualizationservice::VirtualMachineConfig::VirtualMachineConfig,
     aidl::android::system::virtualizationservice::VirtualMachineRawConfig::VirtualMachineRawConfig,
@@ -68,6 +69,8 @@
     pub devices: Vec<PathBuf>,
     /// The serial device for VM console input.
     pub console_input_device: Option<String>,
+    /// The USB config of the VM.
+    pub usb_config: Option<UsbConfig>,
 }
 
 impl VmConfig {
@@ -110,6 +113,7 @@
             Some("match_host") => CpuTopology::MATCH_HOST,
             Some(cpu_topology) => bail!("Invalid cpu topology {}", cpu_topology),
         };
+        let usb_config = self.usb_config.clone().map(|x| x.to_parcelable()).transpose()?;
         Ok(VirtualMachineRawConfig {
             kernel: maybe_open_parcel_file(&self.kernel, false)?,
             initrd: maybe_open_parcel_file(&self.initrd, false)?,
@@ -128,6 +132,7 @@
                 })
                 .collect::<Result<_>>()?,
             consoleInputDevice: self.console_input_device.clone(),
+            usbConfig: usb_config,
             ..Default::default()
         })
     }
@@ -193,6 +198,19 @@
     }
 }
 
+/// USB controller and available USB devices
+#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
+pub struct UsbConfig {
+    /// Enable USB controller
+    pub controller: bool,
+}
+
+impl UsbConfig {
+    fn to_parcelable(&self) -> Result<AidlUsbConfig> {
+        Ok(AidlUsbConfig { controller: self.controller })
+    }
+}
+
 /// Try to open the given file and wrap it in a [`ParcelFileDescriptor`].
 pub fn open_parcel_file(filename: &Path, writable: bool) -> Result<ParcelFileDescriptor> {
     Ok(ParcelFileDescriptor::new(