Merge changes from topic "fh_impl" into main

* changes:
  forwarder_guest_launcher can execute forwarder_guest
  Host can send required infos to the guest for performing forwarding.
diff --git a/android/TerminalApp/Android.bp b/android/TerminalApp/Android.bp
index 84ba041..09287d8 100644
--- a/android/TerminalApp/Android.bp
+++ b/android/TerminalApp/Android.bp
@@ -17,6 +17,10 @@
         "com.google.android.material_material",
         "androidx.window_window",
     ],
+    jni_libs: [
+        "libforwarder_host_jni",
+    ],
+    use_embedded_native_libs: true,
     platform_apis: true,
     privileged: true,
     optimize: {
diff --git a/android/forwarder_host/Android.bp b/android/forwarder_host/Android.bp
index 35c478e..e8d0184 100644
--- a/android/forwarder_host/Android.bp
+++ b/android/forwarder_host/Android.bp
@@ -2,12 +2,14 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-rust_binary {
-    name: "forwarder_host",
+rust_ffi_shared {
+    name: "libforwarder_host_jni",
+    crate_name: "forwarder_host",
     edition: "2021",
-    srcs: ["src/main.rs"],
+    srcs: ["src/forwarder_host.rs"],
     rustlibs: [
         "libforwarder",
+        "libjni",
         "liblog_rust",
         "libnix",
         "libvmm_sys_util",
@@ -17,5 +19,8 @@
         "libpoll_token_derive",
         "libremain",
     ],
-    static_executable: true,
+    apex_available: [
+        "com.android.virt",
+    ],
+    prefer_rlib: true,
 }
diff --git a/android/forwarder_host/src/main.rs b/android/forwarder_host/src/forwarder_host.rs
similarity index 82%
rename from android/forwarder_host/src/main.rs
rename to android/forwarder_host/src/forwarder_host.rs
index b95b2cc..78f3555 100644
--- a/android/forwarder_host/src/main.rs
+++ b/android/forwarder_host/src/forwarder_host.rs
@@ -28,7 +28,10 @@
 use std::time::Duration;
 
 use forwarder::forwarder::ForwarderSession;
-use log::{error, warn};
+use jni::objects::{JObject, JValue};
+use jni::sys::jint;
+use jni::JNIEnv;
+use log::{debug, error, info, warn};
 use nix::sys::eventfd::EventFd;
 use poll_token_derive::PollToken;
 use vmm_sys_util::poll::{PollContext, PollToken};
@@ -45,6 +48,7 @@
     BindVsock(io::Error),
     EventFdNew(nix::Error),
     IncorrectCid(u32),
+    LaunchForwarderGuest(jni::errors::Error),
     NoListenerForPort(u16),
     NoSessionForTag(SessionTag),
     PollContextAdd(vmm_sys_util::errno::Error),
@@ -53,9 +57,12 @@
     PollWait(vmm_sys_util::errno::Error),
     SetVsockNonblocking(io::Error),
     TcpAccept(io::Error),
+    TcpListenerPort(io::Error),
     UpdateEventRead(nix::Error),
+    UpdateEventWrite(nix::Error),
     VsockAccept(io::Error),
     VsockAcceptTimeout,
+    VsockListenerPort(io::Error),
 }
 
 type Result<T> = result::Result<T, Error>;
@@ -70,6 +77,7 @@
             BindVsock(e) => write!(f, "failed to bind vsock: {}", e),
             EventFdNew(e) => write!(f, "failed to create eventfd: {}", e),
             IncorrectCid(cid) => write!(f, "chunnel connection from unexpected cid {}", cid),
+            LaunchForwarderGuest(e) => write!(f, "failed to launch forwarder_guest {}", e),
             NoListenerForPort(port) => write!(f, "could not find listener for port: {}", port),
             NoSessionForTag(tag) => write!(f, "could not find session for tag: {:x}", tag),
             PollContextAdd(e) => write!(f, "failed to add fd to poll context: {}", e),
@@ -78,9 +86,14 @@
             PollWait(e) => write!(f, "failed to wait for poll: {}", e),
             SetVsockNonblocking(e) => write!(f, "failed to set vsock to nonblocking: {}", e),
             TcpAccept(e) => write!(f, "failed to accept tcp: {}", e),
+            TcpListenerPort(e) => {
+                write!(f, "failed to read local sockaddr for tcp listener: {}", e)
+            }
             UpdateEventRead(e) => write!(f, "failed to read update eventfd: {}", e),
+            UpdateEventWrite(e) => write!(f, "failed to write update eventfd: {}", e),
             VsockAccept(e) => write!(f, "failed to accept vsock: {}", e),
             VsockAcceptTimeout => write!(f, "timed out waiting for vsock connection"),
+            VsockListenerPort(e) => write!(f, "failed to get vsock listener port: {}", e),
         }
     }
 }
@@ -120,24 +133,30 @@
 }
 
 /// ForwarderSessions encapsulates all forwarding state for chunneld.
-struct ForwarderSessions {
+struct ForwarderSessions<'a> {
     listening_ports: BTreeMap<u16, PortListeners>,
     tcp4_forwarders: HashMap<SessionTag, ForwarderSession>,
     update_evt: EventFd,
     update_queue: Arc<Mutex<VecDeque<TcpForwardTarget>>>,
+    jni_env: JNIEnv<'a>,
+    jni_cb: JObject<'a>,
 }
 
-impl ForwarderSessions {
+impl<'a> ForwarderSessions<'a> {
     /// Creates a new instance of ForwarderSessions.
     fn new(
         update_evt: EventFd,
         update_queue: Arc<Mutex<VecDeque<TcpForwardTarget>>>,
+        jni_env: JNIEnv<'a>,
+        jni_cb: JObject<'a>,
     ) -> Result<Self> {
         Ok(ForwarderSessions {
             listening_ports: BTreeMap::new(),
             tcp4_forwarders: HashMap::new(),
             update_evt,
             update_queue,
+            jni_env,
+            jni_cb,
         })
     }
 
@@ -217,7 +236,12 @@
         // This session should be dropped if any of the PollContext setup fails. Since the only
         // extant fds for the underlying sockets will be closed, they will be unregistered from
         // epoll set automatically.
-        let session = create_forwarder_session(listener, &port_listeners.forward_target)?;
+        let session = create_forwarder_session(
+            listener,
+            &port_listeners.forward_target,
+            &mut self.jni_env,
+            &self.jni_cb,
+        )?;
 
         let tag = session.local_stream().as_raw_fd() as u32;
 
@@ -309,6 +333,8 @@
 fn create_forwarder_session(
     listener: &TcpListener,
     target: &TcpForwardTarget,
+    jni_env: &mut JNIEnv,
+    jni_cb: &JObject,
 ) -> Result<ForwarderSession> {
     let (tcp_stream, _) = listener.accept().map_err(Error::TcpAccept)?;
     // Bind a vsock port, tell the guest to connect, and accept the connection.
@@ -316,6 +342,17 @@
         .map_err(Error::BindVsock)?;
     vsock_listener.set_nonblocking(true).map_err(Error::SetVsockNonblocking)?;
 
+    let tcp4_port = listener.local_addr().map_err(Error::TcpListenerPort)?.port();
+    let vsock_port = vsock_listener.local_addr().map_err(Error::VsockListenerPort)?.port();
+    jni_env
+        .call_method(
+            jni_cb,
+            "onForwardingRequestReceived",
+            "(II)V",
+            &[JValue::Int(tcp4_port.into()), JValue::Int(vsock_port as i32)],
+        )
+        .map_err(Error::LaunchForwarderGuest)?;
+
     #[derive(PollToken)]
     enum Token {
         VsockAccept,
@@ -341,12 +378,50 @@
     }
 }
 
+fn update_listening_ports(
+    update_queue: &Arc<Mutex<VecDeque<TcpForwardTarget>>>,
+    update_evt: &EventFd,
+    cid: i32,
+) -> Result<()> {
+    let mut update_queue = update_queue.lock().unwrap();
+
+    // TODO(b/340126051): Bring listening ports from the guest.
+    update_queue.push_back(TcpForwardTarget {
+        port: 12345, /* Example value for testing */
+        vsock_cid: cid as u32,
+    });
+
+    update_evt.write(1).map_err(Error::UpdateEventWrite)?;
+    Ok(())
+}
+
 // TODO(b/340126051): Host can receive opened ports from the guest.
-// TODO(b/340126051): Host can order executing chunnel on the guest.
-fn main() -> Result<()> {
+fn run_forwarder_host(cid: i32, jni_env: JNIEnv, jni_cb: JObject) -> Result<()> {
+    debug!("Starting forwarder_host");
     let update_evt = EventFd::new().map_err(Error::EventFdNew)?;
     let update_queue = Arc::new(Mutex::new(VecDeque::new()));
 
-    let mut sessions = ForwarderSessions::new(update_evt, update_queue)?;
+    // TODO(b/340126051): Instead of one-time execution, bring port info with separated thread.
+    update_listening_ports(&update_queue, &update_evt, cid)?;
+
+    let mut sessions = ForwarderSessions::new(update_evt, update_queue, jni_env, jni_cb)?;
     sessions.run()
 }
+
+/// JNI function for running forwarder_host.
+#[no_mangle]
+pub extern "C" fn Java_com_android_virtualization_vmlauncher_DebianServiceImpl_runForwarderHost(
+    env: JNIEnv,
+    _class: JObject,
+    cid: jint,
+    callback: JObject,
+) {
+    match run_forwarder_host(cid, env, callback) {
+        Ok(_) => {
+            info!("forwarder_host is terminated");
+        }
+        Err(e) => {
+            error!("Error on forwarder_host: {:?}", e);
+        }
+    }
+}
diff --git a/guest/forwarder_guest_launcher/Cargo.toml b/guest/forwarder_guest_launcher/Cargo.toml
index bf0c0ed..03d3f7f 100644
--- a/guest/forwarder_guest_launcher/Cargo.toml
+++ b/guest/forwarder_guest_launcher/Cargo.toml
@@ -4,10 +4,12 @@
 edition = "2021"
 
 [dependencies]
+anyhow = "1.0.91"
 clap = { version = "4.5.20", features = ["derive"] }
 prost = "0.13.3"
-tokio = { version = "1.40.0", features = ["rt-multi-thread"] }
+tokio = { version = "1.40.0", features = ["process", "rt-multi-thread"] }
 tonic = "0.12.3"
+vsock = "0.5.1"
 
 [build-dependencies]
 tonic-build = "0.12.3"
diff --git a/guest/forwarder_guest_launcher/src/main.rs b/guest/forwarder_guest_launcher/src/main.rs
index 4042fe5..59ee8c6 100644
--- a/guest/forwarder_guest_launcher/src/main.rs
+++ b/guest/forwarder_guest_launcher/src/main.rs
@@ -14,9 +14,11 @@
 
 //! Launcher of forwarder_guest
 
+use anyhow::Context;
 use clap::Parser;
 use debian_service::debian_service_client::DebianServiceClient;
-use debian_service::Empty;
+use debian_service::QueueOpeningRequest;
+use tokio::process::Command;
 use tonic::transport::Endpoint;
 use tonic::Request;
 
@@ -35,16 +37,34 @@
 
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    println!("Starting forwarder_guest_launcher");
     let args = Args::parse();
     let addr = format!("https://{}:12000", args.host_addr);
 
     let channel = Endpoint::from_shared(addr)?.connect().await?;
     let mut client = DebianServiceClient::new(channel);
-    let mut res_stream =
-        client.open_forwarding_request_queue(Request::new(Empty {})).await?.into_inner();
+    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 }))
+        .await?
+        .into_inner();
 
     while let Some(response) = res_stream.message().await? {
-        println!("Response from the host: {:?}", response);
+        let tcp_port = i16::try_from(response.guest_tcp_port)
+            .context("Failed to convert guest_tcp_port as i16")?;
+        let vsock_port = response.vsock_port as u32;
+
+        println!(
+            "executing forwarder_guest with guest_tcp_port: {:?}, vsock_port: {:?}",
+            &tcp_port, &vsock_port
+        );
+
+        let _ = Command::new("forwarder_guest")
+            .arg("--local")
+            .arg(format!("127.0.0.1:{}", tcp_port))
+            .arg("--remote")
+            .arg(format!("vsock:2:{}", vsock_port))
+            .spawn();
     }
     Ok(())
 }
diff --git a/libs/debian_service/proto/DebianService.proto b/libs/debian_service/proto/DebianService.proto
index 5e3286a..a887bf2 100644
--- a/libs/debian_service/proto/DebianService.proto
+++ b/libs/debian_service/proto/DebianService.proto
@@ -23,10 +23,12 @@
 
 service DebianService {
   rpc ReportVmIpAddr (IpAddr) returns (ReportVmIpAddrResponse) {}
-  rpc OpenForwardingRequestQueue (Empty) returns (stream ForwardingRequestItem) {}
+  rpc OpenForwardingRequestQueue (QueueOpeningRequest) returns (stream ForwardingRequestItem) {}
 }
 
-message Empty {}
+message QueueOpeningRequest {
+  int32 cid = 1;
+}
 
 message IpAddr {
   string addr = 1;
diff --git a/libs/libforwarder/Android.bp b/libs/libforwarder/Android.bp
index 48307e7..1bcb111 100644
--- a/libs/libforwarder/Android.bp
+++ b/libs/libforwarder/Android.bp
@@ -12,4 +12,7 @@
         "libvsock",
     ],
     proc_macros: ["libremain"],
+    apex_available: [
+        "com.android.virt",
+    ],
 }
diff --git a/libs/vm_launcher_lib/Android.bp b/libs/vm_launcher_lib/Android.bp
index f47f6b6..7dced4e 100644
--- a/libs/vm_launcher_lib/Android.bp
+++ b/libs/vm_launcher_lib/Android.bp
@@ -16,6 +16,7 @@
         "apache-commons-compress",
     ],
     libs: [
+        "androidx.annotation_annotation",
         "framework-virtualization.impl",
         "framework-annotations-lib",
     ],
diff --git a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
index ccc0ed6..dcc8152 100644
--- a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
@@ -18,10 +18,12 @@
 
 import android.util.Log;
 
+import androidx.annotation.Keep;
+
 import com.android.virtualization.vmlauncher.proto.DebianServiceGrpc;
-import com.android.virtualization.vmlauncher.proto.Empty;
 import com.android.virtualization.vmlauncher.proto.ForwardingRequestItem;
 import com.android.virtualization.vmlauncher.proto.IpAddr;
+import com.android.virtualization.vmlauncher.proto.QueueOpeningRequest;
 import com.android.virtualization.vmlauncher.proto.ReportVmIpAddrResponse;
 
 import io.grpc.stub.StreamObserver;
@@ -30,6 +32,10 @@
     public static final String TAG = "DebianService";
     private final DebianServiceCallback mCallback;
 
+    static {
+        System.loadLibrary("forwarder_host_jni");
+    }
+
     protected DebianServiceImpl(DebianServiceCallback callback) {
         super();
         mCallback = callback;
@@ -47,14 +53,32 @@
 
     @Override
     public void openForwardingRequestQueue(
-            Empty request, StreamObserver<ForwardingRequestItem> responseObserver) {
+            QueueOpeningRequest request, StreamObserver<ForwardingRequestItem> responseObserver) {
         Log.d(DebianServiceImpl.TAG, "OpenForwardingRequestQueue");
-
-        // TODO(b/340126051): Bring information from forwarder_host.
-
+        runForwarderHost(request.getCid(), new ForwarderHostCallback(responseObserver));
         responseObserver.onCompleted();
     }
 
+    @Keep
+    private static class ForwarderHostCallback {
+        private StreamObserver<ForwardingRequestItem> mResponseObserver;
+
+        ForwarderHostCallback(StreamObserver<ForwardingRequestItem> responseObserver) {
+            mResponseObserver = responseObserver;
+        }
+
+        private void onForwardingRequestReceived(int guestTcpPort, int vsockPort) {
+            ForwardingRequestItem item =
+                    ForwardingRequestItem.newBuilder()
+                            .setGuestTcpPort(guestTcpPort)
+                            .setVsockPort(vsockPort)
+                            .build();
+            mResponseObserver.onNext(item);
+        }
+    }
+
+    private static native void runForwarderHost(int cid, ForwarderHostCallback callback);
+
     protected interface DebianServiceCallback {
         void onIpAddressAvailable(String ipAddr);
     }