use std::any::Any;
use std::mem::ManuallyDrop;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{self, Arc, OnceLock, RwLock};
use crate::binder::{DeathRecipient, IBinder, SIBinder, Stability, Transactable, WIBinder};
use crate::binder::{TransactionCode, TransactionFlags};
use crate::error::{Result, StatusCode};
use crate::parcel::Parcel;
use super::address::RpcAddress;
use super::session::RpcSessionInner;
pub struct RpcProxy {
addr: RpcAddress,
descriptor: OnceLock<String>,
session: Arc<RpcSessionInner>,
obituary_sent: AtomicBool,
recipients: RwLock<Vec<sync::Weak<dyn DeathRecipient>>>,
}
impl RpcProxy {
pub(crate) fn session_ptr(&self) -> *const RpcSessionInner {
Arc::as_ptr(&self.session)
}
pub(crate) fn new(addr: RpcAddress, session: Arc<RpcSessionInner>) -> Self {
RpcProxy {
addr,
descriptor: OnceLock::new(),
session,
obituary_sent: AtomicBool::new(false),
recipients: RwLock::new(Vec::new()),
}
}
pub(crate) fn send_obituary(&self, who: &WIBinder) {
let snapshot: Vec<sync::Weak<dyn DeathRecipient>> = {
let mut recipients = match self.recipients.write() {
Ok(g) => g,
Err(_) => {
log::error!(
"RPC obituary skipped: recipients lock poisoned for addr {:?}",
self.addr
);
return;
}
};
if self.obituary_sent.load(Ordering::Relaxed) {
return;
}
let snapshot = std::mem::take(&mut *recipients);
self.obituary_sent.store(true, Ordering::Relaxed);
snapshot
};
for weak in &snapshot {
let Some(recipient) = weak.upgrade() else {
continue;
};
let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
recipient.binder_died(who);
}));
if let Err(payload) = r {
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 RPC addr {:?}: {msg}",
self.addr
);
}
}
}
pub fn address(&self) -> RpcAddress {
self.addr
}
pub(crate) fn stamp_descriptor(&self, descriptor: &str) -> bool {
self.descriptor.set(descriptor.to_string()).is_ok()
}
fn descriptor_str(&self) -> &str {
self.descriptor.get().map(String::as_str).unwrap_or("")
}
pub fn build_request(&self, descriptor: &str) -> Result<Parcel> {
let inner = &self.session;
let mut data = Parcel::new();
data.configure_rpc(
inner.parcel_ops(),
inner.fd_mode(),
inner.records_fd_positions(),
);
super::session::write_rpc_interface_token(&mut data, descriptor)?;
Ok(data)
}
pub fn transact(
&self,
code: TransactionCode,
data: &Parcel,
flags: TransactionFlags,
) -> Result<Option<Parcel>> {
self.session.client_transact(self.addr, code, data, flags)
}
}
impl crate::binder::RemoteProxy for RpcProxy {
fn prepare_transact(&self, write_header: bool) -> Result<Parcel> {
let inner = &self.session;
let mut data = Parcel::new();
data.configure_rpc(
inner.parcel_ops(),
inner.fd_mode(),
inner.records_fd_positions(),
);
if write_header {
super::session::write_rpc_interface_token(&mut data, self.descriptor_str())?;
}
Ok(data)
}
fn submit_transact(
&self,
code: TransactionCode,
data: &Parcel,
flags: TransactionFlags,
) -> Result<Option<Parcel>> {
RpcProxy::transact(self, code, data, flags)
}
}
impl Drop for RpcProxy {
fn drop(&mut self) {
let inner = &self.session;
inner.queue_dec_strong(self.addr);
inner.forget_remote_if(&self.addr, self as *const RpcProxy as *const ());
}
}
impl IBinder for RpcProxy {
fn link_to_death(&self, recipient: sync::Weak<dyn DeathRecipient>) -> Result<()> {
let mut recipients = self.recipients.write().map_err(|_| {
StatusCode::DeadObject
})?;
if self.obituary_sent.load(Ordering::Relaxed) {
return Err(StatusCode::DeadObject);
}
recipients.push(recipient);
Ok(())
}
fn unlink_to_death(&self, recipient: sync::Weak<dyn DeathRecipient>) -> Result<()> {
let mut recipients = self.recipients.write().map_err(|_| {
StatusCode::DeadObject
})?;
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);
Ok(())
}
fn ping_binder(&self) -> Result<()> {
let data = Parcel::new();
self.session
.client_transact(self.addr, crate::binder::PING_TRANSACTION, &data, 0)?;
Ok(())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_transactable(&self) -> Option<&dyn Transactable> {
None
}
fn descriptor(&self) -> &str {
self.descriptor_str()
}
fn is_remote(&self) -> bool {
true
}
fn inc_strong(&self, _strong: &SIBinder) -> Result<()> {
Ok(())
}
fn attempt_inc_strong(&self) -> bool {
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(())
}
fn stability(&self) -> Stability {
Stability::default()
}
}