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/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);