compsvc/pvm_exec: Support RPC binder

Bug: 190547489
Bug: 189947807
Test: [VM shell] /apex/com.android.compos/bin/compsvc \
          /system/bin/touch --rpc-binder
      [Android shell] /apex/com.android.compos/bin/pvm_exec --cid $CID \
              touch /data/local/tmp/foo
      # IPC did go through, but there is some unrelated problem in the
      # service, which can be fixed separately.

Change-Id: Ie3cdd58be1f98f8084b2b63dd325b76546bc5cf9
diff --git a/compos/src/pvm_exec.rs b/compos/src/pvm_exec.rs
index fcde266..987a198 100644
--- a/compos/src/pvm_exec.rs
+++ b/compos/src/pvm_exec.rs
@@ -25,6 +25,8 @@
 //! used. It is only for ergonomics.
 
 use anyhow::{bail, Context, Result};
+use binder::unstable_api::{new_spibinder, AIBinder};
+use binder::FromIBinder;
 use log::{error, warn};
 use minijail::Minijail;
 use nix::fcntl::{fcntl, FcntlArg::F_GETFD};
@@ -39,11 +41,26 @@
 };
 use compos_aidl_interface::binder::Strong;
 
-static SERVICE_NAME: &str = "compsvc";
-static FD_SERVER_BIN: &str = "/apex/com.android.virt/bin/fd_server";
+mod common;
+use common::{SERVICE_NAME, VSOCK_PORT};
 
-fn get_local_service() -> Strong<dyn ICompService> {
-    compos_aidl_interface::binder::get_interface(SERVICE_NAME).expect("Cannot reach compsvc")
+const FD_SERVER_BIN: &str = "/apex/com.android.virt/bin/fd_server";
+
+fn get_local_service() -> Result<Strong<dyn ICompService>> {
+    compos_aidl_interface::binder::get_interface(SERVICE_NAME).context("get local binder")
+}
+
+fn get_rpc_binder(cid: u32) -> Result<Strong<dyn ICompService>> {
+    // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can be
+    // safely taken by new_spibinder.
+    let ibinder = unsafe {
+        new_spibinder(binder_rpc_unstable_bindgen::RpcClient(cid, VSOCK_PORT) as *mut AIBinder)
+    };
+    if let Some(ibinder) = ibinder {
+        ICompService::try_from(ibinder).context("Cannot connect to RPC service")
+    } else {
+        bail!("Invalid raw AIBinder")
+    }
 }
 
 fn spawn_fd_server(metadata: &Metadata, debuggable: bool) -> Result<Minijail> {
@@ -53,7 +70,7 @@
         vec![]
     };
 
-    let mut args = vec![FD_SERVER_BIN.to_string()];
+    let mut args = vec![FD_SERVER_BIN.to_string(), "--rpc-binder".to_string()];
     for metadata in &metadata.input_fd_annotations {
         args.push("--ro-fds".to_string());
         args.push(metadata.fd.to_string());
@@ -86,6 +103,7 @@
 struct Config {
     args: Vec<String>,
     metadata: Metadata,
+    cid: Option<u32>,
     debuggable: bool,
 }
 
@@ -102,6 +120,9 @@
              .takes_value(true)
              .multiple(true)
              .use_delimiter(true))
+        .arg(clap::Arg::with_name("cid")
+             .takes_value(true)
+             .long("cid"))
         .arg(clap::Arg::with_name("debug")
              .long("debug"))
         .arg(clap::Arg::with_name("args")
@@ -132,18 +153,21 @@
     let output_fd_annotations = results?;
 
     let args: Vec<_> = matches.values_of("args").unwrap().map(|s| s.to_string()).collect();
+    let cid =
+        if let Some(arg) = matches.value_of("cid") { Some(arg.parse::<u32>()?) } else { None };
     let debuggable = matches.is_present("debug");
 
     Ok(Config {
         args,
         metadata: Metadata { input_fd_annotations, output_fd_annotations },
+        cid,
         debuggable,
     })
 }
 
 fn main() -> Result<()> {
     // 1. Parse the command line arguments for collect execution data.
-    let Config { args, metadata, debuggable } = parse_args()?;
+    let Config { args, metadata, cid, debuggable } = parse_args()?;
 
     // 2. Spawn and configure a fd_server to serve remote read/write requests.
     let fd_server_jail = spawn_fd_server(&metadata, debuggable)?;
@@ -156,7 +180,8 @@
     });
 
     // 3. Send the command line args to the remote to execute.
-    let exit_code = get_local_service().execute(&args, &metadata).context("Binder call failed")?;
+    let service = if let Some(cid) = cid { get_rpc_binder(cid) } else { get_local_service() }?;
+    let exit_code = service.execute(&args, &metadata).context("Binder call failed")?;
 
     // Be explicit about the lifetime, which should last at least until the task is finished.
     drop(fd_server_lifetime);