Factor out Rust client library for VirtualizationService.
This reduces code duplication, and will also be useful for Rust tests.
Test: ComposHostTestCases compos_key_tests
Change-Id: I13c41d3b2bbe506495b723e7739f3181cb033f0f
diff --git a/compos/common/Android.bp b/compos/common/Android.bp
index 39e7c0a..0377474 100644
--- a/compos/common/Android.bp
+++ b/compos/common/Android.bp
@@ -17,6 +17,7 @@
"liblog_rust",
"libnum_traits",
"librustutils",
+ "libvmclient",
],
proc_macros: ["libnum_derive"],
shared_libs: [
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index 839280c..15f74cd 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -27,8 +27,7 @@
VirtualMachineConfig::VirtualMachineConfig,
};
use android_system_virtualizationservice::binder::{
- wait_for_interface, BinderFeatures, DeathRecipient, IBinder, Interface, ParcelFileDescriptor,
- Result as BinderResult, Strong,
+ BinderFeatures, Interface, ParcelFileDescriptor, Result as BinderResult, Strong,
};
use anyhow::{anyhow, bail, Context, Result};
use binder::{
@@ -44,15 +43,11 @@
use std::os::raw;
use std::os::unix::io::IntoRawFd;
use std::path::{Path, PathBuf};
-use std::sync::{Arc, Condvar, Mutex};
use std::thread;
+use vmclient::VmInstance;
/// This owns an instance of the CompOS VM.
-pub struct VmInstance {
- #[allow(dead_code)] // Keeps the VM alive even if we don`t touch it
- vm: Strong<dyn IVirtualMachine>,
- cid: i32,
-}
+pub struct ComposClient(VmInstance);
/// Parameters to be used when creating a virtual machine instance.
#[derive(Default, Debug, Clone)]
@@ -74,14 +69,7 @@
pub never_log: bool,
}
-impl VmInstance {
- /// Return a new connection to the Virtualization Service binder interface. This will start the
- /// service if necessary.
- pub fn connect_to_virtualization_service() -> Result<Strong<dyn IVirtualizationService>> {
- wait_for_interface::<dyn IVirtualizationService>("android.system.virtualizationservice")
- .context("Failed to find VirtualizationService")
- }
-
+impl ComposClient {
/// Start a new CompOS VM instance using the specified instance image file and parameters.
pub fn start(
service: &dyn IVirtualizationService,
@@ -89,7 +77,7 @@
idsig: &Path,
idsig_manifest_apk: &Path,
parameters: &VmParameters,
- ) -> Result<VmInstance> {
+ ) -> Result<Self> {
let protected_vm = want_protected_vm()?;
let instance_fd = ParcelFileDescriptor::new(instance_image);
@@ -121,8 +109,6 @@
.context("Failed to create console log file")?;
let log_fd = File::create(data_dir.join("vm.log"))
.context("Failed to create system log file")?;
- let console_fd = ParcelFileDescriptor::new(console_fd);
- let log_fd = ParcelFileDescriptor::new(log_fd);
info!("Running in debug level {:?}", debug_level);
(Some(console_fd), Some(log_fd))
};
@@ -142,31 +128,18 @@
taskProfiles: parameters.task_profiles.clone(),
});
- let vm = service
- .createVm(&config, console_fd.as_ref(), log_fd.as_ref())
+ let instance = VmInstance::create(service, &config, console_fd, log_fd)
.context("Failed to create VM")?;
- let vm_state = Arc::new(VmStateMonitor::default());
- let vm_state_clone = Arc::clone(&vm_state);
- let mut death_recipient = DeathRecipient::new(move || {
- vm_state_clone.set_died();
- log::error!("VirtualizationService died");
- });
- // Note that dropping death_recipient cancels this, so we can't use a temporary here.
- vm.as_binder().link_to_death(&mut death_recipient)?;
+ let callback =
+ BnVirtualMachineCallback::new_binder(VmCallback(), BinderFeatures::default());
+ instance.vm.registerCallback(&callback)?;
- let vm_state_clone = Arc::clone(&vm_state);
- let callback = BnVirtualMachineCallback::new_binder(
- VmCallback(vm_state_clone),
- BinderFeatures::default(),
- );
- vm.registerCallback(&callback)?;
+ instance.start()?;
- vm.start()?;
+ instance.wait_until_ready(timeouts()?.vm_max_time_to_ready)?;
- let cid = vm_state.wait_until_ready()?;
-
- Ok(VmInstance { vm, cid })
+ Ok(Self(instance))
}
fn locate_config_apk(apex_dir: &Path) -> Result<PathBuf> {
@@ -186,7 +159,7 @@
/// Create and return an RPC Binder connection to the Comp OS service in the VM.
pub fn get_service(&self) -> Result<Strong<dyn ICompOsService>> {
- let mut vsock_factory = VsockFactory::new(&*self.vm);
+ let mut vsock_factory = VsockFactory::new(&*self.0.vm);
let ibinder = vsock_factory
.connect_rpc_client()
@@ -194,12 +167,6 @@
FromIBinder::try_from(ibinder).context("Connecting to CompOS service")
}
-
- /// Return the CID of the VM.
- pub fn cid(&self) -> i32 {
- // TODO: Do we actually need/use this?
- self.cid
- }
}
fn prepare_idsig(
@@ -295,67 +262,12 @@
}
}
-#[derive(Debug, Default)]
-struct VmState {
- has_died: bool,
- cid: Option<i32>,
-}
-
-#[derive(Debug)]
-struct VmStateMonitor {
- mutex: Mutex<VmState>,
- state_ready: Condvar,
-}
-
-impl Default for VmStateMonitor {
- fn default() -> Self {
- Self { mutex: Mutex::new(Default::default()), state_ready: Condvar::new() }
- }
-}
-
-impl VmStateMonitor {
- fn set_died(&self) {
- let mut state = self.mutex.lock().unwrap();
- state.has_died = true;
- state.cid = None;
- drop(state); // Unlock the mutex prior to notifying
- self.state_ready.notify_all();
- }
-
- fn set_ready(&self, cid: i32) {
- let mut state = self.mutex.lock().unwrap();
- if state.has_died {
- return;
- }
- state.cid = Some(cid);
- drop(state); // Unlock the mutex prior to notifying
- self.state_ready.notify_all();
- }
-
- fn wait_until_ready(&self) -> Result<i32> {
- let (state, result) = self
- .state_ready
- .wait_timeout_while(
- self.mutex.lock().unwrap(),
- timeouts()?.vm_max_time_to_ready,
- |state| state.cid.is_none() && !state.has_died,
- )
- .unwrap();
- if result.timed_out() {
- bail!("Timed out waiting for VM")
- }
- state.cid.ok_or_else(|| anyhow!("VM died"))
- }
-}
-
-#[derive(Debug)]
-struct VmCallback(Arc<VmStateMonitor>);
+struct VmCallback();
impl Interface for VmCallback {}
impl IVirtualMachineCallback for VmCallback {
fn onDied(&self, cid: i32, reason: DeathReason) -> BinderResult<()> {
- self.0.set_died();
log::warn!("VM died, cid = {}, reason = {:?}", cid, reason);
Ok(())
}
@@ -375,21 +287,16 @@
}
fn onPayloadReady(&self, cid: i32) -> BinderResult<()> {
- self.0.set_ready(cid);
log::info!("VM payload ready, cid = {}", cid);
Ok(())
}
fn onPayloadFinished(&self, cid: i32, exit_code: i32) -> BinderResult<()> {
- // This should probably never happen in our case, but if it does we means our VM is no
- // longer running
- self.0.set_died();
log::warn!("VM payload finished, cid = {}, exit code = {}", cid, exit_code);
Ok(())
}
fn onError(&self, cid: i32, error_code: i32, message: &str) -> BinderResult<()> {
- self.0.set_died();
log::warn!("VM error, cid = {}, error code = {}, message = {}", cid, error_code, message,);
Ok(())
}
diff --git a/compos/composd/Android.bp b/compos/composd/Android.bp
index 55a3107..3a6119f 100644
--- a/compos/composd/Android.bp
+++ b/compos/composd/Android.bp
@@ -24,6 +24,7 @@
"liblog_rust",
"librustutils",
"libshared_child",
+ "libvmclient",
],
apex_available: [
"com.android.compos",
diff --git a/compos/composd/src/composd_main.rs b/compos/composd/src/composd_main.rs
index d1b711d..ebcd689 100644
--- a/compos/composd/src/composd_main.rs
+++ b/compos/composd/src/composd_main.rs
@@ -27,7 +27,6 @@
use crate::instance_manager::InstanceManager;
use android_system_composd::binder::{register_lazy_service, ProcessState};
use anyhow::{Context, Result};
-use compos_common::compos_client::VmInstance;
use log::{error, info};
use std::panic;
use std::sync::Arc;
@@ -46,7 +45,8 @@
ProcessState::start_thread_pool();
- let virtualization_service = VmInstance::connect_to_virtualization_service()?;
+ let virtualization_service =
+ vmclient::connect().context("Failed to find VirtualizationService")?;
let instance_manager = Arc::new(InstanceManager::new(virtualization_service));
let composd_service = service::new_binder(instance_manager);
register_lazy_service("android.system.composd", composd_service.as_binder())
diff --git a/compos/composd/src/instance_starter.rs b/compos/composd/src/instance_starter.rs
index f899497..340e8b7 100644
--- a/compos/composd/src/instance_starter.rs
+++ b/compos/composd/src/instance_starter.rs
@@ -24,7 +24,7 @@
use binder_common::lazy_service::LazyServiceGuard;
use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
use compos_aidl_interface::binder::{ParcelFileDescriptor, Strong};
-use compos_common::compos_client::{VmInstance, VmParameters};
+use compos_common::compos_client::{ComposClient, VmParameters};
use compos_common::{COMPOS_DATA_ROOT, IDSIG_FILE, IDSIG_MANIFEST_APK_FILE, INSTANCE_IMAGE_FILE};
use log::info;
use std::fs;
@@ -33,7 +33,7 @@
pub struct CompOsInstance {
service: Strong<dyn ICompOsService>,
#[allow(dead_code)] // Keeps VirtualizationService & the VM alive
- vm_instance: VmInstance,
+ vm_instance: ComposClient,
#[allow(dead_code)] // Keeps composd process alive
lazy_service_guard: LazyServiceGuard,
}
@@ -105,7 +105,7 @@
.write(true)
.open(&self.instance_image)
.context("Failed to open instance image")?;
- let vm_instance = VmInstance::start(
+ let vm_instance = ComposClient::start(
virtualization_service,
instance_image,
&self.idsig,
diff --git a/compos/verify/Android.bp b/compos/verify/Android.bp
index d6875d1..5c74e4f 100644
--- a/compos/verify/Android.bp
+++ b/compos/verify/Android.bp
@@ -15,6 +15,7 @@
"libcompos_common",
"libcompos_verify_native_rust",
"liblog_rust",
+ "libvmclient",
],
prefer_rlib: true,
apex_available: [
diff --git a/compos/verify/verify.rs b/compos/verify/verify.rs
index 14ce798..7a22cfd 100644
--- a/compos/verify/verify.rs
+++ b/compos/verify/verify.rs
@@ -20,7 +20,7 @@
use android_logger::LogId;
use anyhow::{bail, Context, Result};
use compos_aidl_interface::binder::ProcessState;
-use compos_common::compos_client::{VmInstance, VmParameters};
+use compos_common::compos_client::{ComposClient, VmParameters};
use compos_common::odrefresh::{
CURRENT_ARTIFACTS_SUBDIR, ODREFRESH_OUTPUT_ROOT_DIR, PENDING_ARTIFACTS_SUBDIR,
TEST_ARTIFACTS_SUBDIR,
@@ -98,8 +98,8 @@
// We need to start the thread pool to be able to receive Binder callbacks
ProcessState::start_thread_pool();
- let virtualization_service = VmInstance::connect_to_virtualization_service()?;
- let vm_instance = VmInstance::start(
+ let virtualization_service = vmclient::connect()?;
+ let vm_instance = ComposClient::start(
&*virtualization_service,
instance_image,
&idsig,