use std::any::Any;
use std::fmt::{Debug, Formatter};
use std::mem::ManuallyDrop;
use std::os::fd::OwnedFd;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{self, Arc, RwLock};
use crate::process_state::{HandlePin, ProcessState};
use crate::{binder::*, error::*, parcel::*, parcelable::DeserializeOption, thread_state};
enum ExtensionCache {
NotQueried,
Queried(Option<CachedExtension>),
}
enum CachedExtension {
Strong(SIBinder),
Weak(WIBinder),
}
pub struct ProxyHandle {
handle: u32,
pin: Arc<HandlePin>,
descriptor: String,
stability: Stability,
obituary_sent: AtomicBool,
recipients: RwLock<Vec<sync::Weak<dyn DeathRecipient>>>,
extension: RwLock<ExtensionCache>,
freeze_used: AtomicBool,
me: sync::Weak<ProxyHandle>,
}
impl ProxyHandle {
pub(crate) fn new_acquired(
pin: &Arc<HandlePin>,
descriptor: String,
stability: Stability,
) -> Result<Arc<Self>> {
let handle = pin.handle();
thread_state::inc_strong_handle(handle)?;
Ok(Arc::new_cyclic(|me| Self {
handle,
pin: Arc::clone(pin),
descriptor,
stability,
obituary_sent: AtomicBool::new(false),
recipients: RwLock::new(Vec::new()),
extension: RwLock::new(ExtensionCache::NotQueried),
freeze_used: AtomicBool::new(false),
me: me.clone(),
}))
}
fn owner_key(&self) -> usize {
self as *const Self as usize
}
pub fn handle(&self) -> u32 {
self.handle
}
pub(crate) fn generation(&self) -> u64 {
self.pin.generation()
}
pub(crate) fn pin(&self) -> &Arc<HandlePin> {
&self.pin
}
pub fn descriptor(&self) -> &str {
&self.descriptor
}
fn classify_extension(&self, sib: &SIBinder) -> CachedExtension {
if let Some(proxy) = (**sib).as_proxy() {
if proxy.handle() == self.handle {
return CachedExtension::Weak(SIBinder::downgrade(sib));
}
}
CachedExtension::Strong(sib.clone())
}
pub fn submit_transact(
&self,
code: TransactionCode,
data: &Parcel,
flags: TransactionFlags,
) -> Result<Option<Parcel>> {
if self.obituary_sent.load(Ordering::Acquire) {
return Err(StatusCode::DeadObject);
}
thread_state::transact(self.handle(), code, data, flags)
}
pub fn prepare_transact(&self, write_header: bool) -> Result<Parcel> {
let mut data = Parcel::new();
if write_header {
data.write_interface_token(self.descriptor())?;
}
Ok(data)
}
}
impl RemoteProxy for ProxyHandle {
fn prepare_transact(&self, write_header: bool) -> Result<Parcel> {
ProxyHandle::prepare_transact(self, write_header)
}
fn submit_transact(
&self,
code: TransactionCode,
data: &Parcel,
flags: TransactionFlags,
) -> Result<Option<Parcel>> {
ProxyHandle::submit_transact(self, code, data, flags)
}
}
impl ProxyHandle {
pub(crate) fn send_obituary(&self, who: &WIBinder) -> Result<()> {
let recipients_snapshot: Vec<sync::Weak<dyn DeathRecipient>> = {
let mut recipients = self.recipients.write().expect("Recipients lock poisoned");
if self.obituary_sent.load(Ordering::Relaxed) {
return Ok(());
}
if !recipients.is_empty() {
let unlinked = ProcessState::as_self().unlink_death(self.handle, self.owner_key());
if let Err(e) = unlinked {
log::error!(
"clear_death_notification failed for handle {}: {e:?}; \
delivering the obituary anyway",
self.handle()
);
}
}
let snapshot = std::mem::take(&mut *recipients);
self.obituary_sent.store(true, Ordering::Release);
snapshot
};
self.dispatch_obituary_callbacks(&recipients_snapshot, who);
if !recipients_snapshot.is_empty() {
thread_state::flush_commands()?;
}
Ok(())
}
fn dispatch_obituary_callbacks(
&self,
snapshot: &[sync::Weak<dyn DeathRecipient>],
who: &WIBinder,
) {
for weak in snapshot {
let Some(recipient) = weak.upgrade() else {
continue;
};
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
recipient.binder_died(who);
}));
if let Err(payload) = result {
let msg = payload
.downcast_ref::<&'static str>()
.copied()
.or_else(|| payload.downcast_ref::<String>().map(String::as_str))
.unwrap_or("<non-string panic payload>");
log::error!(
"DeathRecipient panicked during binder_died for handle {:X}: {msg}",
self.handle,
);
}
}
}
pub fn dump<F: Into<OwnedFd>>(&self, fd: F, args: &[String]) -> Result<()> {
if self.obituary_sent.load(Ordering::Acquire) {
return Err(StatusCode::DeadObject);
}
let mut send = Parcel::new();
send.write_kernel_fd(fd.into())?;
send.write::<i32>(&(args.len() as i32))?;
for arg in args {
send.write(arg)?;
}
self.submit_transact(DUMP_TRANSACTION, &send, FLAG_CLEAR_BUF)?;
Ok(())
}
}
impl Debug for ProxyHandle {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ProxyHandle")
.field("handle", &self.handle)
.field("descriptor", &self.descriptor)
.field("stability", &self.stability)
.field("obituary_sent", &self.obituary_sent)
.finish()
}
}
impl PartialEq for ProxyHandle {
fn eq(&self, other: &Self) -> bool {
self.handle() == other.handle() && self.generation() == other.generation()
}
}
impl Eq for ProxyHandle {}
impl Drop for ProxyHandle {
fn drop(&mut self) {
let linked = !self
.recipients
.get_mut()
.unwrap_or_else(|poisoned| poisoned.into_inner())
.is_empty();
if linked {
let unlinked = ProcessState::as_self().unlink_death(self.handle, self.owner_key());
if let Err(err) = unlinked {
log::error!(
"BC_CLEAR_DEATH_NOTIFICATION for handle {} failed during Drop: {err:?}",
self.handle
);
}
}
if *self.freeze_used.get_mut() {
ProcessState::as_self().drop_frozen_owner(self.handle, self.owner_key());
}
if let Err(err) = thread_state::dec_strong_handle(self.handle) {
log::error!(
"BC_RELEASE for handle {} failed during Drop: {err:?}",
self.handle
);
}
}
}
impl IBinder for ProxyHandle {
fn get_extension(&self) -> Result<Option<SIBinder>> {
{
let cached = self.extension.read().expect("Extension lock poisoned");
match &*cached {
ExtensionCache::NotQueried => {}
ExtensionCache::Queried(None) => return Ok(None),
ExtensionCache::Queried(Some(CachedExtension::Strong(s))) => {
return Ok(Some(s.clone()));
}
ExtensionCache::Queried(Some(CachedExtension::Weak(w))) => {
if let Ok(strong) = w.upgrade() {
return Ok(Some(strong));
}
}
}
}
let data = Parcel::new();
let ext: Option<SIBinder> = match self.submit_transact(EXTENSION_TRANSACTION, &data, 0) {
Ok(Some(mut reply)) => DeserializeOption::deserialize_option(&mut reply)?,
Ok(None) => None,
Err(StatusCode::UnknownTransaction) => None,
Err(e) => return Err(e),
};
let entry = ext.as_ref().map(|sib| self.classify_extension(sib));
let mut cache = self.extension.write().expect("Extension lock poisoned");
*cache = ExtensionCache::Queried(entry);
Ok(ext)
}
fn set_extension(&self, _extension: &SIBinder) -> Result<()> {
Err(StatusCode::InvalidOperation)
}
fn link_to_death(&self, recipient: sync::Weak<dyn DeathRecipient>) -> Result<()> {
let mut recipients = self.recipients.write().expect("Recipients lock poisoned");
if self.obituary_sent.load(Ordering::Relaxed) {
return Err(StatusCode::DeadObject);
}
if recipient.upgrade().is_none() {
return Err(StatusCode::BadValue);
}
if recipients.is_empty() {
ProcessState::as_self().link_death(self.handle, self.owner_key())?;
}
recipients.push(recipient);
Ok(())
}
fn unlink_to_death(&self, recipient: sync::Weak<dyn DeathRecipient>) -> Result<()> {
let mut recipients = self.recipients.write().expect("Recipients lock poisoned");
if self.obituary_sent.load(Ordering::Relaxed) {
return Err(StatusCode::DeadObject);
}
let Some(i) = recipients
.iter()
.position(|r| sync::Weak::ptr_eq(r, &recipient))
else {
return Err(StatusCode::NameNotFound);
};
recipients.remove(i);
if recipients.is_empty() {
ProcessState::as_self().unlink_death(self.handle, self.owner_key())?;
let _ = thread_state::flush_commands_if_alive();
}
Ok(())
}
fn add_frozen_state_change_callback(
&self,
callback: sync::Weak<dyn FrozenStateChangeCallback>,
) -> Result<()> {
if callback.upgrade().is_none() {
return Err(StatusCode::BadValue);
}
let process = ProcessState::as_self();
if !process.freeze_notification_supported() {
return Err(StatusCode::InvalidOperation);
}
if !process.thread_pool_started() && process.current_threads.load(Ordering::Relaxed) == 0 {
log::error!(
"add_frozen_state_change_callback on {} but no thread serves binder: \
notifications wait for start_thread_pool or join_thread_pool",
self.descriptor
);
}
self.freeze_used.store(true, Ordering::Relaxed);
process.add_frozen_callback(self.handle, self.owner_key(), self.me.clone(), callback)
}
fn remove_frozen_state_change_callback(
&self,
callback: sync::Weak<dyn FrozenStateChangeCallback>,
) -> Result<()> {
if !self.freeze_used.load(Ordering::Relaxed) {
return Err(StatusCode::NameNotFound);
}
ProcessState::as_self().remove_frozen_callback(self.handle, self.owner_key(), &callback)
}
fn ping_binder(&self) -> Result<()> {
thread_state::ping_binder(self.handle())
}
fn stability(&self) -> Stability {
self.stability
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_transactable(&self) -> Option<&dyn Transactable> {
None
}
fn descriptor(&self) -> &str {
self.descriptor()
}
fn is_remote(&self) -> bool {
true
}
fn inc_strong(&self, _strong: &SIBinder) -> Result<()> {
Ok(())
}
fn attempt_inc_strong(&self) -> bool {
log::warn!(
"attempt_inc_strong called on a ProxyHandle (handle {}); \
it is a no-op for proxies — use Weak<I>::upgrade",
self.handle()
);
true
}
fn dec_strong(&self, _strong: Option<ManuallyDrop<SIBinder>>) -> Result<()> {
Ok(())
}
fn inc_weak(&self, _weak: &WIBinder) -> Result<()> {
Ok(())
}
fn dec_weak(&self) -> Result<()> {
Ok(())
}
}
pub trait Proxy: Sized + Interface {
fn descriptor() -> &'static str;
fn from_binder(binder: SIBinder) -> Option<Self>;
}
#[cfg(test)]
mod tests {
use super::*;
fn synthetic_proxy(obituary_sent: bool) -> Arc<ProxyHandle> {
Arc::new_cyclic(|me| ProxyHandle {
handle: 1,
pin: HandlePin::synthetic(1, 1),
descriptor: "test".to_string(),
stability: Stability::Local,
obituary_sent: AtomicBool::new(obituary_sent),
recipients: RwLock::new(Vec::new()),
extension: RwLock::new(ExtensionCache::NotQueried),
freeze_used: AtomicBool::new(false),
me: me.clone(),
})
}
struct NoopRecipient;
impl DeathRecipient for NoopRecipient {
fn binder_died(&self, _who: &WIBinder) {}
}
fn live_recipient_pair() -> (Arc<dyn DeathRecipient>, sync::Weak<dyn DeathRecipient>) {
let arc: Arc<dyn DeathRecipient> = Arc::new(NoopRecipient);
let weak = Arc::downgrade(&arc);
(arc, weak)
}
#[test]
fn proxy_downgrade_keeps_its_identity_without_a_cache_entry() {
let proxy = synthetic_proxy(false);
let strong = SIBinder::from_arc(proxy.clone() as Arc<dyn IBinder>);
let weak = SIBinder::downgrade(&strong);
assert_eq!(weak, SIBinder::downgrade(&strong));
let resurrected = synthetic_proxy(false);
assert!(
!Arc::ptr_eq(&proxy, &resurrected),
"precondition: distinct allocations"
);
let resurrected_strong = SIBinder::from_arc(resurrected.clone() as Arc<dyn IBinder>);
assert_eq!(
weak,
SIBinder::downgrade(&resurrected_strong),
"same (handle, generation) is the same binder, whatever the allocation"
);
std::mem::forget(strong);
std::mem::forget(resurrected_strong);
std::mem::forget(proxy);
std::mem::forget(resurrected);
}
#[test]
fn weak_compares_against_the_strong_it_came_from() {
let proxy = synthetic_proxy(false);
let strong = SIBinder::from_arc(proxy.clone() as Arc<dyn IBinder>);
let who = SIBinder::downgrade(&strong);
assert!(who == strong, "who == stored binder");
assert!(strong == who, "and the operands commute");
struct Other;
impl crate::Interface for Other {}
impl crate::Remotable for Other {
fn descriptor() -> &'static str {
"rsbinder.test.proxy.IOther"
}
fn on_transact(
&self,
_code: crate::TransactionCode,
_reader: &mut crate::Parcel,
_reply: &mut crate::Parcel,
) -> Result<()> {
Err(StatusCode::UnknownTransaction)
}
fn on_dump(&self, _w: &mut dyn std::io::Write, _args: &[String]) -> Result<()> {
Ok(())
}
}
let native = crate::Interface::as_binder(&crate::Binder::new(Other));
assert!(who != native);
std::mem::forget(strong);
std::mem::forget(proxy);
}
#[test]
fn test_proxy_handle_debug() {
let handle = synthetic_proxy(false);
assert_eq!(handle.handle(), 1);
assert_eq!(handle.descriptor(), "test");
assert!(handle.as_transactable().is_none());
assert!(handle.is_remote());
let debug_str = format!("{handle:?}");
assert_eq!(
debug_str,
"ProxyHandle { handle: 1, descriptor: \"test\", stability: Local, obituary_sent: false }"
);
std::mem::forget(handle);
}
#[test]
fn test_submit_transact_fast_fails_when_obituary_sent() {
let handle = synthetic_proxy(true);
let parcel = Parcel::new();
let result = handle.submit_transact(0, &parcel, 0);
assert!(
matches!(result, Err(StatusCode::DeadObject)),
"expected DeadObject, got {result:?}"
);
std::mem::forget(handle);
}
#[test]
fn test_link_to_death_returns_dead_object_after_obituary() {
let handle = synthetic_proxy(true);
let (_arc, weak_recipient) = live_recipient_pair();
let result = handle.link_to_death(weak_recipient);
assert!(
matches!(result, Err(StatusCode::DeadObject)),
"expected DeadObject, got {result:?}"
);
std::mem::forget(handle);
}
#[test]
fn test_unlink_to_death_returns_dead_object_after_obituary() {
let handle = synthetic_proxy(true);
let (_arc, weak_recipient) = live_recipient_pair();
let result = handle.unlink_to_death(weak_recipient);
assert!(
matches!(result, Err(StatusCode::DeadObject)),
"expected DeadObject, got {result:?}"
);
std::mem::forget(handle);
}
#[test]
fn test_unlink_to_death_removes_only_one_match() {
let proxy = synthetic_proxy(false);
let (_arc, weak) = live_recipient_pair();
{
let mut recipients = proxy.recipients.write().expect("recipients lock");
recipients.push(weak.clone());
recipients.push(weak.clone());
}
let result = proxy.unlink_to_death(weak.clone());
assert!(matches!(result, Ok(())), "expected Ok, got {result:?}");
let recipients = proxy.recipients.read().expect("recipients lock");
assert_eq!(
recipients.len(),
1,
"exactly one duplicate must remain (single-position remove)"
);
assert!(
sync::Weak::ptr_eq(&recipients[0], &weak),
"remaining entry must be the same recipient that was registered twice"
);
drop(recipients);
std::mem::forget(proxy);
}
#[test]
fn test_unlink_to_death_empty_list_returns_name_not_found() {
let proxy = synthetic_proxy(false);
let (_arc, weak) = live_recipient_pair();
let result = proxy.unlink_to_death(weak);
assert!(
matches!(result, Err(StatusCode::NameNotFound)),
"expected NameNotFound, got {result:?}"
);
assert!(
proxy.recipients.read().expect("recipients lock").is_empty(),
"recipients vec must remain empty"
);
std::mem::forget(proxy);
}
#[test]
fn test_get_extension_strong_cache_does_not_auto_invalidate_on_dead_extension() {
let ext_proxy = synthetic_proxy(true); let ext_arc_dyn: Arc<dyn IBinder> = ext_proxy.clone();
let ext_sibinder = SIBinder::from_arc(ext_arc_dyn);
let parent = synthetic_proxy(false);
{
let mut cache = parent.extension.write().expect("Extension lock poisoned");
*cache = ExtensionCache::Queried(Some(CachedExtension::Strong(ext_sibinder)));
}
let returned = parent
.get_extension()
.expect("get_extension")
.expect("cache hit should return Some");
let returned_proxy = (*returned).as_proxy().expect("extension is a proxy");
assert_eq!(returned_proxy.handle(), ext_proxy.handle());
let parcel = Parcel::new();
assert!(
matches!(
returned_proxy.submit_transact(0, &parcel, 0),
Err(StatusCode::DeadObject)
),
"calls through cached dead extension must fast-fail with DeadObject"
);
let cache = parent.extension.read().expect("Extension lock poisoned");
assert!(
matches!(
*cache,
ExtensionCache::Queried(Some(CachedExtension::Strong(_)))
),
"cache must remain Strong after get_extension on a dead extension"
);
drop(cache);
drop(returned);
std::mem::forget(parent);
std::mem::forget(ext_proxy);
}
#[test]
fn test_link_to_death_rejects_already_dead_weak() {
let proxy = synthetic_proxy(false);
let arc: Arc<dyn DeathRecipient> = Arc::new(NoopRecipient);
let dead_weak = Arc::downgrade(&arc);
drop(arc);
assert!(
dead_weak.upgrade().is_none(),
"fixture sanity: weak must be dangling"
);
let result = proxy.link_to_death(dead_weak);
assert!(
matches!(result, Err(StatusCode::BadValue)),
"expected BadValue, got {result:?}"
);
assert!(
proxy.recipients.read().expect("recipients lock").is_empty(),
"dead-weak rejection must not push a recipient"
);
std::mem::forget(proxy);
}
#[test]
fn test_set_extension_on_proxy_rejects_with_invalid_operation() {
let proxy = synthetic_proxy(false);
let ext = SIBinder::new(Arc::new(MockBinder)).expect("SIBinder::new");
let result = proxy.set_extension(&ext);
assert!(
matches!(result, Err(StatusCode::InvalidOperation)),
"expected InvalidOperation, got {result:?}"
);
let cache = proxy.extension.read().expect("Extension lock poisoned");
assert!(
matches!(*cache, ExtensionCache::NotQueried),
"extension cache must remain NotQueried after a rejected set_extension"
);
drop(cache);
std::mem::forget(proxy);
}
#[test]
fn test_dump_fast_fails_and_closes_fd_when_obituary_sent() {
let (r, w) = crate::ParcelFileDescriptor::pipe().expect("pipe");
let r: OwnedFd = r.into();
rustix::fs::fcntl_setfl(&r, rustix::fs::OFlags::NONBLOCK).expect("non-blocking");
let proxy = synthetic_proxy(true); let result = proxy.dump(w, &[]);
assert!(
matches!(result, Err(StatusCode::DeadObject)),
"expected DeadObject fast-fail, got {result:?}"
);
assert_eq!(
rustix::io::read(&r, &mut [0u8; 1]),
Ok(0),
"the fast-fail path must close the fd `dump` took"
);
std::mem::forget(proxy);
}
#[test]
fn test_unlink_to_death_unregistered_returns_name_not_found() {
let proxy = synthetic_proxy(false);
let (_a_arc, a_weak) = live_recipient_pair();
let (_b_arc, b_weak) = live_recipient_pair();
{
let mut recipients = proxy.recipients.write().expect("recipients lock");
recipients.push(a_weak.clone());
}
let result = proxy.unlink_to_death(b_weak);
assert!(
matches!(result, Err(StatusCode::NameNotFound)),
"expected NameNotFound, got {result:?}"
);
let recipients = proxy.recipients.read().expect("recipients lock");
assert_eq!(recipients.len(), 1, "registered recipient must survive");
assert!(
sync::Weak::ptr_eq(&recipients[0], &a_weak),
"surviving entry must be the originally registered recipient"
);
drop(recipients);
std::mem::forget(proxy);
}
struct MockBinder;
impl IBinder for MockBinder {
fn link_to_death(&self, _: sync::Weak<dyn DeathRecipient>) -> Result<()> {
Err(StatusCode::InvalidOperation)
}
fn unlink_to_death(&self, _: sync::Weak<dyn DeathRecipient>) -> Result<()> {
Err(StatusCode::InvalidOperation)
}
fn ping_binder(&self) -> Result<()> {
Ok(())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_transactable(&self) -> Option<&dyn Transactable> {
None
}
fn descriptor(&self) -> &str {
"rsbinder.test.MockBinder"
}
fn is_remote(&self) -> bool {
false
}
fn inc_strong(&self, _: &SIBinder) -> Result<()> {
Ok(())
}
fn attempt_inc_strong(&self) -> bool {
true
}
fn dec_strong(&self, _: Option<ManuallyDrop<SIBinder>>) -> Result<()> {
Ok(())
}
fn inc_weak(&self, _: &WIBinder) -> Result<()> {
Ok(())
}
fn dec_weak(&self) -> Result<()> {
Ok(())
}
}
#[test]
fn test_dispatch_obituary_callbacks_isolates_panic() {
use std::sync::Mutex;
struct PanickingRecipient;
impl DeathRecipient for PanickingRecipient {
fn binder_died(&self, _who: &WIBinder) {
panic!("simulated recipient panic");
}
}
struct CountingRecipient {
count: Arc<Mutex<u32>>,
}
impl DeathRecipient for CountingRecipient {
fn binder_died(&self, _who: &WIBinder) {
*self.count.lock().expect("count lock") += 1;
}
}
let panic_arc: Arc<dyn DeathRecipient> = Arc::new(PanickingRecipient);
let count = Arc::new(Mutex::new(0u32));
let counting_arc: Arc<dyn DeathRecipient> = Arc::new(CountingRecipient {
count: count.clone(),
});
let snapshot: Vec<sync::Weak<dyn DeathRecipient>> =
vec![Arc::downgrade(&panic_arc), Arc::downgrade(&counting_arc)];
let mock_strong = SIBinder::new(Arc::new(MockBinder)).expect("SIBinder::new");
let who = SIBinder::downgrade(&mock_strong);
let proxy = synthetic_proxy(false);
proxy.dispatch_obituary_callbacks(&snapshot, &who);
assert_eq!(
*count.lock().expect("count lock"),
1,
"counting recipient must fire after panicking recipient \
(catch_unwind guard regression)"
);
std::mem::forget(proxy);
}
#[test]
fn test_extension_cache_variant_holds_dual_modes() {
let none_cache = ExtensionCache::Queried(None);
let ExtensionCache::Queried(payload) = &none_cache else {
unreachable!("constructed Queried, must match Queried")
};
let _typed: &Option<CachedExtension> = payload;
fn _exhaust(entry: &CachedExtension) -> &'static str {
match entry {
CachedExtension::Strong(_) => "strong",
CachedExtension::Weak(_) => "weak",
}
}
}
}