libavf: Add callback support
Test: atest vts_libavf_test
Bug: 381195543
Change-Id: I5f3920b7fd223ad50088cdbc429aca19e3717121
diff --git a/libs/libavf/include/android/virtualization.h b/libs/libavf/include/android/virtualization.h
index ef57325..03d04a9 100644
--- a/libs/libavf/include/android/virtualization.h
+++ b/libs/libavf/include/android/virtualization.h
@@ -337,17 +337,30 @@
int AVirtualMachine_createRaw(const AVirtualizationService* _Nonnull service,
AVirtualMachineRawConfig* _Nonnull config, int consoleOutFd,
int consoleInFd, int logFd,
- AVirtualMachine* _Null_unspecified* _Nonnull vm) __INTRODUCED_IN(36);
+ const AVirtualMachine* _Null_unspecified* _Nonnull vm)
+ __INTRODUCED_IN(36);
+
+/**
+ * A callback to be called when virtual machine stops.
+ *
+ * \param vm stopped vm
+ * \param reason stop reason
+ */
+typedef void (*AVirtualMachine_stopCallback)(const AVirtualMachine* _Nonnull vm,
+ enum AVirtualMachineStopReason reason);
/**
* Start a virtual machine. `AVirtualMachine_start` is synchronous and blocks until the virtual
* machine is initialized and free to start executing code, or until an error happens.
*
* \param vm a handle on a virtual machine.
+ * \param callback an optional callback to be called when VM stops.
*
* \return If successful, it returns 0. Otherwise, it returns `-EIO`.
*/
-int AVirtualMachine_start(AVirtualMachine* _Nonnull vm) __INTRODUCED_IN(36);
+int AVirtualMachine_start(const AVirtualMachine* _Nonnull vm,
+ const AVirtualMachine_stopCallback _Nullable callback)
+ __INTRODUCED_IN(36);
/**
* Stop a virtual machine. Stopping a virtual machine is like pulling the plug on a real computer;
@@ -366,7 +379,7 @@
*
* \return If successful, it returns 0. Otherwise, it returns `-EIO`.
*/
-int AVirtualMachine_stop(AVirtualMachine* _Nonnull vm) __INTRODUCED_IN(36);
+int AVirtualMachine_stop(const AVirtualMachine* _Nonnull vm) __INTRODUCED_IN(36);
/**
* Open a vsock connection to the VM on the given port. The caller takes ownership of the returned
@@ -379,7 +392,8 @@
*
* \return If successful, it returns a valid file descriptor. Otherwise, it returns `-EIO`.
*/
-int AVirtualMachine_connectVsock(AVirtualMachine* _Nonnull vm, uint32_t port) __INTRODUCED_IN(36);
+int AVirtualMachine_connectVsock(const AVirtualMachine* _Nonnull vm, uint32_t port)
+ __INTRODUCED_IN(36);
/**
* Wait until a virtual machine stops or the given timeout elapses.
@@ -393,7 +407,7 @@
* sets `reason` and returns true.
* - If the timeout expired, it returns `false`.
*/
-bool AVirtualMachine_waitForStop(AVirtualMachine* _Nonnull vm,
+bool AVirtualMachine_waitForStop(const AVirtualMachine* _Nonnull vm,
const struct timespec* _Nullable timeout,
enum AVirtualMachineStopReason* _Nonnull reason)
__INTRODUCED_IN(36);
@@ -408,6 +422,6 @@
*
* \param vm a handle on a virtual machine.
*/
-void AVirtualMachine_destroy(AVirtualMachine* _Nullable vm) __INTRODUCED_IN(36);
+void AVirtualMachine_destroy(const AVirtualMachine* _Nullable vm) __INTRODUCED_IN(36);
__END_DECLS
diff --git a/libs/libavf/src/lib.rs b/libs/libavf/src/lib.rs
index 50c5e2e..256803f 100644
--- a/libs/libavf/src/lib.rs
+++ b/libs/libavf/src/lib.rs
@@ -19,6 +19,7 @@
use std::os::fd::{FromRawFd, IntoRawFd};
use std::os::raw::{c_char, c_int};
use std::ptr;
+use std::sync::Arc;
use std::time::Duration;
use android_system_virtualizationservice::{
@@ -29,10 +30,10 @@
},
binder::{ParcelFileDescriptor, Strong},
};
-use avf_bindgen::AVirtualMachineStopReason;
+use avf_bindgen::{AVirtualMachineStopReason, AVirtualMachine_stopCallback};
use libc::timespec;
use log::error;
-use vmclient::{DeathReason, VirtualizationService, VmInstance};
+use vmclient::{DeathReason, ErrorCode, VirtualizationService, VmCallback, VmInstance};
/// Create a new virtual machine config object with no properties.
#[no_mangle]
@@ -342,6 +343,49 @@
}
}
+struct LocalVmInstance {
+ vm: Arc<VmInstance>,
+ callback: AVirtualMachine_stopCallback,
+}
+
+impl VmCallback for LocalVmInstance {
+ fn on_payload_started(&self, _cid: i32) {
+ // Microdroid only. no-op.
+ }
+
+ fn on_payload_ready(&self, _cid: i32) {
+ // Microdroid only. no-op.
+ }
+
+ fn on_payload_finished(&self, _cid: i32, _exit_code: i32) {
+ // Microdroid only. no-op.
+ }
+
+ fn on_error(&self, _cid: i32, _error_code: ErrorCode, _message: &str) {
+ // Microdroid only. no-op.
+ }
+
+ fn on_died(&self, _cid: i32, death_reason: DeathReason) {
+ let Some(callback) = self.callback else {
+ return;
+ };
+ let stop_reason = death_reason_to_stop_reason(death_reason);
+ let vm_ptr: *const VmInstance = Arc::into_raw(Arc::clone(&self.vm));
+
+ // SAFETY: `callback` is assumed to be a valid, non-null function pointer passed by
+ // `AVirtualMachine_start`.
+ unsafe {
+ callback(vm_ptr.cast(), stop_reason);
+ }
+
+ // drop ptr after use.
+ // SAFETY: `vm_ptr` is a valid, non-null pointer casted above.
+ unsafe {
+ let _ = Arc::from_raw(vm_ptr);
+ }
+ }
+}
+
/// Create a virtual machine with given `config`.
///
/// # Safety
@@ -357,7 +401,7 @@
console_out_fd: c_int,
console_in_fd: c_int,
log_fd: c_int,
- vm_ptr: *mut *mut VmInstance,
+ vm_ptr: *mut *const VmInstance,
) -> c_int {
// SAFETY: `service` is assumed to be a valid, non-null pointer returned by
// `AVirtualizationService_create` or `AVirtualizationService_create_early`. It's the only
@@ -376,9 +420,8 @@
match VmInstance::create(service.as_ref(), &config, console_out, console_in, log, None) {
Ok(vm) => {
// SAFETY: `vm_ptr` is assumed to be a valid, non-null pointer to a mutable raw pointer.
- // `vm` is the only reference here and `vm_ptr` takes ownership.
unsafe {
- *vm_ptr = Box::into_raw(Box::new(vm));
+ *vm_ptr = Arc::into_raw(Arc::new(vm));
}
0
}
@@ -394,11 +437,20 @@
/// # Safety
/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`.
#[no_mangle]
-pub unsafe extern "C" fn AVirtualMachine_start(vm: *const VmInstance) -> c_int {
+pub unsafe extern "C" fn AVirtualMachine_start(
+ vm: *const VmInstance,
+ callback: AVirtualMachine_stopCallback,
+) -> c_int {
// SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
// `AVirtualMachine_createRaw`. It's the only reference to the object.
- let vm = unsafe { &*vm };
- match vm.start(None) {
+ let vm = unsafe { Arc::from_raw(vm) };
+ let callback = callback.map(|_| {
+ let cb: Box<dyn VmCallback + Send + Sync> =
+ Box::new(LocalVmInstance { vm: Arc::clone(&vm), callback });
+ cb
+ });
+
+ match vm.start(callback) {
Ok(_) => 0,
Err(e) => {
error!("AVirtualMachine_start failed: {e:?}");
@@ -509,12 +561,12 @@
/// `vm` must be a pointer returned by `AVirtualMachine_createRaw`. `vm` must not be reused after
/// deletion.
#[no_mangle]
-pub unsafe extern "C" fn AVirtualMachine_destroy(vm: *mut VmInstance) {
+pub unsafe extern "C" fn AVirtualMachine_destroy(vm: *const VmInstance) {
if !vm.is_null() {
// SAFETY: `vm` is assumed to be a valid, non-null pointer returned by
// AVirtualMachine_create. It's the only reference to the object.
unsafe {
- let _ = Box::from_raw(vm);
+ let _ = Arc::from_raw(vm);
}
}
}