Merge "vmbase: bionic: Make FILE* non-dereferencable" into main
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java b/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
index e18aca2..b3c0746 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -709,6 +709,10 @@
         config.consoleInputDevice = mConsoleInputDevice;
         config.devices = EMPTY_STRING_ARRAY;
         config.platformVersion = "~1.0";
+        config.audioConfig =
+                Optional.ofNullable(customImageConfig.getAudioConfig())
+                        .map(ac -> ac.toParcelable())
+                        .orElse(null);
         return config;
     }
 
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java b/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
index 7fbfb33..125e01c 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
@@ -39,6 +39,7 @@
     private static final String KEY_SWITCHES = "switches";
     private static final String KEY_NETWORK = "network";
     private static final String KEY_GPU = "gpu";
+    private static final String KEY_AUDIO_CONFIG = "audio_config";
 
     @Nullable private final String name;
     @Nullable private final String kernelPath;
@@ -47,6 +48,7 @@
     @Nullable private final String[] params;
     @Nullable private final Disk[] disks;
     @Nullable private final DisplayConfig displayConfig;
+    @Nullable private final AudioConfig audioConfig;
     private final boolean touch;
     private final boolean keyboard;
     private final boolean mouse;
@@ -118,7 +120,8 @@
             boolean mouse,
             boolean switches,
             boolean network,
-            GpuConfig gpuConfig) {
+            GpuConfig gpuConfig,
+            AudioConfig audioConfig) {
         this.name = name;
         this.kernelPath = kernelPath;
         this.initrdPath = initrdPath;
@@ -132,6 +135,7 @@
         this.switches = switches;
         this.network = network;
         this.gpuConfig = gpuConfig;
+        this.audioConfig = audioConfig;
     }
 
     static VirtualMachineCustomImageConfig from(PersistableBundle customImageConfigBundle) {
@@ -164,6 +168,9 @@
         builder.useMouse(customImageConfigBundle.getBoolean(KEY_MOUSE));
         builder.useNetwork(customImageConfigBundle.getBoolean(KEY_NETWORK));
         builder.setGpuConfig(GpuConfig.from(customImageConfigBundle.getPersistableBundle(KEY_GPU)));
+        PersistableBundle audioConfigPb =
+                customImageConfigBundle.getPersistableBundle(KEY_AUDIO_CONFIG);
+        builder.setAudioConfig(AudioConfig.from(audioConfigPb));
         return builder.build();
     }
 
@@ -200,10 +207,18 @@
         pb.putPersistableBundle(
                 KEY_GPU,
                 Optional.ofNullable(gpuConfig).map(gc -> gc.toPersistableBundle()).orElse(null));
+        pb.putPersistableBundle(
+                KEY_AUDIO_CONFIG,
+                Optional.ofNullable(audioConfig).map(ac -> ac.toPersistableBundle()).orElse(null));
         return pb;
     }
 
     @Nullable
+    public AudioConfig getAudioConfig() {
+        return audioConfig;
+    }
+
+    @Nullable
     public DisplayConfig getDisplayConfig() {
         return displayConfig;
     }
@@ -252,6 +267,7 @@
         private String bootloaderPath;
         private List<String> params = new ArrayList<>();
         private List<Disk> disks = new ArrayList<>();
+        private AudioConfig audioConfig;
         private DisplayConfig displayConfig;
         private boolean touch;
         private boolean keyboard;
@@ -342,6 +358,12 @@
         }
 
         /** @hide */
+        public Builder setAudioConfig(AudioConfig audioConfig) {
+            this.audioConfig = audioConfig;
+            return this;
+        }
+
+        /** @hide */
         public VirtualMachineCustomImageConfig build() {
             return new VirtualMachineCustomImageConfig(
                     this.name,
@@ -356,7 +378,83 @@
                     mouse,
                     switches,
                     network,
-                    gpuConfig);
+                    gpuConfig,
+                    audioConfig);
+        }
+    }
+
+    /** @hide */
+    public static final class AudioConfig {
+        private static final String KEY_USE_MICROPHONE = "use_microphone";
+        private static final String KEY_USE_SPEAKER = "use_speaker";
+        private final boolean useMicrophone;
+        private final boolean useSpeaker;
+
+        private AudioConfig(boolean useMicrophone, boolean useSpeaker) {
+            this.useMicrophone = useMicrophone;
+            this.useSpeaker = useSpeaker;
+        }
+
+        /** @hide */
+        public boolean useMicrophone() {
+            return useMicrophone;
+        }
+
+        /** @hide */
+        public boolean useSpeaker() {
+            return useSpeaker;
+        }
+
+        android.system.virtualizationservice.AudioConfig toParcelable() {
+            android.system.virtualizationservice.AudioConfig parcelable =
+                    new android.system.virtualizationservice.AudioConfig();
+            parcelable.useSpeaker = this.useSpeaker;
+            parcelable.useMicrophone = this.useMicrophone;
+
+            return parcelable;
+        }
+
+        private static AudioConfig from(PersistableBundle pb) {
+            if (pb == null) {
+                return null;
+            }
+            Builder builder = new Builder();
+            builder.setUseMicrophone(pb.getBoolean(KEY_USE_MICROPHONE));
+            builder.setUseSpeaker(pb.getBoolean(KEY_USE_SPEAKER));
+            return builder.build();
+        }
+
+        private PersistableBundle toPersistableBundle() {
+            PersistableBundle pb = new PersistableBundle();
+            pb.putBoolean(KEY_USE_MICROPHONE, this.useMicrophone);
+            pb.putBoolean(KEY_USE_SPEAKER, this.useSpeaker);
+            return pb;
+        }
+
+        /** @hide */
+        public static class Builder {
+            private boolean useMicrophone = false;
+            private boolean useSpeaker = false;
+
+            /** @hide */
+            public Builder() {}
+
+            /** @hide */
+            public Builder setUseMicrophone(boolean useMicrophone) {
+                this.useMicrophone = useMicrophone;
+                return this;
+            }
+
+            /** @hide */
+            public Builder setUseSpeaker(boolean useSpeaker) {
+                this.useSpeaker = useSpeaker;
+                return this;
+            }
+
+            /** @hide */
+            public AudioConfig build() {
+                return new AudioConfig(useMicrophone, useSpeaker);
+            }
         }
     }
 
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 10dafdf..671a012 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/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::{CrosvmConfig, DiskFile, DisplayConfig, GpuConfig, InputDeviceOption, PayloadState, VmContext, VmInstance, VmState};
+use crate::crosvm::{AudioConfig, CrosvmConfig, DiskFile, DisplayConfig, GpuConfig, 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};
@@ -578,8 +578,12 @@
         } else {
             None
         };
-        let virtio_snd_backend =
-            if cfg!(paravirtualized_devices) { Some(String::from("aaudio")) } else { None };
+
+        let audio_config = if cfg!(paravirtualized_devices) {
+            config.audioConfig.as_ref().map(AudioConfig::new)
+        } else {
+            None
+        };
 
         // Actually start the VM.
         let crosvm_config = CrosvmConfig {
@@ -610,10 +614,10 @@
             input_device_options,
             hugepages: config.hugePages,
             tap,
-            virtio_snd_backend,
             console_input_device: config.consoleInputDevice.clone(),
             boost_uclamp: config.boostUclamp,
             gpu_config,
+            audio_config,
         };
         let instance = Arc::new(
             VmInstance::new(
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 13c018b..a9a91fe 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -46,6 +46,7 @@
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
     MemoryTrimLevel::MemoryTrimLevel,
     VirtualMachineAppConfig::DebugLevel::DebugLevel,
+    AudioConfig::AudioConfig as AudioConfigParcelable,
     DisplayConfig::DisplayConfig as DisplayConfigParcelable,
     GpuConfig::GpuConfig as GpuConfigParcelable,
 };
@@ -131,10 +132,22 @@
     pub input_device_options: Vec<InputDeviceOption>,
     pub hugepages: bool,
     pub tap: Option<File>,
-    pub virtio_snd_backend: Option<String>,
     pub console_input_device: Option<String>,
     pub boost_uclamp: bool,
     pub gpu_config: Option<GpuConfig>,
+    pub audio_config: Option<AudioConfig>,
+}
+
+#[derive(Debug)]
+pub struct AudioConfig {
+    pub use_microphone: bool,
+    pub use_speaker: bool,
+}
+
+impl AudioConfig {
+    pub fn new(raw_config: &AudioConfigParcelable) -> Self {
+        AudioConfig { use_microphone: raw_config.useMicrophone, use_speaker: raw_config.useSpeaker }
+    }
 }
 
 #[derive(Debug)]
@@ -1159,8 +1172,12 @@
     command.preserved_fds(preserved_fds);
 
     if cfg!(paravirtualized_devices) {
-        if let Some(virtio_snd_backend) = &config.virtio_snd_backend {
-            command.arg("--virtio-snd").arg(format!("backend={}", virtio_snd_backend));
+        if let Some(audio_config) = &config.audio_config {
+            command.arg("--virtio-snd").arg(format!(
+                "backend=aaudio,num_input_devices={},num_output_devices={}",
+                if audio_config.use_microphone { 1 } else { 0 },
+                if audio_config.use_speaker { 1 } else { 0 }
+            ));
         }
     }
 
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/AudioConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/AudioConfig.aidl
new file mode 100644
index 0000000..3e62d95
--- /dev/null
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/AudioConfig.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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 AudioConfig {
+    boolean useMicrophone;
+    boolean useSpeaker;
+}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index 69664b4..07d52db 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -15,6 +15,7 @@
  */
 package android.system.virtualizationservice;
 
+import android.system.virtualizationservice.AudioConfig;
 import android.system.virtualizationservice.CpuTopology;
 import android.system.virtualizationservice.DiskImage;
 import android.system.virtualizationservice.DisplayConfig;
@@ -97,4 +98,6 @@
     boolean boostUclamp;
 
     @nullable GpuConfig gpuConfig;
+
+    @nullable AudioConfig audioConfig;
 }
diff --git a/vmlauncher_app/AndroidManifest.xml b/vmlauncher_app/AndroidManifest.xml
index bae3227..f39e53b 100644
--- a/vmlauncher_app/AndroidManifest.xml
+++ b/vmlauncher_app/AndroidManifest.xml
@@ -5,6 +5,7 @@
     <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
     <uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-feature android:name="android.software.virtualization_framework" android:required="true" />
     <application
         android:label="VmLauncherApp">
diff --git a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
index e13d2c9..e9262ed 100644
--- a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
+++ b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
@@ -16,8 +16,10 @@
 
 package com.android.virtualization.vmlauncher;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.system.virtualmachine.VirtualMachineConfig.CPU_TOPOLOGY_MATCH_HOST;
 
+import android.Manifest.permission;
 import android.app.Activity;
 import android.crosvm.ICrosvmAndroidDisplayService;
 import android.graphics.PixelFormat;
@@ -31,6 +33,7 @@
 import android.system.virtualmachine.VirtualMachineCallback;
 import android.system.virtualmachine.VirtualMachineConfig;
 import android.system.virtualmachine.VirtualMachineCustomImageConfig;
+import android.system.virtualmachine.VirtualMachineCustomImageConfig.AudioConfig;
 import android.system.virtualmachine.VirtualMachineCustomImageConfig.DisplayConfig;
 import android.system.virtualmachine.VirtualMachineCustomImageConfig.GpuConfig;
 import android.system.virtualmachine.VirtualMachineException;
@@ -76,6 +79,7 @@
     private ExecutorService mExecutorService;
     private VirtualMachine mVirtualMachine;
     private ParcelFileDescriptor mCursorStream;
+    private static final int RECORD_AUDIO_PERMISSION_REQUEST_CODE = 101;
 
     private VirtualMachineConfig createVirtualMachineConfig(String jsonPath) {
         VirtualMachineConfig.Builder configBuilder =
@@ -191,6 +195,10 @@
             customImageConfigBuilder.useSwitches(true);
             customImageConfigBuilder.useNetwork(true);
 
+            AudioConfig.Builder audioConfigBuilder = new AudioConfig.Builder();
+            audioConfigBuilder.setUseMicrophone(true);
+            audioConfigBuilder.setUseSpeaker(true);
+            customImageConfigBuilder.setAudioConfig(audioConfigBuilder.build());
             configBuilder.setCustomImageConfig(customImageConfigBuilder.build());
 
         } catch (JSONException | IOException e) {
@@ -224,6 +232,7 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        checkAndRequestRecordAudioPermission();
         mExecutorService = Executors.newCachedThreadPool();
         try {
             // To ensure that the previous display service is removed.
@@ -519,6 +528,14 @@
         }
     }
 
+    private void checkAndRequestRecordAudioPermission() {
+        if (getApplicationContext().checkSelfPermission(permission.RECORD_AUDIO)
+                != PERMISSION_GRANTED) {
+            requestPermissions(
+                    new String[] {permission.RECORD_AUDIO}, RECORD_AUDIO_PERMISSION_REQUEST_CODE);
+        }
+    }
+
     /** Reads data from an input stream and posts it to the output data */
     static class Reader implements Runnable {
         private final String mName;