Merge changes from topic "rust-rpc-binder" am: 553baa9d3b

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Virtualization/+/1728672

Change-Id: I2f07812a1c4591ff728dfca73ecd163831f441cd
diff --git a/authfs/Android.bp b/authfs/Android.bp
index 16d0b3a..174914f 100644
--- a/authfs/Android.bp
+++ b/authfs/Android.bp
@@ -14,6 +14,8 @@
         "libandroid_logger",
         "libanyhow",
         "libauthfs_crypto_bindgen",
+        "libbinder_rpc_unstable_bindgen",
+        "libbinder_rs",
         "libcfg_if",
         "libfuse_rust",
         "liblibc",
@@ -27,7 +29,10 @@
             enabled: false,
         },
     },
-    shared_libs: ["libcrypto"],
+    shared_libs: [
+        "libcrypto",
+        "libbinder_rpc_unstable",
+    ],
     defaults: ["crosvm_defaults"],
 }
 
diff --git a/authfs/fd_server/Android.bp b/authfs/fd_server/Android.bp
index 748a5b6..8ddbf69 100644
--- a/authfs/fd_server/Android.bp
+++ b/authfs/fd_server/Android.bp
@@ -9,11 +9,16 @@
         "authfs_aidl_interface-rust",
         "libandroid_logger",
         "libanyhow",
+        "libbinder_rpc_unstable_bindgen",
+        "libbinder_rs",
         "libclap",
         "liblibc",
         "liblog_rust",
         "libnix",
     ],
     prefer_rlib: true,
+    shared_libs: [
+        "libbinder_rpc_unstable",
+    ],
     apex_available: ["com.android.virt"],
 }
diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs
index 204d1b1..5137a2e 100644
--- a/authfs/fd_server/src/main.rs
+++ b/authfs/fd_server/src/main.rs
@@ -37,6 +37,7 @@
 use std::os::unix::io::{AsRawFd, FromRawFd};
 
 use anyhow::{bail, Context, Result};
+use binder::unstable_api::AsNative;
 use log::{debug, error};
 
 use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService::{
@@ -48,6 +49,7 @@
 };
 
 const SERVICE_NAME: &str = "authfs_fd_server";
+const RPC_SERVICE_PORT: u32 = 3264; // TODO: support dynamic port for multiple fd_server instances
 
 fn new_binder_exception<T: AsRef<str>>(exception: ExceptionCode, message: T) -> Status {
     Status::new_exception(exception, CString::new(message.as_ref()).as_deref().ok())
@@ -275,7 +277,7 @@
     Ok((fd, FdConfig::ReadWrite(file)))
 }
 
-fn parse_args() -> Result<BTreeMap<i32, FdConfig>> {
+fn parse_args() -> Result<(bool, BTreeMap<i32, FdConfig>)> {
     #[rustfmt::skip]
     let matches = clap::App::new("fd_server")
         .arg(clap::Arg::with_name("ro-fds")
@@ -286,6 +288,8 @@
              .long("rw-fds")
              .multiple(true)
              .number_of_values(1))
+        .arg(clap::Arg::with_name("rpc-binder")
+             .long("rpc-binder"))
         .get_matches();
 
     let mut fd_pool = BTreeMap::new();
@@ -301,7 +305,9 @@
             fd_pool.insert(fd, config);
         }
     }
-    Ok(fd_pool)
+
+    let rpc_binder = matches.is_present("rpc-binder");
+    Ok((rpc_binder, fd_pool))
 }
 
 fn main() -> Result<()> {
@@ -309,14 +315,32 @@
         android_logger::Config::default().with_tag("fd_server").with_min_level(log::Level::Debug),
     );
 
-    let fd_pool = parse_args()?;
+    let (rpc_binder, fd_pool) = parse_args()?;
 
-    ProcessState::start_thread_pool();
-
-    add_service(SERVICE_NAME, FdService::new_binder(fd_pool).as_binder())
-        .with_context(|| format!("Failed to register service {}", SERVICE_NAME))?;
-    debug!("fd_server is running.");
-
-    ProcessState::join_thread_pool();
-    bail!("Unexpected exit after join_thread_pool")
+    if rpc_binder {
+        let mut service = FdService::new_binder(fd_pool).as_binder();
+        debug!("fd_server is starting as a rpc service.");
+        // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+        // Plus the binder objects are threadsafe.
+        let retval = unsafe {
+            binder_rpc_unstable_bindgen::RunRpcServer(
+                service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder,
+                RPC_SERVICE_PORT,
+            )
+        };
+        if retval {
+            debug!("RPC server has shut down gracefully");
+            Ok(())
+        } else {
+            bail!("Premature termination of RPC server");
+        }
+    } else {
+        ProcessState::start_thread_pool();
+        let service = FdService::new_binder(fd_pool).as_binder();
+        add_service(SERVICE_NAME, service)
+            .with_context(|| format!("Failed to register service {}", SERVICE_NAME))?;
+        debug!("fd_server is running as a local service.");
+        ProcessState::join_thread_pool();
+        bail!("Unexpected exit after join_thread_pool")
+    }
 }
diff --git a/authfs/src/file.rs b/authfs/src/file.rs
index 4b43786..033dbd6 100644
--- a/authfs/src/file.rs
+++ b/authfs/src/file.rs
@@ -4,21 +4,56 @@
 pub use local_file::LocalFileReader;
 pub use remote_file::{RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader};
 
+use binder::unstable_api::{new_spibinder, AIBinder};
+use binder::FromIBinder;
 use std::io;
 
 use crate::common::CHUNK_SIZE;
-
-use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService;
+use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService::IVirtFdService;
 use authfs_aidl_interface::binder::{get_interface, Strong};
 
-// TODO(victorhsieh): use remote binder.
-pub fn get_local_binder() -> Strong<dyn IVirtFdService::IVirtFdService> {
-    let service_name = "authfs_fd_server";
-    get_interface(&service_name).expect("Cannot reach authfs_fd_server binder service")
-}
+pub type VirtFdService = Strong<dyn IVirtFdService>;
 
 pub type ChunkBuffer = [u8; CHUNK_SIZE as usize];
 
+pub const RPC_SERVICE_PORT: u32 = 3264;
+
+fn get_local_binder() -> io::Result<VirtFdService> {
+    let service_name = "authfs_fd_server";
+    get_interface(&service_name).map_err(|e| {
+        io::Error::new(
+            io::ErrorKind::AddrNotAvailable,
+            format!("Cannot reach authfs_fd_server binder service: {}", e),
+        )
+    })
+}
+
+fn get_rpc_binder(cid: u32) -> io::Result<VirtFdService> {
+    // 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, RPC_SERVICE_PORT) as *mut AIBinder)
+    };
+    if let Some(ibinder) = ibinder {
+        Ok(IVirtFdService::try_from(ibinder).map_err(|e| {
+            io::Error::new(
+                io::ErrorKind::AddrNotAvailable,
+                format!("Cannot connect to RPC service: {}", e),
+            )
+        })?)
+    } else {
+        Err(io::Error::new(io::ErrorKind::InvalidInput, "Invalid raw AIBinder"))
+    }
+}
+
+pub fn get_binder_service(cid: Option<u32>) -> io::Result<VirtFdService> {
+    if let Some(cid) = cid {
+        get_rpc_binder(cid)
+    } else {
+        get_local_binder()
+    }
+}
+
 /// A trait for reading data by chunks. Chunks can be read by specifying the chunk index. Only the
 /// last chunk may have incomplete chunk size.
 pub trait ReadByChunk {
diff --git a/authfs/src/file/remote_file.rs b/authfs/src/file/remote_file.rs
index bd99893..0b6c007 100644
--- a/authfs/src/file/remote_file.rs
+++ b/authfs/src/file/remote_file.rs
@@ -19,14 +19,9 @@
 use std::io;
 use std::sync::{Arc, Mutex};
 
-use super::{ChunkBuffer, RandomWrite, ReadByChunk};
+use super::{ChunkBuffer, RandomWrite, ReadByChunk, VirtFdService};
 use crate::common::CHUNK_SIZE;
 
-use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService;
-use authfs_aidl_interface::binder::Strong;
-
-type VirtFdService = Strong<dyn IVirtFdService::IVirtFdService>;
-
 fn remote_read_chunk(
     service: &Arc<Mutex<VirtFdService>>,
     remote_fd: i32,
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index b30195a..593fa74 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -53,6 +53,10 @@
     #[structopt(parse(from_os_str))]
     mount_point: PathBuf,
 
+    /// CID of the VM where the service runs.
+    #[structopt(long)]
+    cid: Option<u32>,
+
     /// A read-only remote file with integrity check. Can be multiple.
     ///
     /// For example, `--remote-verified-file 5:10:1234:/path/to/cert` tells the filesystem to
@@ -205,8 +209,11 @@
     })
 }
 
-fn new_config_remote_verified_file(remote_id: i32, file_size: u64) -> Result<FileConfig> {
-    let service = file::get_local_binder();
+fn new_config_remote_verified_file(
+    service: file::VirtFdService,
+    remote_id: i32,
+    file_size: u64,
+) -> Result<FileConfig> {
     let signature = service.readFsveritySignature(remote_id).context("Failed to read signature")?;
 
     let service = Arc::new(Mutex::new(service));
@@ -223,8 +230,12 @@
     })
 }
 
-fn new_config_remote_unverified_file(remote_id: i32, file_size: u64) -> Result<FileConfig> {
-    let reader = RemoteFileReader::new(Arc::new(Mutex::new(file::get_local_binder())), remote_id);
+fn new_config_remote_unverified_file(
+    service: file::VirtFdService,
+    remote_id: i32,
+    file_size: u64,
+) -> Result<FileConfig> {
+    let reader = RemoteFileReader::new(Arc::new(Mutex::new(service)), remote_id);
     Ok(FileConfig::RemoteUnverifiedReadonlyFile { reader, file_size })
 }
 
@@ -251,31 +262,38 @@
     Ok(FileConfig::LocalUnverifiedReadonlyFile { reader, file_size })
 }
 
-fn new_config_remote_new_verified_file(remote_id: i32) -> Result<FileConfig> {
-    let remote_file =
-        RemoteFileEditor::new(Arc::new(Mutex::new(file::get_local_binder())), remote_id);
+fn new_config_remote_new_verified_file(
+    service: file::VirtFdService,
+    remote_id: i32,
+) -> Result<FileConfig> {
+    let remote_file = RemoteFileEditor::new(Arc::new(Mutex::new(service)), remote_id);
     Ok(FileConfig::RemoteVerifiedNewFile { editor: VerifiedFileEditor::new(remote_file) })
 }
 
 fn prepare_file_pool(args: &Args) -> Result<BTreeMap<Inode, FileConfig>> {
     let mut file_pool = BTreeMap::new();
 
+    let service = file::get_binder_service(args.cid)?;
+
     for config in &args.remote_ro_file {
         file_pool.insert(
             config.ino,
-            new_config_remote_verified_file(config.remote_id, config.file_size)?,
+            new_config_remote_verified_file(service.clone(), config.remote_id, config.file_size)?,
         );
     }
 
     for config in &args.remote_ro_file_unverified {
         file_pool.insert(
             config.ino,
-            new_config_remote_unverified_file(config.remote_id, config.file_size)?,
+            new_config_remote_unverified_file(service.clone(), config.remote_id, config.file_size)?,
         );
     }
 
     for config in &args.remote_new_rw_file {
-        file_pool.insert(config.ino, new_config_remote_new_verified_file(config.remote_id)?);
+        file_pool.insert(
+            config.ino,
+            new_config_remote_new_verified_file(service.clone(), config.remote_id)?,
+        );
     }
 
     for config in &args.local_ro_file {
diff --git a/compos/Android.bp b/compos/Android.bp
index 1611b68..858f64c 100644
--- a/compos/Android.bp
+++ b/compos/Android.bp
@@ -8,6 +8,8 @@
     rustlibs: [
         "compos_aidl_interface-rust",
         "libanyhow",
+        "libbinder_rpc_unstable_bindgen",
+        "libbinder_rs",
         "libclap",
         "liblibc",
         "liblog_rust",
@@ -16,6 +18,9 @@
         "libscopeguard",
     ],
     prefer_rlib: true,
+    shared_libs: [
+        "libbinder_rpc_unstable",
+    ],
     apex_available: [
         "com.android.compos",
     ],
@@ -28,11 +33,16 @@
         "compos_aidl_interface-rust",
         "libandroid_logger",
         "libanyhow",
+        "libbinder_rpc_unstable_bindgen",
+        "libbinder_rs",
         "libclap",
         "liblog_rust",
         "libminijail_rust",
     ],
     prefer_rlib: true,
+    shared_libs: [
+        "libbinder_rpc_unstable",
+    ],
     apex_available: [
         "com.android.compos",
     ],
diff --git a/compos/src/common.rs b/compos/src/common.rs
new file mode 100644
index 0000000..6cad63a
--- /dev/null
+++ b/compos/src/common.rs
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+/// Port to listen. This should be out of future port range (if happens) that microdroid may
+/// reserve for system components.
+pub const VSOCK_PORT: u32 = 6432;
+
+/// Service name of local binder. Used only for debugging purpose.
+pub const SERVICE_NAME: &str = "compsvc";
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index ddfcea0..16c3510 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -29,7 +29,8 @@
 //!     - actual task
 
 use anyhow::{bail, Context, Result};
-use log::error;
+use binder::unstable_api::AsNative;
+use log::{debug, error};
 use minijail::{self, Minijail};
 use std::path::PathBuf;
 
@@ -42,7 +43,9 @@
     StatusCode, Strong,
 };
 
-const SERVICE_NAME: &str = "compsvc";
+mod common;
+use common::{SERVICE_NAME, VSOCK_PORT};
+
 const WORKER_BIN: &str = "/apex/com.android.compos/bin/compsvc_worker";
 // TODO: Replace with a valid directory setup in the VM.
 const AUTHFS_MOUNTPOINT: &str = "/data/local/tmp/authfs_mnt";
@@ -50,6 +53,7 @@
 struct CompService {
     worker_bin: PathBuf,
     task_bin: String,
+    rpc_binder: bool,
     debuggable: bool,
 }
 
@@ -128,11 +132,14 @@
              .long("debug"))
         .arg(clap::Arg::with_name("task_bin")
              .required(true))
+        .arg(clap::Arg::with_name("rpc_binder")
+             .long("rpc-binder"))
         .get_matches();
 
     Ok(CompService {
         task_bin: matches.value_of("task_bin").unwrap().to_string(),
         worker_bin: PathBuf::from(WORKER_BIN),
+        rpc_binder: matches.is_present("rpc_binder"),
         debuggable: matches.is_present("debug"),
     })
 }
@@ -144,10 +151,29 @@
 
     let service = parse_args()?;
 
-    ProcessState::start_thread_pool();
-    // TODO: switch to remote binder
-    add_service(SERVICE_NAME, CompService::new_binder(service).as_binder())
-        .with_context(|| format!("Failed to register service {}", SERVICE_NAME))?;
-    ProcessState::join_thread_pool();
-    bail!("Unexpected exit after join_thread_pool")
+    if service.rpc_binder {
+        let mut service = CompService::new_binder(service).as_binder();
+        debug!("compsvc is starting as a rpc service.");
+        // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+        // Plus the binder objects are threadsafe.
+        let retval = unsafe {
+            binder_rpc_unstable_bindgen::RunRpcServer(
+                service.as_native_mut() as *mut binder_rpc_unstable_bindgen::AIBinder,
+                VSOCK_PORT,
+            )
+        };
+        if retval {
+            debug!("RPC server has shut down gracefully");
+            Ok(())
+        } else {
+            bail!("Premature termination of RPC server");
+        }
+    } else {
+        ProcessState::start_thread_pool();
+        debug!("compsvc is starting as a local service.");
+        add_service(SERVICE_NAME, CompService::new_binder(service).as_binder())
+            .with_context(|| format!("Failed to register service {}", SERVICE_NAME))?;
+        ProcessState::join_thread_pool();
+        bail!("Unexpected exit after join_thread_pool")
+    }
 }
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);
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index d350b56..a8dcba8 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -100,7 +100,11 @@
         lib64: {
             deps: [
                 "apkdmverity",
+                "authfs",
                 "zipfuse",
+
+                // TODO(b/184872979): Needed by authfs. Remove once the Rust API is created.
+                "libbinder_rpc_unstable",
             ],
         },
     },