vmclient: Spawn child virtmgr and connect to it
Instead of connecting to the global virtualizationservice, make
vmclient::connect() spawn a child virtmgr process and connect to it via
RpcBinder.
Bug: 245727626
Test: atest -p packages/modules/Virtualization:avf-presubmit
Change-Id: I456a151071a5f0d8448b11b89d88c1c2892f911f
diff --git a/vmclient/Android.bp b/vmclient/Android.bp
index 88b0c9a..0a2e692 100644
--- a/vmclient/Android.bp
+++ b/vmclient/Android.bp
@@ -11,8 +11,11 @@
"android.system.virtualizationcommon-rust",
"android.system.virtualizationservice-rust",
"libbinder_rs",
+ "libcommand_fds",
"liblog_rust",
+ "libnix",
"librpcbinder_rs",
+ "libshared_child",
"libthiserror",
],
shared_libs: [
diff --git a/vmclient/src/lib.rs b/vmclient/src/lib.rs
index a9c2619..0e3d140 100644
--- a/vmclient/src/lib.rs
+++ b/vmclient/src/lib.rs
@@ -39,12 +39,16 @@
ParcelFileDescriptor, Result as BinderResult, StatusCode, Strong,
},
};
+use command_fds::CommandFdExt;
use log::warn;
-use rpcbinder::RpcSession;
+use rpcbinder::{FileDescriptorTransportMode, RpcSession};
+use shared_child::SharedChild;
+use std::io::{self, Read};
+use std::process::Command;
use std::{
fmt::{self, Debug, Formatter},
fs::File,
- os::unix::io::IntoRawFd,
+ os::unix::io::{AsFd, AsRawFd, FromRawFd, IntoRawFd, OwnedFd},
sync::Arc,
time::Duration,
};
@@ -52,6 +56,79 @@
const VIRTUALIZATION_SERVICE_BINDER_SERVICE_IDENTIFIER: &str =
"android.system.virtualizationservice";
+const VIRTMGR_PATH: &str = "/apex/com.android.virt/bin/virtmgr";
+const VIRTMGR_THREADS: usize = 16;
+
+fn posix_pipe() -> Result<(OwnedFd, OwnedFd), io::Error> {
+ use nix::fcntl::OFlag;
+ use nix::unistd::pipe2;
+
+ // Create new POSIX pipe. Make it O_CLOEXEC to align with how Rust creates
+ // file descriptors (expected by SharedChild).
+ let (raw1, raw2) = pipe2(OFlag::O_CLOEXEC)?;
+
+ // SAFETY - Taking ownership of brand new FDs.
+ unsafe { Ok((OwnedFd::from_raw_fd(raw1), OwnedFd::from_raw_fd(raw2))) }
+}
+
+fn posix_socketpair() -> Result<(OwnedFd, OwnedFd), io::Error> {
+ use nix::sys::socket::{socketpair, AddressFamily, SockFlag, SockType};
+
+ // Create new POSIX socketpair, suitable for use with RpcBinder UDS bootstrap
+ // transport. Make it O_CLOEXEC to align with how Rust creates file
+ // descriptors (expected by SharedChild).
+ let (raw1, raw2) =
+ socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::SOCK_CLOEXEC)?;
+
+ // SAFETY - Taking ownership of brand new FDs.
+ unsafe { Ok((OwnedFd::from_raw_fd(raw1), OwnedFd::from_raw_fd(raw2))) }
+}
+
+/// A running instance of virtmgr which is hosting a VirtualizationService
+/// RpcBinder server.
+pub struct VirtualizationService {
+ /// Client FD for UDS connection to virtmgr's RpcBinder server. Closing it
+ /// will make virtmgr shut down.
+ client_fd: OwnedFd,
+}
+
+impl VirtualizationService {
+ /// Spawns a new instance of virtmgr, a child process that will host
+ /// the VirtualizationService AIDL service.
+ pub fn new() -> Result<VirtualizationService, io::Error> {
+ let (wait_fd, ready_fd) = posix_pipe()?;
+ let (client_fd, server_fd) = posix_socketpair()?;
+
+ let mut command = Command::new(VIRTMGR_PATH);
+ command.arg("--rpc-server-fd").arg(format!("{}", server_fd.as_raw_fd()));
+ command.arg("--ready-fd").arg(format!("{}", ready_fd.as_raw_fd()));
+ command.preserved_fds(vec![server_fd.as_raw_fd(), ready_fd.as_raw_fd()]);
+
+ SharedChild::spawn(&mut command)?;
+
+ // Drop FDs that belong to virtmgr.
+ drop(server_fd);
+ drop(ready_fd);
+
+ // Wait for the child to signal that the RpcBinder server is ready
+ // by closing its end of the pipe.
+ let _ = File::from(wait_fd).read(&mut [0]);
+
+ Ok(VirtualizationService { client_fd })
+ }
+
+ /// Connects to the VirtualizationService AIDL service.
+ pub fn connect(&self) -> Result<Strong<dyn IVirtualizationService>, io::Error> {
+ let session = RpcSession::new();
+ session.set_file_descriptor_transport_mode(FileDescriptorTransportMode::Unix);
+ session.set_max_incoming_threads(VIRTMGR_THREADS);
+ session.set_max_outgoing_threads(VIRTMGR_THREADS);
+ session
+ .setup_unix_domain_bootstrap_client(self.client_fd.as_fd())
+ .map_err(|_| io::Error::from(io::ErrorKind::ConnectionRefused))
+ }
+}
+
/// Connects to the VirtualizationService AIDL service.
pub fn connect() -> Result<Strong<dyn IVirtualizationService>, StatusCode> {
wait_for_interface(VIRTUALIZATION_SERVICE_BINDER_SERVICE_IDENTIFIER)