Merge changes I74c62e77,I18af2ded,Ib9eabfbe into main

* changes:
  VmTerminalApp: Initial skeleton for downloading from server
  VmTerminalApp: Add UI skeleton for download activity and service
  vm_launcher_lib: Fix bug in InstallUtils#isImageInstalled()
diff --git a/android/virtmgr/Android.bp b/android/virtmgr/Android.bp
index d0d7915..ad63995 100644
--- a/android/virtmgr/Android.bp
+++ b/android/virtmgr/Android.bp
@@ -37,6 +37,7 @@
         "libbinder_rs",
         "libcfg_if",
         "libclap",
+        "libcrosvm_control_static",
         "libcstr",
         "libcommand_fds",
         "libdisk",
@@ -62,7 +63,6 @@
         "libstatslog_virtualization_rust",
         "libtombstoned_client_rust",
         "libvbmeta_rust",
-        "libvm_control",
         "libvmconfig",
         "libzip",
         "libvsock",
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index 52bfd87..5dac07f 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, UsbConfig, VmContext, VmInstance, VmState};
+use crate::crosvm::{AudioConfig, CrosvmConfig, DiskFile, SharedPathConfig, 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};
@@ -32,6 +32,7 @@
     AssignableDevice::AssignableDevice,
     CpuTopology::CpuTopology,
     DiskImage::DiskImage,
+    SharedPath::SharedPath,
     InputDevice::InputDevice,
     IVirtualMachine::{self, BnVirtualMachine},
     IVirtualMachineCallback::IVirtualMachineCallback,
@@ -613,6 +614,8 @@
             })
             .collect::<Result<Vec<DiskFile>, _>>()?;
 
+        let shared_paths = assemble_shared_paths(&config.sharedPaths, &temporary_directory)?;
+
         let (cpus, host_cpu_topology) = match config.cpuTopology {
             CpuTopology::MATCH_HOST => (None, true),
             CpuTopology::ONE_CPU => (NonZeroU32::new(1), false),
@@ -719,6 +722,7 @@
             kernel,
             initrd,
             disks,
+            shared_paths,
             params: config.params.to_owned(),
             protected: *is_protected,
             debug_config,
@@ -956,6 +960,32 @@
         },
     })
 }
+
+fn assemble_shared_paths(
+    shared_paths: &[SharedPath],
+    temporary_directory: &Path,
+) -> Result<Vec<SharedPathConfig>, Status> {
+    if shared_paths.is_empty() {
+        return Ok(Vec::new()); // Return an empty vector if shared_paths is empty
+    }
+
+    shared_paths
+        .iter()
+        .map(|path| {
+            Ok(SharedPathConfig {
+                path: path.sharedPath.clone(),
+                host_uid: path.hostUid,
+                host_gid: path.hostGid,
+                guest_uid: path.guestUid,
+                guest_gid: path.guestGid,
+                mask: path.mask,
+                tag: path.tag.clone(),
+                socket_path: temporary_directory.join(&path.socket).to_string_lossy().to_string(),
+            })
+        })
+        .collect()
+}
+
 /// 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/android/virtmgr/src/crosvm.rs b/android/virtmgr/src/crosvm.rs
index 25271f8..86b3571 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -29,12 +29,14 @@
 use shared_child::SharedChild;
 use std::borrow::Cow;
 use std::cmp::max;
+use std::ffi::CString;
 use std::fmt;
 use std::fs::{read_to_string, File};
 use std::io::{self, Read};
 use std::mem;
 use std::num::{NonZeroU16, NonZeroU32};
 use std::os::unix::io::{AsRawFd, OwnedFd};
+use std::os::unix::process::CommandExt;
 use std::os::unix::process::ExitStatusExt;
 use std::path::{Path, PathBuf};
 use std::process::{Command, ExitStatus};
@@ -56,9 +58,6 @@
 use tombstoned_client::{TombstonedConnection, DebuggerdDumpType};
 use rpcbinder::RpcServer;
 
-/// external/crosvm
-use vm_control::{BalloonControlCommand, VmRequest, VmResponse};
-
 const CROSVM_PATH: &str = "/apex/com.android.virt/bin/crosvm";
 
 /// Version of the platform that crosvm currently implements. The format follows SemVer. This
@@ -108,6 +107,7 @@
     pub kernel: Option<File>,
     pub initrd: Option<File>,
     pub disks: Vec<DiskFile>,
+    pub shared_paths: Vec<SharedPathConfig>,
     pub params: Option<String>,
     pub protected: bool,
     pub debug_config: DebugConfig,
@@ -224,6 +224,19 @@
     pub writable: bool,
 }
 
+/// Shared path between host and guest VM.
+#[derive(Debug)]
+pub struct SharedPathConfig {
+    pub path: String,
+    pub host_uid: i32,
+    pub host_gid: i32,
+    pub guest_uid: i32,
+    pub guest_gid: i32,
+    pub mask: i32,
+    pub tag: String,
+    pub socket_path: String,
+}
+
 /// virtio-input device configuration from `external/crosvm/src/crosvm/config.rs`
 #[derive(Debug)]
 #[allow(dead_code)]
@@ -306,6 +319,8 @@
             let tap =
                 if let Some(tap_file) = &config.tap { Some(tap_file.try_clone()?) } else { None };
 
+            run_virtiofs(&config)?;
+
             // If this fails and returns an error, `self` will be left in the `Failed` state.
             let child =
                 Arc::new(run_vm(config, &instance.crosvm_control_socket_path, failure_pipe_write)?);
@@ -638,37 +653,34 @@
         Ok(())
     }
 
-    /// Responds to memory-trimming notifications by inflating the virtio
-    /// balloon to reclaim guest memory.
+    /// Returns current virtio-balloon size.
     pub fn get_memory_balloon(&self) -> Result<u64, Error> {
-        let request = VmRequest::BalloonCommand(BalloonControlCommand::Stats {});
-        let result =
-            match vm_control::client::handle_request(&request, &self.crosvm_control_socket_path) {
-                Ok(VmResponse::BalloonStats { stats: _, balloon_actual }) => balloon_actual,
-                Ok(VmResponse::Err(e)) => {
-                    // ENOTSUP is returned when the balloon protocol is not initialized. This
-                    // can occur for numerous reasons: Guest is still booting, guest doesn't
-                    // support ballooning, host doesn't support ballooning. We don't log or
-                    // raise an error in this case: trim is just a hint and we can ignore it.
-                    if e.errno() != libc::ENOTSUP {
-                        bail!("Errno return when requesting balloon stats: {}", e.errno())
-                    }
-                    0
-                }
-                e => bail!("Error requesting balloon stats: {:?}", e),
-            };
-        Ok(result)
+        let socket_path_cstring = path_to_cstring(&self.crosvm_control_socket_path);
+        let mut balloon_actual = 0u64;
+        // SAFETY: Pointers are valid for the lifetime of the call. Null `stats` is valid.
+        let success = unsafe {
+            crosvm_control::crosvm_client_balloon_stats(
+                socket_path_cstring.as_ptr(),
+                /* stats= */ std::ptr::null_mut(),
+                &mut balloon_actual,
+            )
+        };
+        if !success {
+            bail!("Error requesting balloon stats");
+        }
+        Ok(balloon_actual)
     }
 
-    /// Responds to memory-trimming notifications by inflating the virtio
-    /// balloon to reclaim guest memory.
+    /// Inflates the virtio-balloon by `num_bytes` to reclaim guest memory. Called in response to
+    /// memory-trimming notifications.
     pub fn set_memory_balloon(&self, num_bytes: u64) -> Result<(), Error> {
-        let command = BalloonControlCommand::Adjust { num_bytes, wait_for_success: false };
-        if let Err(e) = vm_control::client::handle_request(
-            &VmRequest::BalloonCommand(command),
-            &self.crosvm_control_socket_path,
-        ) {
-            bail!("Error sending balloon adjustment: {:?}", e);
+        let socket_path_cstring = path_to_cstring(&self.crosvm_control_socket_path);
+        // SAFETY: Pointer is valid for the lifetime of the call.
+        let success = unsafe {
+            crosvm_control::crosvm_client_balloon_vms(socket_path_cstring.as_ptr(), num_bytes)
+        };
+        if !success {
+            bail!("Error sending balloon adjustment");
         }
         Ok(())
     }
@@ -704,26 +716,28 @@
         Ok(())
     }
 
-    /// Suspends the VM
+    /// Suspends the VM's vCPUs.
     pub fn suspend(&self) -> Result<(), Error> {
-        match vm_control::client::handle_request(
-            &VmRequest::SuspendVcpus,
-            &self.crosvm_control_socket_path,
-        ) {
-            Ok(VmResponse::Ok) => Ok(()),
-            e => bail!("Failed to suspend VM: {e:?}"),
+        let socket_path_cstring = path_to_cstring(&self.crosvm_control_socket_path);
+        // SAFETY: Pointer is valid for the lifetime of the call.
+        let success =
+            unsafe { crosvm_control::crosvm_client_suspend_vm(socket_path_cstring.as_ptr()) };
+        if !success {
+            bail!("Failed to suspend VM");
         }
+        Ok(())
     }
 
-    /// Resumes the suspended VM
+    /// Resumes the VM's vCPUs.
     pub fn resume(&self) -> Result<(), Error> {
-        match vm_control::client::handle_request(
-            &VmRequest::ResumeVcpus,
-            &self.crosvm_control_socket_path,
-        ) {
-            Ok(VmResponse::Ok) => Ok(()),
-            e => bail!("Failed to resume: {e:?}"),
+        let socket_path_cstring = path_to_cstring(&self.crosvm_control_socket_path);
+        // SAFETY: Pointer is valid for the lifetime of the call.
+        let success =
+            unsafe { crosvm_control::crosvm_client_resume_vm(socket_path_cstring.as_ptr()) };
+        if !success {
+            bail!("Failed to resume VM");
         }
+        Ok(())
     }
 }
 
@@ -884,6 +898,39 @@
     }
 }
 
+fn run_virtiofs(config: &CrosvmConfig) -> io::Result<()> {
+    for shared_path in &config.shared_paths {
+        let ugid_map_value = format!(
+            "{} {} {} {} {} /",
+            shared_path.guest_uid,
+            shared_path.guest_gid,
+            shared_path.host_uid,
+            shared_path.host_gid,
+            shared_path.mask,
+        );
+
+        let cfg_arg = format!("writeback=true,cache_policy=always,ugid_map='{}'", ugid_map_value);
+
+        let mut command = Command::new(CROSVM_PATH);
+        command
+            .arg("device")
+            .arg("fs")
+            .arg(format!("--socket={}", &shared_path.socket_path))
+            .arg(format!("--tag={}", &shared_path.tag))
+            .arg(format!("--shared-dir={}", &shared_path.path))
+            .arg("--cfg")
+            .arg(cfg_arg.as_str())
+            .arg("--disable-sandbox");
+
+        print_crosvm_args(&command);
+
+        let result = SharedChild::spawn(&mut command)?;
+        info!("Spawned virtiofs crosvm({})", result.id());
+    }
+
+    Ok(())
+}
+
 /// Starts an instance of `crosvm` to manage a new VM.
 fn run_vm(
     config: CrosvmConfig,
@@ -893,6 +940,9 @@
     validate_config(&config)?;
 
     let mut command = Command::new(CROSVM_PATH);
+
+    let vm_name = "crosvm_".to_owned() + &config.name;
+    command.arg0(vm_name.clone());
     // TODO(qwandor): Remove --disable-sandbox.
     command
         .arg("--extended-status")
@@ -901,6 +951,8 @@
         .arg("--log-level")
         .arg("info,disk=warn")
         .arg("run")
+        .arg("--name")
+        .arg(vm_name)
         .arg("--disable-sandbox")
         .arg("--cid")
         .arg(config.cid.to_string());
@@ -1062,6 +1114,9 @@
         command.arg(add_preserved_fd(&mut preserved_fds, kernel));
     }
 
+    #[cfg(target_arch = "aarch64")]
+    command.arg("--no-pmu");
+
     let control_sock = create_crosvm_control_listener(crosvm_control_socket_path)
         .context("failed to create control listener")?;
     command.arg("--socket").arg(add_preserved_fd(&mut preserved_fds, control_sock));
@@ -1187,6 +1242,12 @@
         command.arg(vfio_argument_for_platform_device(&device)?);
     }
 
+    for shared_path in &config.shared_paths {
+        command
+            .arg("--vhost-user-fs")
+            .arg(format!("{},tag={}", &shared_path.socket_path, &shared_path.tag));
+    }
+
     debug!("Preserving FDs {:?}", preserved_fds);
     command.preserved_fds(preserved_fds);
 
@@ -1296,3 +1357,13 @@
     socket::listen(&fd, socket::Backlog::new(127).unwrap()).context("listen failed")?;
     Ok(fd)
 }
+
+fn path_to_cstring(path: &Path) -> CString {
+    if let Some(s) = path.to_str() {
+        if let Ok(s) = CString::new(s) {
+            return s;
+        }
+    }
+    // The path contains invalid utf8 or a null, which should never happen.
+    panic!("bad path: {path:?}");
+}
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/SharedPath.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/SharedPath.aidl
new file mode 100644
index 0000000..7be7a5f
--- /dev/null
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/SharedPath.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+/** Shared directory path between host and guest */
+parcelable SharedPath {
+    /** Shared path between host and guest */
+    String sharedPath;
+
+    /** UID of the path on the host */
+    int hostUid;
+
+    /** GID of the path on the host */
+    int hostGid;
+
+    /** UID of the path on the guest */
+    int guestUid;
+
+    /** GID of the path on the guest */
+    int guestGid;
+
+    /** umask settings for the path */
+    int mask;
+
+    /** virtiofs unique tag per path */
+    String tag;
+
+    /** socket name for vhost-user-fs */
+    String socket;
+}
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index f559a71..9f2a23e 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.SharedPath;
 import android.system.virtualizationservice.UsbConfig;
 
 /** Raw configuration for running a VM. */
@@ -52,6 +53,9 @@
     /** Disk images to be made available to the VM. */
     DiskImage[] disks;
 
+    /** Shared paths between host and guest */
+    SharedPath[] sharedPaths;
+
     /** Whether the VM should be a protected VM. */
     boolean protectedVm;
 
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index f794239..e485aa7 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -285,9 +285,11 @@
         "libz",
     ],
     data: [
-        ":com.android.virt",
         ":test.com.android.virt.pem",
     ],
+    device_common_data: [
+        ":com.android.virt",
+    ],
     test_suites: ["general-tests"],
 }
 
diff --git a/build/microdroid/Android.bp b/build/microdroid/Android.bp
index 27d0246..abb97da 100644
--- a/build/microdroid/Android.bp
+++ b/build/microdroid/Android.bp
@@ -197,7 +197,7 @@
     no_full_install: true,
 }
 
-genrule {
+java_genrule {
     name: "microdroid_build_prop_gen_x86_64",
     srcs: [
         "build.prop",
@@ -215,7 +215,7 @@
         "echo ro.product.cpu.abi=x86_64) > $(out)",
 }
 
-genrule {
+java_genrule {
     name: "microdroid_build_prop_gen_arm64",
     srcs: [
         "build.prop",
@@ -597,6 +597,7 @@
 // HACK: use cc_genrule for arch-specific properties
 cc_genrule {
     name: "microdroid_kernel_hashes_rs",
+    compile_multilib: "first",
     srcs: [":microdroid_kernel"],
     arch: {
         arm64: {
@@ -621,6 +622,7 @@
 
 rust_library_rlib {
     name: "libmicrodroid_kernel_hashes",
+    compile_multilib: "first",
     srcs: [":microdroid_kernel_hashes_rs"],
     crate_name: "microdroid_kernel_hashes",
     prefer_rlib: true,
diff --git a/build/microdroid/initrd/Android.bp b/build/microdroid/initrd/Android.bp
index 9904511..6d45417 100644
--- a/build/microdroid/initrd/Android.bp
+++ b/build/microdroid/initrd/Android.bp
@@ -30,7 +30,7 @@
     srcs: ["gen_vbmeta_bootconfig.py"],
 }
 
-genrule {
+java_genrule {
     name: "microdroid_initrd_gen",
     srcs: [
         ":microdroid_ramdisk",
@@ -40,7 +40,7 @@
     cmd: "cat $(in) > $(out)",
 }
 
-genrule {
+java_genrule {
     name: "microdroid_gki-android15-6.6_initrd_gen_arm64",
     srcs: [
         ":microdroid_ramdisk",
@@ -51,7 +51,7 @@
     cmd: "cat $(in) > $(out)",
 }
 
-genrule {
+java_genrule {
     name: "microdroid_gki-android15-6.6_initrd_gen_x86_64",
     srcs: [
         ":microdroid_ramdisk",
@@ -63,7 +63,7 @@
 }
 
 // This contains vbmeta hashes & related (boot)configs which are passed to kernel/init
-genrule {
+java_genrule {
     name: "microdroid_vbmeta_bootconfig_gen",
     srcs: [":microdroid_vbmeta"],
     out: ["bootconfig_microdroid_vbmeta"],
@@ -84,7 +84,7 @@
     ":microdroid_vbmeta_bootconfig_gen",
 ]
 
-genrule {
+java_genrule {
     name: "microdroid_initrd_debuggable_arm64",
     tools: ["initrd_bootconfig"],
     srcs: [
@@ -95,7 +95,7 @@
     cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
-genrule {
+java_genrule {
     name: "microdroid_gki-android15-6.6_initrd_debuggable_arm64",
     tools: ["initrd_bootconfig"],
     srcs: [
@@ -106,7 +106,7 @@
     cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
-genrule {
+java_genrule {
     name: "microdroid_initrd_debuggable_x86_64",
     tools: ["initrd_bootconfig"],
     srcs: [
@@ -117,7 +117,7 @@
     cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
-genrule {
+java_genrule {
     name: "microdroid_gki-android15-6.6_initrd_debuggable_x86_64",
     tools: ["initrd_bootconfig"],
     srcs: [
@@ -128,7 +128,7 @@
     cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
-genrule {
+java_genrule {
     name: "microdroid_initrd_normal_arm64",
     tools: ["initrd_bootconfig"],
     srcs: [
@@ -139,7 +139,7 @@
     cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
-genrule {
+java_genrule {
     name: "microdroid_gki-android15-6.6_initrd_normal_arm64",
     tools: ["initrd_bootconfig"],
     srcs: [
@@ -150,7 +150,7 @@
     cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
-genrule {
+java_genrule {
     name: "microdroid_initrd_normal_x86_64",
     tools: ["initrd_bootconfig"],
     srcs: [
@@ -161,7 +161,7 @@
     cmd: "$(location initrd_bootconfig) attach --output $(out) $(in)",
 }
 
-genrule {
+java_genrule {
     name: "microdroid_gki-android15-6.6_initrd_normal_x86_64",
     tools: ["initrd_bootconfig"],
     srcs: [
diff --git a/guest/trusty/security_vm/launcher/Android.bp b/guest/trusty/security_vm/launcher/Android.bp
new file mode 100644
index 0000000..ff628fd
--- /dev/null
+++ b/guest/trusty/security_vm/launcher/Android.bp
@@ -0,0 +1,20 @@
+rust_binary {
+    name: "trusty_security_vm_launcher",
+    crate_name: "trusty_security_vm_launcher",
+    srcs: ["src/main.rs"],
+    edition: "2021",
+    prefer_rlib: true,
+    rustlibs: [
+        "android.system.virtualizationservice-rust",
+        "libanyhow",
+        "libclap",
+        "libvmclient",
+    ],
+    bootstrap: true,
+    apex_available: ["//apex_available:platform"],
+    system_ext_specific: true,
+    enabled: select(release_flag("RELEASE_AVF_ENABLE_EARLY_VM"), {
+        true: true,
+        false: false,
+    }),
+}
diff --git a/guest/trusty/security_vm/launcher/src/main.rs b/guest/trusty/security_vm/launcher/src/main.rs
new file mode 100644
index 0000000..c5cc6b4
--- /dev/null
+++ b/guest/trusty/security_vm/launcher/src/main.rs
@@ -0,0 +1,84 @@
+// 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.
+
+//! A client for trusty security VMs during early boot.
+
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+    IVirtualizationService::IVirtualizationService, VirtualMachineConfig::VirtualMachineConfig,
+    VirtualMachineRawConfig::VirtualMachineRawConfig,
+};
+use android_system_virtualizationservice::binder::{ParcelFileDescriptor, Strong};
+use anyhow::{Context, Result};
+use clap::Parser;
+use std::fs::File;
+use std::path::PathBuf;
+use vmclient::VmInstance;
+
+#[derive(Parser)]
+struct Args {
+    /// Path to the trusty kernel image.
+    #[arg(long)]
+    kernel: PathBuf,
+
+    /// Whether the VM is protected or not.
+    #[arg(long)]
+    protected: bool,
+}
+
+fn get_service() -> Result<Strong<dyn IVirtualizationService>> {
+    let virtmgr = vmclient::VirtualizationService::new_early()
+        .context("Failed to spawn VirtualizationService")?;
+    virtmgr.connect().context("Failed to connect to VirtualizationService")
+}
+
+fn main() -> Result<()> {
+    let args = Args::parse();
+
+    let service = get_service()?;
+
+    let kernel =
+        File::open(&args.kernel).with_context(|| format!("Failed to open {:?}", &args.kernel))?;
+
+    let vm_config = VirtualMachineConfig::RawConfig(VirtualMachineRawConfig {
+        name: "trusty_security_vm_launcher".to_owned(),
+        kernel: Some(ParcelFileDescriptor::new(kernel)),
+        protectedVm: args.protected,
+        memoryMib: 128,
+        platformVersion: "~1.0".to_owned(),
+        // TODO: add instanceId
+        ..Default::default()
+    });
+
+    println!("creating VM");
+    let vm = VmInstance::create(
+        service.as_ref(),
+        &vm_config,
+        // console_in, console_out, and log will be redirected to the kernel log by virtmgr
+        None, // console_in
+        None, // console_out
+        None, // log
+        None, // dump_dt
+        None, // callback
+    )
+    .context("Failed to create VM")?;
+    vm.start().context("Failed to start VM")?;
+
+    println!("started trusty_security_vm_launcher VM");
+    let death_reason = vm.wait_for_death();
+    eprintln!("trusty_security_vm_launcher ended: {:?}", death_reason);
+
+    // TODO(b/331320802): we may want to use android logger instead of stdio_to_kmsg?
+
+    Ok(())
+}
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
index de1b081..2bcb40b 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.SharedPath;
 import android.system.virtualizationservice.UsbConfig;
 import android.system.virtualizationservice.VirtualMachineAppConfig;
 import android.system.virtualizationservice.VirtualMachinePayloadConfig;
@@ -712,6 +713,15 @@
             config.disks[i].partitions = partitions.toArray(new Partition[0]);
         }
 
+        config.sharedPaths =
+                new SharedPath
+                        [Optional.ofNullable(customImageConfig.getSharedPaths())
+                                .map(arr -> arr.length)
+                                .orElse(0)];
+        for (int i = 0; i < config.sharedPaths.length; i++) {
+            config.sharedPaths[i] = customImageConfig.getSharedPaths()[i].toParcelable();
+        }
+
         config.displayConfig =
                 Optional.ofNullable(customImageConfig.getDisplayConfig())
                         .map(dc -> dc.toParcelable())
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
index 9774585..9b0709d 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
+++ b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
@@ -54,6 +54,7 @@
     @Nullable private final String bootloaderPath;
     @Nullable private final String[] params;
     @Nullable private final Disk[] disks;
+    @Nullable private final SharedPath[] sharedPaths;
     @Nullable private final DisplayConfig displayConfig;
     @Nullable private final AudioConfig audioConfig;
     private final boolean touch;
@@ -96,6 +97,11 @@
         return params;
     }
 
+    @Nullable
+    public SharedPath[] getSharedPaths() {
+        return sharedPaths;
+    }
+
     public boolean useTouch() {
         return touch;
     }
@@ -132,6 +138,7 @@
             String bootloaderPath,
             String[] params,
             Disk[] disks,
+            SharedPath[] sharedPaths,
             DisplayConfig displayConfig,
             boolean touch,
             boolean keyboard,
@@ -149,6 +156,7 @@
         this.bootloaderPath = bootloaderPath;
         this.params = params;
         this.disks = disks;
+        this.sharedPaths = sharedPaths;
         this.displayConfig = displayConfig;
         this.touch = touch;
         this.keyboard = keyboard;
@@ -300,6 +308,91 @@
     }
 
     /** @hide */
+    public static final class SharedPath {
+        private final String path;
+        private final int hostUid;
+        private final int hostGid;
+        private final int guestUid;
+        private final int guestGid;
+        private final int mask;
+        private final String tag;
+        private final String socket;
+
+        public SharedPath(
+                String path,
+                int hostUid,
+                int hostGid,
+                int guestUid,
+                int guestGid,
+                int mask,
+                String tag,
+                String socket) {
+            this.path = path;
+            this.hostUid = hostUid;
+            this.hostGid = hostGid;
+            this.guestUid = guestUid;
+            this.guestGid = guestGid;
+            this.mask = mask;
+            this.tag = tag;
+            this.socket = socket;
+        }
+
+        android.system.virtualizationservice.SharedPath toParcelable() {
+            android.system.virtualizationservice.SharedPath parcelable =
+                    new android.system.virtualizationservice.SharedPath();
+            parcelable.sharedPath = this.path;
+            parcelable.hostUid = this.hostUid;
+            parcelable.hostGid = this.hostGid;
+            parcelable.guestUid = this.guestUid;
+            parcelable.guestGid = this.guestGid;
+            parcelable.mask = this.mask;
+            parcelable.tag = this.tag;
+            parcelable.socket = this.socket;
+            return parcelable;
+        }
+
+        /** @hide */
+        public String getSharedPath() {
+            return path;
+        }
+
+        /** @hide */
+        public int getHostUid() {
+            return hostUid;
+        }
+
+        /** @hide */
+        public int getHostGid() {
+            return hostGid;
+        }
+
+        /** @hide */
+        public int getGuestUid() {
+            return guestUid;
+        }
+
+        /** @hide */
+        public int getGuestGid() {
+            return guestGid;
+        }
+
+        /** @hide */
+        public int getMask() {
+            return mask;
+        }
+
+        /** @hide */
+        public String getTag() {
+            return tag;
+        }
+
+        /** @hide */
+        public String getSocket() {
+            return socket;
+        }
+    }
+
+    /** @hide */
     public static final class Disk {
         private final boolean writable;
         private final String imagePath;
@@ -366,6 +459,7 @@
         private String bootloaderPath;
         private List<String> params = new ArrayList<>();
         private List<Disk> disks = new ArrayList<>();
+        private List<SharedPath> sharedPaths = new ArrayList<>();
         private AudioConfig audioConfig;
         private DisplayConfig displayConfig;
         private boolean touch;
@@ -413,6 +507,12 @@
         }
 
         /** @hide */
+        public Builder addSharedPath(SharedPath path) {
+            this.sharedPaths.add(path);
+            return this;
+        }
+
+        /** @hide */
         public Builder addParam(String param) {
             this.params.add(param);
             return this;
@@ -493,6 +593,7 @@
                     this.bootloaderPath,
                     this.params.toArray(new String[0]),
                     this.disks.toArray(new Disk[0]),
+                    this.sharedPaths.toArray(new SharedPath[0]),
                     displayConfig,
                     touch,
                     keyboard,
diff --git a/libs/libservice_vm_fake_chain/Android.bp b/libs/libservice_vm_fake_chain/Android.bp
index 39f36eb..56fb22a 100644
--- a/libs/libservice_vm_fake_chain/Android.bp
+++ b/libs/libservice_vm_fake_chain/Android.bp
@@ -18,6 +18,7 @@
 
 rust_defaults {
     name: "libservice_vm_fake_chain_defaults",
+    compile_multilib: "first",
     crate_name: "service_vm_fake_chain",
     defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
diff --git a/libs/libservice_vm_requests/Android.bp b/libs/libservice_vm_requests/Android.bp
index 57da012..d87b087 100644
--- a/libs/libservice_vm_requests/Android.bp
+++ b/libs/libservice_vm_requests/Android.bp
@@ -4,6 +4,7 @@
 
 rust_defaults {
     name: "libservice_vm_requests_nostd_defaults",
+    compile_multilib: "first",
     crate_name: "service_vm_requests",
     defaults: ["avf_build_flags_rust"],
     srcs: ["src/lib.rs"],
diff --git a/libs/vbmeta/Android.bp b/libs/vbmeta/Android.bp
index 9a7375d..15c7b4a 100644
--- a/libs/vbmeta/Android.bp
+++ b/libs/vbmeta/Android.bp
@@ -31,7 +31,7 @@
         "libanyhow",
         "libtempfile",
     ],
-    data: [
+    device_common_data: [
         ":avb_testkey_rsa2048",
         ":avb_testkey_rsa4096",
         ":avb_testkey_rsa8192",
diff --git a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
index 6d39b46..5d6b13f 100644
--- a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
@@ -17,6 +17,7 @@
 package com.android.virtualization.vmlauncher;
 
 import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Rect;
 import android.system.virtualmachine.VirtualMachineConfig;
 import android.system.virtualmachine.VirtualMachineCustomImageConfig;
@@ -25,6 +26,7 @@
 import android.system.virtualmachine.VirtualMachineCustomImageConfig.DisplayConfig;
 import android.system.virtualmachine.VirtualMachineCustomImageConfig.GpuConfig;
 import android.system.virtualmachine.VirtualMachineCustomImageConfig.Partition;
+import android.system.virtualmachine.VirtualMachineCustomImageConfig.SharedPath;
 import android.util.DisplayMetrics;
 import android.view.WindowManager;
 import android.view.WindowMetrics;
@@ -34,6 +36,7 @@
 
 import java.io.FileReader;
 import java.util.Arrays;
+import java.util.Objects;
 
 /** This class and its inner classes model vm_config.json. */
 class ConfigJson {
@@ -60,6 +63,7 @@
     private InputJson input;
     private AudioJson audio;
     private DiskJson[] disks;
+    private SharedPathJson[] sharedPath;
     private DisplayJson display;
     private GpuJson gpu;
 
@@ -141,9 +145,36 @@
             Arrays.stream(disks).map(d -> d.toConfig()).forEach(builder::addDisk);
         }
 
+        if (sharedPath != null) {
+            Arrays.stream(sharedPath)
+                    .map(d -> d.toConfig(context))
+                    .filter(Objects::nonNull)
+                    .forEach(builder::addSharedPath);
+        }
         return builder.build();
     }
 
+    private static class SharedPathJson {
+        private SharedPathJson() {}
+
+        // Package ID of Terminal app.
+        private static final String TERMINAL_PACKAGE_ID =
+                "com.google.android.virtualization.terminal";
+        private String sharedPath;
+
+        private SharedPath toConfig(Context context) {
+            try {
+                int uid =
+                        context.getPackageManager()
+                                .getPackageUidAsUser(TERMINAL_PACKAGE_ID, context.getUserId());
+
+                return new SharedPath(sharedPath, uid, uid, 0, 0, 0007, "android", "android");
+            } catch (NameNotFoundException e) {
+                return null;
+            }
+        }
+    }
+
     private static class InputJson {
         private InputJson() {}
 
diff --git a/tests/authfs/benchmarks/Android.bp b/tests/authfs/benchmarks/Android.bp
index 27a6af1..aad8d59 100644
--- a/tests/authfs/benchmarks/Android.bp
+++ b/tests/authfs/benchmarks/Android.bp
@@ -19,7 +19,7 @@
         "open_then_run",
     ],
     per_testcase_directory: true,
-    data: [
+    device_common_data: [
         ":authfs_test_files",
         ":MicrodroidTestApp",
     ],
@@ -43,7 +43,7 @@
 java_genrule {
     name: "measure_io_as_jar",
     out: ["measure_io.jar"],
-    srcs: [
+    device_first_srcs: [
         ":measure_io",
     ],
     tools: ["soong_zip"],
diff --git a/tests/authfs/hosttests/Android.bp b/tests/authfs/hosttests/Android.bp
index 83ef853..50dbc05 100644
--- a/tests/authfs/hosttests/Android.bp
+++ b/tests/authfs/hosttests/Android.bp
@@ -20,6 +20,8 @@
     per_testcase_directory: true,
     data: [
         ":authfs_test_files",
+    ],
+    device_common_data: [
         ":MicrodroidTestApp",
     ],
 }
diff --git a/tests/benchmark_hostside/Android.bp b/tests/benchmark_hostside/Android.bp
index b613a8a..e91ac8f 100644
--- a/tests/benchmark_hostside/Android.bp
+++ b/tests/benchmark_hostside/Android.bp
@@ -18,7 +18,7 @@
     test_suites: [
         "general-tests",
     ],
-    data: [
+    device_common_data: [
         ":MicrodroidTestApp",
     ],
 }
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index d0838a6..0f2fe58 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -20,7 +20,7 @@
         "microdroid_payload_metadata",
     ],
     per_testcase_directory: true,
-    data: [
+    device_common_data: [
         ":MicrodroidTestApp",
         ":MicrodroidTestAppUpdated",
         ":microdroid_general_sepolicy.conf",
diff --git a/tests/pvmfw/Android.bp b/tests/pvmfw/Android.bp
index 0483066..e124e55 100644
--- a/tests/pvmfw/Android.bp
+++ b/tests/pvmfw/Android.bp
@@ -45,13 +45,17 @@
     ],
     per_testcase_directory: true,
     data: [
+        "assets/bcc.dat",
+    ],
+    device_common_data: [
         ":MicrodroidTestApp",
-        ":pvmfw_test",
         ":test_avf_debug_policy_with_ramdump",
         ":test_avf_debug_policy_without_ramdump",
         ":test_avf_debug_policy_with_adb",
         ":test_avf_debug_policy_without_adb",
-        "assets/bcc.dat",
+    ],
+    device_first_data: [
+        ":pvmfw_test",
     ],
     data_device_bins_first: ["dtc_static"],
 }
diff --git a/tests/vendor_images/Android.bp b/tests/vendor_images/Android.bp
index 66f0219..0430eaa 100644
--- a/tests/vendor_images/Android.bp
+++ b/tests/vendor_images/Android.bp
@@ -2,19 +2,13 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-prebuilt_etc {
-    name: "vendor_sign_key",
-    src: ":avb_testkey_rsa4096",
-    installable: false,
-}
-
 android_filesystem {
     name: "test_microdroid_vendor_image",
     partition_name: "microdroid-vendor",
     type: "ext4",
     file_contexts: ":microdroid_vendor_file_contexts.gen",
     use_avb: true,
-    avb_private_key: ":vendor_sign_key",
+    avb_private_key: ":avb_testkey_rsa4096",
     rollback_index: 5,
 }
 
@@ -24,7 +18,7 @@
     type: "ext4",
     file_contexts: ":microdroid_vendor_file_contexts.gen",
     use_avb: true,
-    avb_private_key: ":vendor_sign_key",
+    avb_private_key: ":avb_testkey_rsa4096",
 }
 
 android_filesystem {