ndk: Add way to manage ownership of linkToDeath cookie
Bug: 181971563
Bug: 197721058
Test: m, modified existing test
Change-Id: Ib9b4ab41fd7929f9ba56e112c8a5b58966040771
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index b03ed49..e1d01ef 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -31,8 +31,10 @@
use std::convert::TryInto;
use std::ffi::{c_void, CString};
use std::fmt;
+use std::mem;
use std::os::unix::io::AsRawFd;
use std::ptr;
+use std::sync::Arc;
/// A strong reference to a Binder remote object.
///
@@ -378,13 +380,17 @@
// Safety: `SpIBinder` guarantees that `self` always contains a
// valid pointer to an `AIBinder`. `recipient` can always be
// converted into a valid pointer to an
- // `AIBinder_DeathRecipient`. Any value is safe to pass as the
- // cookie, although we depend on this value being set by
- // `get_cookie` when the death recipient callback is called.
+ // `AIBinder_DeathRecipient`.
+ //
+ // The cookie is also the correct pointer, and by calling new_cookie,
+ // we have created a new ref-count to the cookie, which linkToDeath
+ // takes ownership of. Once the DeathRecipient is unlinked for any
+ // reason (including if this call fails), the onUnlinked callback
+ // will consume that ref-count.
sys::AIBinder_linkToDeath(
self.as_native_mut(),
recipient.as_native_mut(),
- recipient.get_cookie(),
+ recipient.new_cookie(),
)
})
}
@@ -552,10 +558,20 @@
}
/// Rust wrapper around DeathRecipient objects.
+///
+/// The cookie in this struct represents an Arc<F> for the owned callback.
+/// This struct owns a ref-count of it, and so does every binder that we
+/// have been linked with.
#[repr(C)]
pub struct DeathRecipient {
recipient: *mut sys::AIBinder_DeathRecipient,
- callback: Box<dyn Fn() + Send + 'static>,
+ cookie: *mut c_void,
+ vtable: &'static DeathRecipientVtable,
+}
+
+struct DeathRecipientVtable {
+ cookie_incr_refcount: unsafe extern "C" fn(*mut c_void),
+ cookie_decr_refcount: unsafe extern "C" fn(*mut c_void),
}
impl DeathRecipient {
@@ -563,9 +579,9 @@
/// associated object dies.
pub fn new<F>(callback: F) -> DeathRecipient
where
- F: Fn() + Send + 'static,
+ F: Fn() + Send + Sync + 'static,
{
- let callback = Box::new(callback);
+ let callback: *const F = Arc::into_raw(Arc::new(callback));
let recipient = unsafe {
// Safety: The function pointer is a valid death recipient callback.
//
@@ -574,34 +590,85 @@
// no longer needed.
sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::<F>))
};
+ unsafe {
+ // Safety: The function pointer is a valid onUnlinked callback.
+ //
+ // All uses of linkToDeath in this file correctly increment the
+ // ref-count that this onUnlinked callback will decrement.
+ sys::AIBinder_DeathRecipient_setOnUnlinked(recipient, Some(Self::cookie_decr_refcount::<F>));
+ }
DeathRecipient {
recipient,
- callback,
+ cookie: callback as *mut c_void,
+ vtable: &DeathRecipientVtable {
+ cookie_incr_refcount: Self::cookie_incr_refcount::<F>,
+ cookie_decr_refcount: Self::cookie_decr_refcount::<F>,
+ },
}
}
+ /// Increment the ref-count for the cookie and return it.
+ ///
+ /// # Safety
+ ///
+ /// The caller must handle the returned ref-count correctly.
+ unsafe fn new_cookie(&self) -> *mut c_void {
+ (self.vtable.cookie_incr_refcount)(self.cookie);
+
+ // Return a raw pointer with ownership of a ref-count
+ self.cookie
+ }
+
/// Get the opaque cookie that identifies this death recipient.
///
/// This cookie will be used to link and unlink this death recipient to a
/// binder object and will be passed to the `binder_died` callback as an
/// opaque userdata pointer.
fn get_cookie(&self) -> *mut c_void {
- &*self.callback as *const _ as *mut c_void
+ self.cookie
}
/// Callback invoked from C++ when the binder object dies.
///
/// # Safety
///
- /// The `cookie` parameter must have been created with the `get_cookie`
- /// method of this object.
+ /// The `cookie` parameter must be the cookie for an Arc<F> and
+ /// the caller must hold a ref-count to it.
unsafe extern "C" fn binder_died<F>(cookie: *mut c_void)
where
- F: Fn() + Send + 'static,
+ F: Fn() + Send + Sync + 'static,
{
- let callback = (cookie as *mut F).as_ref().unwrap();
+ let callback = (cookie as *const F).as_ref().unwrap();
callback();
}
+
+ /// Callback that decrements the ref-count.
+ /// This is invoked from C++ when a binder is unlinked.
+ ///
+ /// # Safety
+ ///
+ /// The `cookie` parameter must be the cookie for an Arc<F> and
+ /// the owner must give up a ref-count to it.
+ unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
+ where
+ F: Fn() + Send + Sync + 'static,
+ {
+ drop(Arc::from_raw(cookie as *const F));
+ }
+
+ /// Callback that increments the ref-count.
+ ///
+ /// # Safety
+ ///
+ /// The `cookie` parameter must be the cookie for an Arc<F> and
+ /// the owner must handle the created ref-count properly.
+ unsafe extern "C" fn cookie_incr_refcount<F>(cookie: *mut c_void)
+ where
+ F: Fn() + Send + Sync + 'static,
+ {
+ let arc = mem::ManuallyDrop::new(Arc::from_raw(cookie as *const F));
+ mem::forget(Arc::clone(&arc));
+ }
}
/// # Safety
@@ -627,6 +694,12 @@
// `AIBinder_DeathRecipient_new` when `self` was created. This
// delete method can only be called once when `self` is dropped.
sys::AIBinder_DeathRecipient_delete(self.recipient);
+
+ // Safety: We own a ref-count to the cookie, and so does every
+ // linked binder. This call gives up our ref-count. The linked
+ // binders should already have given up their ref-count, or should
+ // do so shortly.
+ (self.vtable.cookie_decr_refcount)(self.cookie)
}
}
}