Wait for crosvm in a separate thread, and keep track of when it dies.
This lets us tell whether a VM is still running or has finished. Also
add callback so clients can get notified when crosvm dies.
Bug: 171277638
Test: Ran on VIM3L
Test: atest VirtualizationTestCases
Change-Id: I52c1625af45cfcfe7aa0be465ea08f427ec5bc43
diff --git a/vm/src/main.rs b/vm/src/main.rs
index f47d9e8..061cfd7 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -17,9 +17,14 @@
mod sync;
use android_system_virtmanager::aidl::android::system::virtmanager::IVirtManager::IVirtManager;
+use android_system_virtmanager::aidl::android::system::virtmanager::IVirtualMachine::IVirtualMachine;
+use android_system_virtmanager::aidl::android::system::virtmanager::IVirtualMachineCallback::{
+ BnVirtualMachineCallback, IVirtualMachineCallback,
+};
use android_system_virtmanager::binder::{
get_interface, DeathRecipient, IBinder, ParcelFileDescriptor, ProcessState, Strong,
};
+use android_system_virtmanager::binder::{Interface, Result as BinderResult};
use anyhow::{Context, Error};
use std::fs::File;
use std::io;
@@ -91,14 +96,26 @@
// Pass the VM reference back to Virt Manager and have it hold it in the background.
virt_manager.debugHoldVmRef(&vm).context("Failed to pass VM to Virt Manager")
} else {
- // Wait until the VM dies. If we just returned immediately then the IVirtualMachine Binder
- // object would be dropped and the VM would be killed.
- wait_for_death(&mut vm.as_binder())?;
- println!("VM died");
- Ok(())
+ // Wait until the VM or VirtManager dies. If we just returned immediately then the
+ // IVirtualMachine Binder object would be dropped and the VM would be killed.
+ wait_for_vm(vm)
}
}
+/// Wait until the given VM or the VirtManager itself dies.
+fn wait_for_vm(vm: Strong<dyn IVirtualMachine>) -> Result<(), Error> {
+ let dead = AtomicFlag::default();
+ let callback =
+ BnVirtualMachineCallback::new_binder(VirtualMachineCallback { dead: dead.clone() });
+ vm.registerCallback(&callback)?;
+ let death_recipient = wait_for_death(&mut vm.as_binder(), dead.clone())?;
+ dead.wait();
+ // Ensure that death_recipient isn't dropped before we wait on the flag, as it is removed
+ // from the Binder when it's dropped.
+ drop(death_recipient);
+ Ok(())
+}
+
/// Retrieve reference to a previously daemonized VM and stop it.
fn command_stop(virt_manager: Strong<dyn IVirtManager>, cid: u32) -> Result<(), Error> {
virt_manager
@@ -115,18 +132,31 @@
Ok(())
}
-/// Block until the given Binder object dies.
-fn wait_for_death(binder: &mut impl IBinder) -> Result<(), Error> {
- let dead = AtomicFlag::default();
- let mut death_recipient = {
- let dead = dead.clone();
- DeathRecipient::new(move || {
- dead.raise();
- })
- };
+/// Raise the given flag when the given Binder object dies.
+///
+/// If the returned DeathRecipient is dropped then this will no longer do anything.
+fn wait_for_death(binder: &mut impl IBinder, dead: AtomicFlag) -> Result<DeathRecipient, Error> {
+ let mut death_recipient = DeathRecipient::new(move || {
+ println!("VirtManager died");
+ dead.raise();
+ });
binder.link_to_death(&mut death_recipient)?;
- dead.wait();
- Ok(())
+ Ok(death_recipient)
+}
+
+#[derive(Debug)]
+struct VirtualMachineCallback {
+ dead: AtomicFlag,
+}
+
+impl Interface for VirtualMachineCallback {}
+
+impl IVirtualMachineCallback for VirtualMachineCallback {
+ fn onDied(&self, _cid: i32) -> BinderResult<()> {
+ println!("VM died");
+ self.dead.raise();
+ Ok(())
+ }
}
/// Safely duplicate the standard output file descriptor.