Merge "VmTerminalApp: Restricted button texts length" into main
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
index 2ce0cdc..b785416 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.java
@@ -54,7 +54,6 @@
 public class InstallerService extends Service {
     private static final String TAG = "InstallerService";
 
-    private static final String NOTIFICATION_CHANNEL_ID = "installer";
     private static final int NOTIFICATION_ID = 1313; // any unique number among notifications
 
     private static final String IMAGE_URL =
@@ -81,23 +80,12 @@
     public void onCreate() {
         super.onCreate();
 
-        // Create mandatory notification
-        NotificationManager manager = getSystemService(NotificationManager.class);
-        if (manager.getNotificationChannel(NOTIFICATION_CHANNEL_ID) == null) {
-            NotificationChannel channel =
-                    new NotificationChannel(
-                            NOTIFICATION_CHANNEL_ID,
-                            getString(R.string.installer_notif_title_text),
-                            NotificationManager.IMPORTANCE_DEFAULT);
-            manager.createNotificationChannel(channel);
-        }
-
         Intent intent = new Intent(this, MainActivity.class);
         PendingIntent pendingIntent =
                 PendingIntent.getActivity(
                         this, /* requestCode= */ 0, intent, PendingIntent.FLAG_IMMUTABLE);
         mNotification =
-                new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
+                new Notification.Builder(this, this.getPackageName())
                         .setSmallIcon(R.drawable.ic_launcher_foreground)
                         .setContentTitle(getString(R.string.installer_notif_title_text))
                         .setContentText(getString(R.string.installer_notif_desc_text))
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index fdf1f3b..10451ec 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -95,15 +95,18 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        boolean launchInstaller = installIfNecessary();
-
         NotificationManager notificationManager = getSystemService(NotificationManager.class);
-        if (notificationManager.getNotificationChannel(TAG) == null) {
-            NotificationChannel notificationChannel =
-                    new NotificationChannel(TAG, TAG, NotificationManager.IMPORTANCE_LOW);
-            notificationManager.createNotificationChannel(notificationChannel);
+        if (notificationManager.getNotificationChannel(this.getPackageName()) == null) {
+            NotificationChannel channel =
+                    new NotificationChannel(
+                            this.getPackageName(),
+                            getString(R.string.app_name),
+                            NotificationManager.IMPORTANCE_DEFAULT);
+            notificationManager.createNotificationChannel(channel);
         }
 
+        boolean launchInstaller = installIfNecessary();
+
         setContentView(R.layout.activity_headless);
         diskSizeStep = getResources().getInteger(
                 R.integer.disk_size_round_up_step_size_in_mb) << 20;
@@ -471,8 +474,7 @@
                         PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
         Icon icon = Icon.createWithResource(getResources(), R.drawable.ic_launcher_foreground);
         Notification notification =
-                new Notification.Builder(this, TAG)
-                        .setChannelId(TAG)
+                new Notification.Builder(this, this.getPackageName())
                         .setSmallIcon(R.drawable.ic_launcher_foreground)
                         .setContentTitle(
                                 getResources().getString(R.string.service_notification_title))
@@ -540,9 +542,8 @@
             SharedPreferences.Editor editor = sharedPref.edit();
 
             long currentDiskSize = getFilesystemSize(file);
-            // The default partition size is 6G
             long newSizeInBytes = sharedPref.getLong(getString(R.string.preference_disk_size_key),
-                    6L << 30);
+                    roundUpDiskSize(currentDiskSize));
             editor.putLong(getString(R.string.preference_disk_size_key), newSizeInBytes);
             editor.apply();
 
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index 4fb8be4..1cae344 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -21,7 +21,7 @@
 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};
-use crate::selinux::{getfilecon, SeContext};
+use crate::selinux::{check_tee_service_permission, getfilecon, getprevcon, SeContext};
 use android_os_permissions_aidl::aidl::android::os::IPermissionController;
 use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::{
     Certificate::Certificate,
@@ -502,6 +502,9 @@
             check_config_allowed_for_early_vms(config)?;
         }
 
+        let caller_secontext = getprevcon().or_service_specific_exception(-1)?;
+        info!("callers secontext: {}", caller_secontext);
+
         // Allocating VM context checks the MANAGE_VIRTUAL_MACHINE permission.
         let (vm_context, cid, temporary_directory) = if cfg!(early) {
             self.create_early_vm_context(config)?
@@ -564,6 +567,12 @@
         let config = config.as_ref();
         *is_protected = config.protectedVm;
 
+        if !config.teeServices.is_empty() {
+            check_tee_service_permission(&caller_secontext, &config.teeServices)
+                .with_log()
+                .or_binder_exception(ExceptionCode::SECURITY)?;
+        }
+
         // Check if partition images are labeled incorrectly. This is to prevent random images
         // which are not protected by the Android Verified Boot (e.g. bits downloaded by apps) from
         // being loaded in a pVM. This applies to everything but the instance image in the raw
@@ -1158,6 +1167,8 @@
         for param in custom_config.extraKernelCmdlineParams.iter() {
             append_kernel_param(param, &mut vm_config);
         }
+
+        vm_config.teeServices.clone_from(&custom_config.teeServices);
     }
 
     // Unfortunately specifying page_shift = 14 in bootconfig doesn't enable 16k pages emulation,
@@ -1775,6 +1786,26 @@
     Ok(())
 }
 
+fn check_no_tee_services(config: &VirtualMachineConfig) -> binder::Result<()> {
+    match config {
+        VirtualMachineConfig::RawConfig(config) => {
+            if !config.teeServices.is_empty() {
+                return Err(anyhow!("tee_services_allowlist feature is disabled"))
+                    .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
+            }
+        }
+        VirtualMachineConfig::AppConfig(config) => {
+            if let Some(custom_config) = &config.customConfig {
+                if !custom_config.teeServices.is_empty() {
+                    return Err(anyhow!("tee_services_allowlist feature is disabled"))
+                        .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
+                }
+            }
+        }
+    };
+    Ok(())
+}
+
 fn check_protected_vm_is_supported() -> binder::Result<()> {
     let is_pvm_supported =
         hypervisor_props::is_protected_vm_supported().or_service_specific_exception(-1)?;
@@ -1799,6 +1830,9 @@
     if !cfg!(debuggable_vms_improvements) {
         check_no_extra_kernel_cmdline_params(config)?;
     }
+    if !cfg!(tee_services_allowlist) {
+        check_no_tee_services(config)?;
+    }
     Ok(())
 }
 
diff --git a/android/virtmgr/src/selinux.rs b/android/virtmgr/src/selinux.rs
index ba62b7f..719c9a9 100644
--- a/android/virtmgr/src/selinux.rs
+++ b/android/virtmgr/src/selinux.rs
@@ -15,13 +15,30 @@
 //! Wrapper to libselinux
 
 use anyhow::{anyhow, bail, Context, Result};
-use std::ffi::{CStr, CString};
+use std::ffi::{c_int, CStr, CString};
 use std::fmt;
 use std::io;
 use std::ops::Deref;
 use std::os::fd::AsRawFd;
 use std::os::raw::c_char;
 use std::ptr;
+use std::sync;
+
+static SELINUX_LOG_INIT: sync::Once = sync::Once::new();
+
+fn redirect_selinux_logs_to_logcat() {
+    let cb =
+        selinux_bindgen::selinux_callback { func_log: Some(selinux_bindgen::selinux_log_callback) };
+    // SAFETY: `selinux_set_callback` assigns the static lifetime function pointer
+    // `selinux_log_callback` to a static lifetime variable.
+    unsafe {
+        selinux_bindgen::selinux_set_callback(selinux_bindgen::SELINUX_CB_LOG as c_int, cb);
+    }
+}
+
+fn init_logger_once() {
+    SELINUX_LOG_INIT.call_once(redirect_selinux_logs_to_logcat)
+}
 
 // Partially copied from system/security/keystore2/selinux/src/lib.rs
 /// SeContext represents an SELinux context string. It can take ownership of a raw
@@ -101,6 +118,56 @@
     }
 }
 
+/// Takes ownership of context handle returned by `selinux_android_tee_service_context_handle`
+/// and closes it via `selabel_close` when dropped.
+struct TeeServiceSelinuxBackend {
+    handle: *mut selinux_bindgen::selabel_handle,
+}
+
+impl TeeServiceSelinuxBackend {
+    const BACKEND_ID: i32 = selinux_bindgen::SELABEL_CTX_ANDROID_SERVICE as i32;
+
+    /// Creates a new instance representing selinux context handle returned from
+    /// `selinux_android_tee_service_context_handle`.
+    fn new() -> Result<Self> {
+        // SAFETY: selinux_android_tee_service_context_handle is always safe to call. The returned
+        // handle is valid until `selabel_close` is called on it (see the safety comment on the drop
+        // trait).
+        let handle = unsafe { selinux_bindgen::selinux_android_tee_service_context_handle() };
+        if handle.is_null() {
+            Err(anyhow!("selinux_android_tee_service_context_handle returned a NULL context"))
+        } else {
+            Ok(TeeServiceSelinuxBackend { handle })
+        }
+    }
+
+    fn lookup(&self, tee_service: &str) -> Result<SeContext> {
+        let mut con: *mut c_char = ptr::null_mut();
+        let c_key = CString::new(tee_service).context("failed to convert to CString")?;
+        // SAFETY: the returned pointer `con` is valid until `freecon` is called on it.
+        match unsafe {
+            selinux_bindgen::selabel_lookup(self.handle, &mut con, c_key.as_ptr(), Self::BACKEND_ID)
+        } {
+            0 => {
+                if !con.is_null() {
+                    Ok(SeContext::Raw(con))
+                } else {
+                    Err(anyhow!("selabel_lookup returned a NULL context"))
+                }
+            }
+            _ => Err(anyhow!(io::Error::last_os_error())).context("selabel_lookup failed"),
+        }
+    }
+}
+
+impl Drop for TeeServiceSelinuxBackend {
+    fn drop(&mut self) {
+        // SAFETY: the TeeServiceSelinuxBackend is created only with a pointer is set by
+        // libselinux and has to be freed with `selabel_close`.
+        unsafe { selinux_bindgen::selabel_close(self.handle) };
+    }
+}
+
 pub fn getfilecon<F: AsRawFd>(file: &F) -> Result<SeContext> {
     let fd = file.as_raw_fd();
     let mut con: *mut c_char = ptr::null_mut();
@@ -117,3 +184,92 @@
         _ => Err(anyhow!(io::Error::last_os_error())).context("fgetfilecon failed"),
     }
 }
+
+pub fn getprevcon() -> Result<SeContext> {
+    let mut con: *mut c_char = ptr::null_mut();
+    // SAFETY: the returned pointer `con` is wrapped in SeContext::Raw which is freed with
+    // `freecon` when it is dropped.
+    match unsafe { selinux_bindgen::getprevcon(&mut con) } {
+        0.. => {
+            if !con.is_null() {
+                Ok(SeContext::Raw(con))
+            } else {
+                Err(anyhow!("getprevcon returned a NULL context"))
+            }
+        }
+        _ => Err(anyhow!(io::Error::last_os_error())).context("getprevcon failed"),
+    }
+}
+
+// Wrapper around selinux_check_access
+fn check_access(source: &CStr, target: &CStr, tclass: &str, perm: &str) -> Result<()> {
+    let c_tclass = CString::new(tclass).context("failed to convert tclass to CString")?;
+    let c_perm = CString::new(perm).context("failed to convert perm to CString")?;
+
+    // SAFETY: lifecycle of pointers passed to the selinux_check_access outlive the duration of the
+    // call.
+    match unsafe {
+        selinux_bindgen::selinux_check_access(
+            source.as_ptr(),
+            target.as_ptr(),
+            c_tclass.as_ptr(),
+            c_perm.as_ptr(),
+            ptr::null_mut(),
+        )
+    } {
+        0 => Ok(()),
+        _ => Err(anyhow!(io::Error::last_os_error())).with_context(|| {
+            format!(
+                "check_access: Failed with sctx: {:?} tctx: {:?} tclass: {:?} perm {:?}",
+                source, target, tclass, perm
+            )
+        }),
+    }
+}
+
+pub fn check_tee_service_permission(caller_ctx: &SeContext, tee_services: &[String]) -> Result<()> {
+    init_logger_once();
+
+    let backend = TeeServiceSelinuxBackend::new()?;
+
+    for tee_service in tee_services {
+        let tee_service_ctx = backend.lookup(tee_service)?;
+        check_access(caller_ctx, &tee_service_ctx, "tee_service", "use")
+            .with_context(|| format!("permission denied for {:?}", tee_service))?;
+    }
+
+    Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    #[ignore = "disabling test while investigating b/379087641"]
+    fn test_check_tee_service_permission_has_permission() -> Result<()> {
+        if cfg!(not(tee_services_allowlist)) {
+            // Skip test on release configurations without tee_services_allowlist feature enabled.
+            return Ok(());
+        }
+
+        let caller_ctx = SeContext::new("u:r:shell:s0")?;
+        let tee_services = [String::from("test_pkvm_tee_service")];
+        check_tee_service_permission(&caller_ctx, &tee_services)
+    }
+
+    #[test]
+    #[ignore = "disabling test while investigating b/379087641"]
+    fn test_check_tee_service_permission_invalid_tee_service() -> Result<()> {
+        if cfg!(not(tee_services_allowlist)) {
+            // Skip test on release configurations without tee_services_allowlist feature enabled.
+            return Ok(());
+        }
+
+        let caller_ctx = SeContext::new("u:r:shell:s0")?;
+        let tee_services = [String::from("test_tee_service_does_not_exist")];
+        let ret = check_tee_service_permission(&caller_ctx, &tee_services);
+        assert!(ret.is_err());
+        Ok(())
+    }
+}
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index 9123742..114a851 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -130,6 +130,9 @@
 
         /** Additional parameters to pass to the VM's kernel cmdline. */
         String[] extraKernelCmdlineParams;
+
+        /** List of tee services this VM wants to access */
+        String[] teeServices;
     }
 
     /** Configuration parameters guarded by android.permission.USE_CUSTOM_VIRTUAL_MACHINE */
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index 9f2a23e..5728a68 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -110,4 +110,7 @@
 
     /** Enable or disable USB passthrough support */
     @nullable UsbConfig usbConfig;
+
+    /** List of tee services this VM wants to access */
+    String[] teeServices;
 }
diff --git a/android/vm/src/main.rs b/android/vm/src/main.rs
index 110e0ca..7bfd957 100644
--- a/android/vm/src/main.rs
+++ b/android/vm/src/main.rs
@@ -72,6 +72,11 @@
     /// Boost uclamp to stablise results for benchmarks.
     #[arg(short, long)]
     boost_uclamp: bool,
+
+    /// Secure services this VM wants to access.
+    #[cfg(tee_services_allowlist)]
+    #[arg(long)]
+    tee_services: Vec<String>,
 }
 
 impl CommonConfig {
@@ -84,6 +89,16 @@
             }
         }
     }
+
+    fn tee_services(&self) -> &[String] {
+        cfg_if::cfg_if! {
+            if #[cfg(tee_services_allowlist)] {
+                &self.tee_services
+            } else {
+                &[]
+            }
+        }
+    }
 }
 
 #[derive(Args, Default)]
diff --git a/android/vm/src/run.rs b/android/vm/src/run.rs
index b07a472..2157ea8 100644
--- a/android/vm/src/run.rs
+++ b/android/vm/src/run.rs
@@ -156,6 +156,7 @@
             })
             .collect::<Result<_, _>>()?,
         networkSupported: config.common.network_supported(),
+        teeServices: config.common.tee_services().to_vec(),
         ..Default::default()
     };
 
@@ -263,8 +264,8 @@
     if let Some(mem) = config.common.mem {
         vm_config.memoryMib = mem as i32;
     }
-    if let Some(name) = config.common.name {
-        vm_config.name = name;
+    if let Some(ref name) = config.common.name {
+        vm_config.name = name.to_string();
     } else {
         vm_config.name = String::from("VmRun");
     }
@@ -274,6 +275,7 @@
     vm_config.cpuTopology = config.common.cpu_topology;
     vm_config.hugePages = config.common.hugepages;
     vm_config.boostUclamp = config.common.boost_uclamp;
+    vm_config.teeServices = config.common.tee_services().to_vec();
     run(
         get_service()?.as_ref(),
         &VirtualMachineConfig::RawConfig(vm_config),
diff --git a/build/Android.bp b/build/Android.bp
index 4c4fbba..2b97927 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -41,6 +41,9 @@
     }) + select(release_flag("RELEASE_AVF_ENABLE_VENDOR_MODULES"), {
         true: ["vendor_modules"],
         default: [],
+    }) + select(release_flag("RELEASE_AVF_ENABLE_VM_TO_TEE_SERVICES_ALLOWLIST"), {
+        true: ["tee_services_allowlist"],
+        default: [],
     }) + select(release_flag("RELEASE_AVF_ENABLE_VIRT_CPUFREQ"), {
         true: ["virt_cpufreq"],
         default: [],
diff --git a/build/debian/fai_config/package_config/AVF b/build/debian/fai_config/package_config/AVF
index 1be57fe..2e55e90 100644
--- a/build/debian/fai_config/package_config/AVF
+++ b/build/debian/fai_config/package_config/AVF
@@ -1,3 +1,5 @@
 PACKAGES install
 
+bpfcc-tools
+linux-headers-generic
 procps
diff --git a/guest/forwarder_guest_launcher/Cargo.toml b/guest/forwarder_guest_launcher/Cargo.toml
index 03fda56..c875484 100644
--- a/guest/forwarder_guest_launcher/Cargo.toml
+++ b/guest/forwarder_guest_launcher/Cargo.toml
@@ -7,9 +7,13 @@
 [dependencies]
 anyhow = "1.0.91"
 clap = { version = "4.5.20", features = ["derive"] }
+csv-async = { version = "1.3.0", features = ["tokio"] }
 env_logger = "0.11.5"
+futures = "0.3.31"
+listeners = "0.2.1"
 log = "0.4.22"
 prost = "0.13.3"
+serde = { version = "1.0.215", features = ["derive"] }
 tokio = { version = "1.40.0", features = ["process", "rt-multi-thread"] }
 tonic = "0.12.3"
 vsock = "0.5.1"
diff --git a/guest/forwarder_guest_launcher/src/main.rs b/guest/forwarder_guest_launcher/src/main.rs
index abb39f6..fd1ffd9 100644
--- a/guest/forwarder_guest_launcher/src/main.rs
+++ b/guest/forwarder_guest_launcher/src/main.rs
@@ -14,19 +14,39 @@
 
 //! Launcher of forwarder_guest
 
-use anyhow::Context;
+use anyhow::{anyhow, Context};
 use clap::Parser;
+use csv_async::AsyncReader;
 use debian_service::debian_service_client::DebianServiceClient;
-use debian_service::QueueOpeningRequest;
-use log::debug;
+use debian_service::{QueueOpeningRequest, ReportVmActivePortsRequest};
+use futures::stream::StreamExt;
+use log::{debug, error};
+use serde::Deserialize;
+use std::collections::HashSet;
+use std::process::Stdio;
+use tokio::io::BufReader;
 use tokio::process::Command;
-use tonic::transport::Endpoint;
+use tokio::try_join;
+use tonic::transport::{Channel, Endpoint};
 use tonic::Request;
 
 mod debian_service {
     tonic::include_proto!("com.android.virtualization.vmlauncher.proto");
 }
 
+const NON_PREVILEGED_PORT_RANGE_START: i32 = 1024;
+const TCPSTATES_IP_4: i8 = 4;
+const TCPSTATES_STATE_LISTEN: &str = "LISTEN";
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "UPPERCASE")]
+struct TcpStateRow {
+    ip: i8,
+    lport: i32,
+    oldstate: String,
+    newstate: String,
+}
+
 #[derive(Parser)]
 /// Flags for running command
 pub struct Args {
@@ -40,15 +60,9 @@
     grpc_port: String,
 }
 
-#[tokio::main]
-async fn main() -> Result<(), Box<dyn std::error::Error>> {
-    env_logger::init();
-    debug!("Starting forwarder_guest_launcher");
-    let args = Args::parse();
-    let addr = format!("https://{}:{}", args.host_addr, args.grpc_port);
-
-    let channel = Endpoint::from_shared(addr)?.connect().await?;
-    let mut client = DebianServiceClient::new(channel);
+async fn process_forwarding_request_queue(
+    mut client: DebianServiceClient<Channel>,
+) -> Result<(), Box<dyn std::error::Error>> {
     let cid = vsock::get_local_cid().context("Failed to get CID of VM")?;
     let mut res_stream = client
         .open_forwarding_request_queue(Request::new(QueueOpeningRequest { cid: cid as i32 }))
@@ -72,5 +86,82 @@
             .arg(format!("vsock:2:{}", vsock_port))
             .spawn();
     }
+    Err(anyhow!("process_forwarding_request_queue is terminated").into())
+}
+
+async fn send_active_ports_report(
+    listening_ports: HashSet<i32>,
+    client: &mut DebianServiceClient<Channel>,
+) -> Result<(), Box<dyn std::error::Error>> {
+    let res = client
+        .report_vm_active_ports(Request::new(ReportVmActivePortsRequest {
+            ports: listening_ports.into_iter().collect(),
+        }))
+        .await?
+        .into_inner();
+    if res.success {
+        debug!("Successfully reported active ports to the host");
+    } else {
+        error!("Failure response received from the host for reporting active ports");
+    }
+    Ok(())
+}
+
+async fn report_active_ports(
+    mut client: DebianServiceClient<Channel>,
+) -> Result<(), Box<dyn std::error::Error>> {
+    let mut cmd = Command::new("python3")
+        .arg("-u")
+        .arg("/usr/sbin/tcpstates-bpfcc")
+        .arg("-s")
+        .stdout(Stdio::piped())
+        .spawn()?;
+    let stdout = cmd.stdout.take().context("Failed to get stdout of tcpstates")?;
+    let mut csv_reader = AsyncReader::from_reader(BufReader::new(stdout));
+    let header = csv_reader.headers().await?.clone();
+
+    // TODO(b/340126051): Consider using NETLINK_SOCK_DIAG for the optimization.
+    let listeners = listeners::get_all()?;
+    // TODO(b/340126051): Support distinguished port forwarding for ipv6 as well.
+    let mut listening_ports: HashSet<_> = listeners
+        .iter()
+        .map(|x| x.socket)
+        .filter(|x| x.is_ipv4())
+        .map(|x| x.port().into())
+        .filter(|x| *x >= NON_PREVILEGED_PORT_RANGE_START) // Ignore privileged ports
+        .collect();
+    send_active_ports_report(listening_ports.clone(), &mut client).await?;
+
+    let mut records = csv_reader.records();
+    while let Some(record) = records.next().await {
+        let row: TcpStateRow = record?.deserialize(Some(&header))?;
+        if row.ip != TCPSTATES_IP_4 {
+            continue;
+        }
+        match (row.oldstate.as_str(), row.newstate.as_str()) {
+            (_, TCPSTATES_STATE_LISTEN) => {
+                listening_ports.insert(row.lport);
+            }
+            (TCPSTATES_STATE_LISTEN, _) => {
+                listening_ports.remove(&row.lport);
+            }
+            (_, _) => continue,
+        }
+        send_active_ports_report(listening_ports.clone(), &mut client).await?;
+    }
+
+    Err(anyhow!("report_active_ports is terminated").into())
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    env_logger::init();
+    debug!("Starting forwarder_guest_launcher");
+    let args = Args::parse();
+    let addr = format!("https://{}:{}", args.host_addr, args.grpc_port);
+    let channel = Endpoint::from_shared(addr)?.connect().await?;
+    let client = DebianServiceClient::new(channel);
+
+    try_join!(process_forwarding_request_queue(client.clone()), report_active_ports(client))?;
     Ok(())
 }
diff --git a/guest/trusty/security_vm/launcher/Android.bp b/guest/trusty/security_vm/launcher/Android.bp
index e482e02..ef32740 100644
--- a/guest/trusty/security_vm/launcher/Android.bp
+++ b/guest/trusty/security_vm/launcher/Android.bp
@@ -40,12 +40,15 @@
 // python -c "import hashlib; print(hashlib.sha256(b'trusty_security_vm_salt').hexdigest())"
 trusty_security_vm_salt = "75a71e967c1a1e0f805cca20465e7acf83e6a04e567a67c426d8b5a94f8d61c5"
 
+TRUSTY_SECURITY_VM_VERSION = 1
+
 avb_add_hash_footer {
     name: "trusty_security_vm_signed",
     filename: "trusty_security_vm_signed",
     partition_name: "boot",
     private_key: ":trusty_vm_sign_key",
     salt: trusty_security_vm_salt,
+    rollback_index: TRUSTY_SECURITY_VM_VERSION,
     src: ":empty_file",
     enabled: false,
     arch: {
diff --git a/libs/cborutil/src/lib.rs b/libs/cborutil/src/lib.rs
index b218c82..4d308c1 100644
--- a/libs/cborutil/src/lib.rs
+++ b/libs/cborutil/src/lib.rs
@@ -21,10 +21,7 @@
 use alloc::string::String;
 use alloc::vec::Vec;
 use ciborium::value::{Integer, Value};
-use coset::{
-    iana::{self, EnumI64},
-    CborSerializable, CoseError, CoseKey, Label, Result,
-};
+use coset::{CborSerializable, CoseError, CoseKey, Label, Result};
 use log::error;
 use serde::{de::DeserializeOwned, Serialize};
 
@@ -135,19 +132,3 @@
         .ok_or(CoseError::UnexpectedItem("", "Label not found in CoseKey"))?
         .1)
 }
-
-/// Converts the provided COSE key algorithm integer to an `iana::Algorithm` used
-/// by DICE chains.
-pub fn dice_cose_key_alg(cose_key_alg: i32) -> Result<iana::Algorithm> {
-    let key_alg = iana::Algorithm::from_i64(cose_key_alg as i64).ok_or_else(|| {
-        error!("Unsupported COSE key algorithm for DICE: {cose_key_alg}");
-        CoseError::UnexpectedItem("COSE key algorithm", "")
-    })?;
-    match key_alg {
-        iana::Algorithm::EdDSA | iana::Algorithm::ES256 | iana::Algorithm::ES384 => Ok(key_alg),
-        _ => {
-            error!("Unsupported COSE key algorithm for DICE: {key_alg:?}");
-            Err(CoseError::UnexpectedItem("-8, -7 or -35", ""))
-        }
-    }
-}
diff --git a/libs/dice/open_dice/Android.bp b/libs/dice/open_dice/Android.bp
index d1129fb..c60260e 100644
--- a/libs/dice/open_dice/Android.bp
+++ b/libs/dice/open_dice/Android.bp
@@ -14,13 +14,11 @@
     name: "libdiced_open_dice_nostd",
     defaults: ["libdiced_open_dice_defaults"],
     rustlibs: [
+        "libcoset_nostd",
         "libopen_dice_android_bindgen_nostd",
         "libopen_dice_cbor_bindgen_nostd",
         "libzeroize_nostd",
     ],
-    features: [
-        "alloc",
-    ],
     whole_static_libs: [
         "libcrypto_baremetal",
     ],
@@ -35,13 +33,13 @@
     host_supported: true,
     vendor_available: true,
     rustlibs: [
+        "libcoset",
         "libopen_dice_android_bindgen",
         "libopen_dice_cbor_bindgen",
         "libserde",
         "libzeroize",
     ],
     features: [
-        "alloc",
         "serde_derive",
         "std",
     ],
@@ -154,10 +152,7 @@
         "--allowlist-var=DICE_INLINE_CONFIG_SIZE",
         "--allowlist-var=DICE_PRIVATE_KEY_SEED_SIZE",
         "--allowlist-var=DICE_ID_SIZE",
-        "--allowlist-var=DICE_PUBLIC_KEY_SIZE",
         "--allowlist-var=DICE_PRIVATE_KEY_SIZE",
-        "--allowlist-var=DICE_SIGNATURE_SIZE",
-        "--allowlist-var=DICE_COSE_KEY_ALG_VALUE",
     ],
 }
 
diff --git a/libs/dice/open_dice/src/dice.rs b/libs/dice/open_dice/src/dice.rs
index e330e00..6404508 100644
--- a/libs/dice/open_dice/src/dice.rs
+++ b/libs/dice/open_dice/src/dice.rs
@@ -16,12 +16,12 @@
 //! This module mirrors the content in open-dice/include/dice/dice.h
 
 use crate::error::{check_result, Result};
+use coset::iana;
 pub use open_dice_cbor_bindgen::DiceMode;
 use open_dice_cbor_bindgen::{
     DiceConfigType, DiceDeriveCdiCertificateId, DiceDeriveCdiPrivateKeySeed, DiceInputValues,
     DiceMainFlow, DICE_CDI_SIZE, DICE_HASH_SIZE, DICE_HIDDEN_SIZE, DICE_ID_SIZE,
     DICE_INLINE_CONFIG_SIZE, DICE_PRIVATE_KEY_SEED_SIZE, DICE_PRIVATE_KEY_SIZE,
-    DICE_PUBLIC_KEY_SIZE, DICE_SIGNATURE_SIZE,
 };
 #[cfg(feature = "serde_derive")]
 use serde_derive::{Deserialize, Serialize};
@@ -40,10 +40,6 @@
 pub const PRIVATE_KEY_SEED_SIZE: usize = DICE_PRIVATE_KEY_SEED_SIZE as usize;
 /// The size of a private key.
 pub const PRIVATE_KEY_SIZE: usize = DICE_PRIVATE_KEY_SIZE as usize;
-/// The size of a public key.
-pub const PUBLIC_KEY_SIZE: usize = DICE_PUBLIC_KEY_SIZE as usize;
-/// The size of a signature.
-pub const SIGNATURE_SIZE: usize = DICE_SIGNATURE_SIZE as usize;
 /// The size of an ID.
 pub const ID_SIZE: usize = DICE_ID_SIZE as usize;
 
@@ -55,13 +51,69 @@
 pub type InlineConfig = [u8; INLINE_CONFIG_SIZE];
 /// Array type of CDIs.
 pub type Cdi = [u8; CDI_SIZE];
-/// Array type of the public key.
-pub type PublicKey = [u8; PUBLIC_KEY_SIZE];
-/// Array type of the signature.
-pub type Signature = [u8; SIGNATURE_SIZE];
 /// Array type of DICE ID.
 pub type DiceId = [u8; ID_SIZE];
 
+/// Key algorithm used for DICE.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum KeyAlgorithm {
+    /// Ed25519.
+    Ed25519,
+    /// ECDSA using P-256 curve.
+    EcdsaP256,
+    /// ECDSA using P-384 curve.
+    EcdsaP384,
+}
+
+impl From<KeyAlgorithm> for iana::Algorithm {
+    fn from(alg: KeyAlgorithm) -> Self {
+        match alg {
+            KeyAlgorithm::Ed25519 => iana::Algorithm::EdDSA,
+            KeyAlgorithm::EcdsaP256 => iana::Algorithm::ES256,
+            KeyAlgorithm::EcdsaP384 => iana::Algorithm::ES384,
+        }
+    }
+}
+
+/// Key algorithm used within different components in VMs.
+///
+/// This algorithm serves two primary purposes:
+///
+/// * **pvmfw Handover:** In pvmfw, a vendor DICE chain, potentially using various algorithms, is
+///   transitioned to this specific algorithm.
+/// * **Post-Handover Consistency:** In components following pvmfw (e.g., the Microdroid OS), this
+///   algorithm is used consistently for both the authority and subject keys in DICE derivation.
+pub const VM_KEY_ALGORITHM: KeyAlgorithm = KeyAlgorithm::Ed25519;
+
+impl KeyAlgorithm {
+    /// Returns the size of the public key.
+    pub fn public_key_size(&self) -> usize {
+        match self {
+            KeyAlgorithm::Ed25519 => 32,
+            KeyAlgorithm::EcdsaP256 => 64,
+            KeyAlgorithm::EcdsaP384 => 96,
+        }
+    }
+
+    /// Returns the size of the signature.
+    pub fn signature_size(&self) -> usize {
+        match self {
+            KeyAlgorithm::Ed25519 => 64,
+            KeyAlgorithm::EcdsaP256 => 64,
+            KeyAlgorithm::EcdsaP384 => 96,
+        }
+    }
+
+    /// Returns the size of the private key.
+    pub fn private_key_size(&self) -> usize {
+        match self {
+            KeyAlgorithm::Ed25519 => 64,
+            KeyAlgorithm::EcdsaP256 => 32,
+            KeyAlgorithm::EcdsaP384 => 48,
+        }
+    }
+}
+
 /// A trait for types that represent Dice artifacts, which include:
 ///
 /// - Attestation CDI
diff --git a/libs/dice/open_dice/src/lib.rs b/libs/dice/open_dice/src/lib.rs
index 085a2cd..a347d46 100644
--- a/libs/dice/open_dice/src/lib.rs
+++ b/libs/dice/open_dice/src/lib.rs
@@ -17,7 +17,6 @@
 
 #![cfg_attr(not(feature = "std"), no_std)]
 
-#[cfg(feature = "alloc")]
 extern crate alloc;
 
 #[cfg(not(feature = "std"))]
@@ -27,7 +26,6 @@
 mod dice;
 mod error;
 mod ops;
-#[cfg(feature = "alloc")]
 mod retry;
 
 pub use bcc::{
@@ -36,18 +34,14 @@
 };
 pub use dice::{
     derive_cdi_certificate_id, derive_cdi_private_key_seed, dice_main_flow, Cdi, CdiValues, Config,
-    DiceArtifacts, DiceMode, Hash, Hidden, InlineConfig, InputValues, PrivateKey, PrivateKeySeed,
-    PublicKey, Signature, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE, ID_SIZE, PRIVATE_KEY_SEED_SIZE,
+    DiceArtifacts, DiceMode, Hash, Hidden, InlineConfig, InputValues, KeyAlgorithm, PrivateKey,
+    PrivateKeySeed, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE, ID_SIZE, PRIVATE_KEY_SEED_SIZE,
+    VM_KEY_ALGORITHM,
 };
 pub use error::{DiceError, Result};
-// Currently, open-dice library only supports a single signing and verification algorithm.
-// The value of DICE_COSE_KEY_ALG_VALUE depends on the algorithm chosen by the underlying C
-// library at build time. Refer to b/342333212 for more information.
-pub use open_dice_cbor_bindgen::DICE_COSE_KEY_ALG_VALUE;
 pub use ops::{
     derive_cdi_leaf_priv, generate_certificate, hash, kdf, keypair_from_seed, sign, verify,
 };
-#[cfg(feature = "alloc")]
 pub use retry::{
     retry_bcc_format_config_descriptor, retry_bcc_main_flow, retry_dice_main_flow,
     retry_generate_certificate, OwnedDiceArtifacts,
diff --git a/libs/dice/open_dice/src/ops.rs b/libs/dice/open_dice/src/ops.rs
index 47b5244..137736f 100644
--- a/libs/dice/open_dice/src/ops.rs
+++ b/libs/dice/open_dice/src/ops.rs
@@ -17,10 +17,11 @@
 //! main DICE functions depend on.
 
 use crate::dice::{
-    derive_cdi_private_key_seed, DiceArtifacts, Hash, InputValues, PrivateKey, PublicKey,
-    Signature, HASH_SIZE, PRIVATE_KEY_SEED_SIZE, PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE, SIGNATURE_SIZE,
+    derive_cdi_private_key_seed, DiceArtifacts, Hash, InputValues, PrivateKey, HASH_SIZE,
+    PRIVATE_KEY_SEED_SIZE, PRIVATE_KEY_SIZE, VM_KEY_ALGORITHM,
 };
-use crate::error::{check_result, Result};
+use crate::error::{check_result, DiceError, Result};
+use alloc::{vec, vec::Vec};
 use open_dice_cbor_bindgen::{
     DiceGenerateCertificate, DiceHash, DiceKdf, DiceKeypairFromSeed, DiceSign, DiceVerify,
 };
@@ -71,8 +72,8 @@
 /// Deterministically generates a public and private key pair from `seed`.
 /// Since this is deterministic, `seed` is as sensitive as a private key and can
 /// be used directly as the private key.
-pub fn keypair_from_seed(seed: &[u8; PRIVATE_KEY_SEED_SIZE]) -> Result<(PublicKey, PrivateKey)> {
-    let mut public_key = [0u8; PUBLIC_KEY_SIZE];
+pub fn keypair_from_seed(seed: &[u8; PRIVATE_KEY_SEED_SIZE]) -> Result<(Vec<u8>, PrivateKey)> {
+    let mut public_key = vec![0u8; VM_KEY_ALGORITHM.public_key_size()];
     let mut private_key = PrivateKey::default();
     check_result(
         // SAFETY: The function writes to the `public_key` and `private_key` within the given
@@ -106,8 +107,8 @@
 }
 
 /// Signs the `message` with the give `private_key` using `DiceSign`.
-pub fn sign(message: &[u8], private_key: &[u8; PRIVATE_KEY_SIZE]) -> Result<Signature> {
-    let mut signature = [0u8; SIGNATURE_SIZE];
+pub fn sign(message: &[u8], private_key: &[u8; PRIVATE_KEY_SIZE]) -> Result<Vec<u8>> {
+    let mut signature = vec![0u8; VM_KEY_ALGORITHM.signature_size()];
     check_result(
         // SAFETY: The function writes to the `signature` within the given bounds, and only reads
         // the message and the private key. The first argument context is not used in this
@@ -127,7 +128,12 @@
 }
 
 /// Verifies the `signature` of the `message` with the given `public_key` using `DiceVerify`.
-pub fn verify(message: &[u8], signature: &Signature, public_key: &PublicKey) -> Result<()> {
+pub fn verify(message: &[u8], signature: &[u8], public_key: &[u8]) -> Result<()> {
+    if signature.len() != VM_KEY_ALGORITHM.signature_size()
+        || public_key.len() != VM_KEY_ALGORITHM.public_key_size()
+    {
+        return Err(DiceError::InvalidInput);
+    }
     check_result(
         // SAFETY: only reads the messages, signature and public key as constant values.
         // The first argument context is not used in this function.
diff --git a/libs/dice/open_dice/src/retry.rs b/libs/dice/open_dice/src/retry.rs
index d9551f3..6e75e91 100644
--- a/libs/dice/open_dice/src/retry.rs
+++ b/libs/dice/open_dice/src/retry.rs
@@ -23,7 +23,6 @@
 };
 use crate::error::{DiceError, Result};
 use crate::ops::generate_certificate;
-#[cfg(feature = "alloc")]
 use alloc::vec::Vec;
 #[cfg(feature = "serde_derive")]
 use serde_derive::{Deserialize, Serialize};
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
index 3d1964d..8230166 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -744,6 +744,7 @@
                                     return usbConfig;
                                 })
                         .orElse(null);
+        config.teeServices = EMPTY_STRING_ARRAY;
         return config;
     }
 
@@ -798,6 +799,7 @@
                     new VirtualMachineAppConfig.CustomConfig();
             customConfig.devices = EMPTY_STRING_ARRAY;
             customConfig.extraKernelCmdlineParams = EMPTY_STRING_ARRAY;
+            customConfig.teeServices = EMPTY_STRING_ARRAY;
             try {
                 customConfig.vendorImage =
                         ParcelFileDescriptor.open(mVendorDiskImage, MODE_READ_ONLY);
diff --git a/libs/libclient_vm_csr/src/lib.rs b/libs/libclient_vm_csr/src/lib.rs
index 77cfea9..e5a6036 100644
--- a/libs/libclient_vm_csr/src/lib.rs
+++ b/libs/libclient_vm_csr/src/lib.rs
@@ -20,9 +20,7 @@
     iana, CborSerializable, CoseKey, CoseKeyBuilder, CoseSign, CoseSignBuilder, CoseSignature,
     CoseSignatureBuilder, HeaderBuilder,
 };
-use diced_open_dice::{
-    derive_cdi_leaf_priv, sign, DiceArtifacts, PrivateKey, DICE_COSE_KEY_ALG_VALUE,
-};
+use diced_open_dice::{derive_cdi_leaf_priv, sign, DiceArtifacts, PrivateKey, VM_KEY_ALGORITHM};
 use openssl::{
     bn::{BigNum, BigNumContext},
     ec::{EcGroup, EcKey, EcKeyRef},
@@ -93,8 +91,7 @@
     cdi_leaf_priv: &PrivateKey,
     attestation_key: &EcKeyRef<Private>,
 ) -> Result<CoseSign> {
-    let dice_key_alg = cbor_util::dice_cose_key_alg(DICE_COSE_KEY_ALG_VALUE)?;
-    let cdi_leaf_sig_headers = build_signature_headers(dice_key_alg);
+    let cdi_leaf_sig_headers = build_signature_headers(VM_KEY_ALGORITHM.into());
     let attestation_key_sig_headers = build_signature_headers(ATTESTATION_KEY_ALGO);
     let aad = &[];
     let signed_data = CoseSignBuilder::new()
diff --git a/libs/libservice_vm_requests/src/rkp.rs b/libs/libservice_vm_requests/src/rkp.rs
index 7de7cd5..2c26b16 100644
--- a/libs/libservice_vm_requests/src/rkp.rs
+++ b/libs/libservice_vm_requests/src/rkp.rs
@@ -28,7 +28,7 @@
 use core::result;
 use coset::{AsCborValue, CoseSign1, CoseSign1Builder, HeaderBuilder};
 use diced_open_dice::{
-    derive_cdi_leaf_priv, kdf, sign, DiceArtifacts, PrivateKey, DICE_COSE_KEY_ALG_VALUE,
+    derive_cdi_leaf_priv, kdf, sign, DiceArtifacts, PrivateKey, VM_KEY_ALGORITHM,
 };
 use log::{debug, error};
 use service_vm_comm::{EcdsaP256KeyPair, GenerateCertificateRequestParams, RequestProcessingError};
@@ -152,8 +152,7 @@
         error!("Failed to derive the CDI_Leaf_Priv: {e}");
         RequestProcessingError::InternalError
     })?;
-    let dice_key_alg = cbor_util::dice_cose_key_alg(DICE_COSE_KEY_ALG_VALUE)?;
-    let protected = HeaderBuilder::new().algorithm(dice_key_alg).build();
+    let protected = HeaderBuilder::new().algorithm(VM_KEY_ALGORITHM.into()).build();
     let signed_data = CoseSign1Builder::new()
         .protected(protected)
         .payload(cbor_util::serialize(payload)?)
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index a6c79cb..b1485e3 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -83,6 +83,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.function.ThrowingRunnable;
@@ -1938,6 +1939,7 @@
     }
 
     @Test
+    @Ignore("b/372874464")
     public void outputIsNotRedirectedToLogcatIfNotDebuggable() throws Exception {
         assumeSupportedDevice();