Host can send required infos to the guest for performing forwarding.

Since forwarder_guest_launcher is not implemented in 100%, we should
manually run forwarder_guest_launcher with `--host-addr 192.168.0.1`,
and accessing `http://localhost:12345` to check the log printed at
forwarder_guest_launcher.

Bug: 340126051
Test: Manual test described above.

Change-Id: If3a7784fb29febc3fed8653297fa6c840ef22b49
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..a32ce20 100644
--- a/android/forwarder_host/src/main.rs
+++ b/android/forwarder_host/src/forwarder_host.rs
@@ -28,7 +28,9 @@
 use std::time::Duration;
 
 use forwarder::forwarder::ForwarderSession;
-use log::{error, warn};
+use jni::objects::{JObject, JValue};
+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 +47,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 +56,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 +76,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 +85,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 +132,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 +235,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 +332,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 +341,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 +377,49 @@
     }
 }
 
+fn update_listening_ports(
+    update_queue: &Arc<Mutex<VecDeque<TcpForwardTarget>>>,
+    update_evt: &EventFd,
+) -> 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: 157, /* Placeholder value */
+    });
+
+    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(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)?;
+
+    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,
+    callback: JObject,
+) {
+    match run_forwarder_host(env, callback) {
+        Ok(_) => {
+            info!("forwarder_host is terminated");
+        }
+        Err(e) => {
+            error!("Error on forwarder_host: {:?}", e);
+        }
+    }
+}
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..d62ccfb 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,6 +18,8 @@
 
 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;
@@ -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;
@@ -49,12 +55,30 @@
     public void openForwardingRequestQueue(
             Empty request, StreamObserver<ForwardingRequestItem> responseObserver) {
         Log.d(DebianServiceImpl.TAG, "OpenForwardingRequestQueue");
-
-        // TODO(b/340126051): Bring information from forwarder_host.
-
+        runForwarderHost(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(ForwarderHostCallback callback);
+
     protected interface DebianServiceCallback {
         void onIpAddressAvailable(String ipAddr);
     }