Framework JNI uses libvmclient to connect to virtmgr
libvmclient, as its name shows, is a library designed as a client-side
library for AVF. It is responsible for spawning virtmgr and make a
connection to it.
However, the library wasn't used for the Java APIs. For those APIs, the
JNI part (libvirtualization_jni) implemented the almost identical
routine for spawning virtmgr.
This change de-duplicates the code in the JNI part by exposing an FFI
for libvmclient and let the JNI part use it.
Note that the JNI part had more checks at the end of the connection
establishment. That part is copied over to libvmclient.
Bug: N/A
Test: watch TH
Change-Id: Ib6b0f3a180a7dbd4c797188e98d4b0e66f76423b
diff --git a/libs/libvmclient/Android.bp b/libs/libvmclient/Android.bp
index 9fdeaf8..5bd59da 100644
--- a/libs/libvmclient/Android.bp
+++ b/libs/libvmclient/Android.bp
@@ -2,8 +2,8 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-rust_library {
- name: "libvmclient",
+rust_defaults {
+ name: "libvmclient.default",
crate_name: "vmclient",
defaults: ["avf_build_flags_rust"],
srcs: ["src/lib.rs"],
@@ -25,3 +25,13 @@
"com.android.virt",
],
}
+
+rust_library {
+ name: "libvmclient",
+ defaults: ["libvmclient.default"],
+}
+
+rust_ffi_static {
+ name: "libvmclient.ffi",
+ defaults: ["libvmclient.default"],
+}
diff --git a/libs/libvmclient/src/lib.rs b/libs/libvmclient/src/lib.rs
index 7b576e6..bc9d683 100644
--- a/libs/libvmclient/src/lib.rs
+++ b/libs/libvmclient/src/lib.rs
@@ -43,7 +43,9 @@
use log::warn;
use rpcbinder::{FileDescriptorTransportMode, RpcSession};
use shared_child::SharedChild;
+use std::ffi::{c_char, c_int, c_void, CString};
use std::io::{self, Read};
+use std::os::fd::RawFd;
use std::process::Command;
use std::{
fmt::{self, Debug, Formatter},
@@ -74,6 +76,40 @@
Ok(socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::SOCK_CLOEXEC)?)
}
+/// Error handling function for `get_virtualization_service`.
+///
+/// # Safety
+/// `message` shouldn't be used outside of the lifetime of the function. Management of `ctx` is
+/// entirely up to the function.
+pub type ErrorCallback =
+ unsafe extern "C" fn(code: c_int, message: *const c_char, ctx: *mut c_void);
+
+/// Spawns a new instance of virtmgr and rerturns a file descriptor for the socket connection to
+/// the service. When error occurs, it is reported via the ErrorCallback function along with the
+/// error message and any context that is set by the client.
+///
+/// # Safety
+/// `cb` should be null or a valid function pointer of type `ErrorCallback`
+#[no_mangle]
+pub unsafe extern "C" fn get_virtualization_service(
+ cb: Option<ErrorCallback>,
+ ctx: *mut c_void,
+) -> RawFd {
+ match VirtualizationService::new() {
+ Ok(vs) => vs.client_fd.into_raw_fd(),
+ Err(e) => {
+ if let Some(cb) = cb {
+ let code = e.raw_os_error().unwrap_or(-1);
+ let msg = CString::new(e.to_string()).unwrap();
+ // SAFETY: `cb` doesn't use `msg` outside of the lifetime of the function.
+ // msg's lifetime is longer than `cb` as it is bound to a local variable.
+ unsafe { cb(code, msg.as_ptr(), ctx) };
+ }
+ -1
+ }
+ }
+}
+
/// A running instance of virtmgr which is hosting a VirtualizationService
/// RpcBinder server.
pub struct VirtualizationService {
@@ -97,10 +133,11 @@
SharedChild::spawn(&mut command)?;
- // Wait for the child to signal that the RpcBinder server is ready
- // by closing its end of the pipe.
- let _ignored = File::from(wait_fd).read(&mut [0]);
-
+ // Wait for the child to signal that the RpcBinder server is read by closing its end of the
+ // pipe. Failing to read (especially EACCESS or EPERM) can happen if the client lacks the
+ // MANAGE_VIRTUAL_MACHINE permission. Therefore, such errors are propagated instead of
+ // being ignored.
+ let _ = File::from(wait_fd).read(&mut [0])?;
Ok(VirtualizationService { client_fd })
}