use super::sys::*;
use crate::CoreError;
use std::os::raw::{c_char, c_void};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, OnceLock};
pub struct ServeCall<'a> {
pub code: u32,
pub calling_uid: u32,
pub calling_pid: i32,
pub request: ParcelReader<'a>,
pub reply: Option<ParcelWriter<'a>>,
}
pub type ServeHandler = Box<dyn for<'a> FnMut(ServeCall<'a>) -> Result<(), CoreError> + Send>;
const DEFAULT_MAX_INFLIGHT: usize = 4;
struct ServeCtx {
vt: Vtable,
handler: Arc<Mutex<ServeHandler>>,
in_flight: AtomicUsize,
max_inflight: usize,
}
impl ServeCtx {
fn try_acquire(&self) -> Option<InFlightGuard<'_>> {
loop {
let cur = self.in_flight.load(Ordering::Acquire);
if cur >= self.max_inflight {
return None;
}
if self
.in_flight
.compare_exchange_weak(cur, cur + 1, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
{
return Some(InFlightGuard {
in_flight: &self.in_flight,
});
}
}
}
}
struct InFlightGuard<'a> {
in_flight: &'a AtomicUsize,
}
impl Drop for InFlightGuard<'_> {
fn drop(&mut self) {
self.in_flight.fetch_sub(1, Ordering::Release);
}
}
unsafe extern "C" fn serve_on_create(args: *mut c_void) -> *mut c_void {
args
}
unsafe extern "C" fn serve_on_destroy(userdata: *mut c_void) {
if !userdata.is_null() {
unsafe { drop(Box::from_raw(userdata as *mut ServeCtx)) };
}
}
unsafe extern "C" fn serve_on_transact(
binder: *mut AIBinder,
code: u32,
in_parcel: *const AParcel,
out_parcel: *mut AParcel,
) -> BinderStatus {
let get_user_data = GET_USER_DATA.lock().unwrap_or_else(|p| p.into_inner());
if let Some(get_user_data) = *get_user_data {
let userdata = unsafe { get_user_data(binder) };
if !userdata.is_null() {
let ctx = unsafe { &*(userdata as *mut ServeCtx) };
let calling_uid = unsafe { (ctx.vt.get_calling_uid)() };
let calling_pid = unsafe { (ctx.vt.get_calling_pid)() };
let request = ParcelReader::borrowed(&ctx.vt, in_parcel);
let reply = if out_parcel.is_null() {
None
} else {
Some(ParcelWriter::borrowed(&ctx.vt, out_parcel))
};
let call = ServeCall {
code,
calling_uid,
calling_pid,
request,
reply,
};
let Some(_permit) = ctx.try_acquire() else {
return STATUS_OUT_OF_RESOURCES;
};
let mut handler = ctx.handler.lock().unwrap_or_else(|p| p.into_inner());
return match std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
(handler)(call)
})) {
Ok(Ok(())) => STATUS_OK,
Ok(Err(_)) => STATUS_UNKNOWN_TRANSACTION,
Err(_) => STATUS_UNKNOWN_TRANSACTION,
};
}
}
STATUS_UNKNOWN_TRANSACTION
}
pub struct ServingBinder {
_lib: DlHandle,
vt: Vtable,
binder: *mut AIBinder,
_class: *mut AIBinder_Class,
}
unsafe impl Send for ServingBinder {}
impl ServingBinder {
pub fn open_bounded(
descriptor: &[u8],
handler: ServeHandler,
max_inflight: usize,
) -> Result<Self, CoreError> {
let handle = unsafe {
libc::dlopen(
LIBBINDER_PATH.as_ptr() as *const c_char,
libc::RTLD_NOW | libc::RTLD_LOCAL,
)
};
if handle.is_null() {
return Err(CoreError::binder(-1, "dlopen:libbinder_ndk.so"));
}
let lib = DlHandle;
let vt = load_vtable(handle)?;
let class = unsafe {
(vt.class_define)(
descriptor.as_ptr() as *const c_char,
serve_on_create,
serve_on_destroy,
serve_on_transact,
)
};
if class.is_null() {
return Err(CoreError::binder(-1, "AIBinder_Class_define:serve"));
}
let ctx = Box::into_raw(Box::new(ServeCtx {
vt,
handler: Arc::new(Mutex::new(handler)),
in_flight: AtomicUsize::new(0),
max_inflight,
})) as *mut c_void;
let binder = unsafe { (vt.new_binder)(class, ctx) };
if binder.is_null() {
unsafe { drop(Box::from_raw(ctx as *mut ServeCtx)) };
return Err(CoreError::binder(-1, "AIBinder_new:serve"));
}
unsafe { (vt.associate_class)(binder, class) };
*GET_USER_DATA.lock().unwrap_or_else(|p| p.into_inner()) = Some(vt.get_user_data);
unsafe { (vt.set_thread_pool_max)(0) };
let join_fn = vt.join_thread_pool;
std::thread::spawn(move || unsafe { join_fn() });
Ok(Self {
_lib: lib,
vt,
binder,
_class: class,
})
}
pub fn open(descriptor: &[u8], handler: ServeHandler) -> Result<Self, CoreError> {
Self::open_bounded(descriptor, handler, DEFAULT_MAX_INFLIGHT)
}
pub fn as_raw(&self) -> *mut c_void {
self.binder as *mut c_void
}
pub fn push_oneway(
&self,
target: &OwnedBinder,
code: u32,
writes: impl FnOnce(&ParcelWriter<'_>) -> Result<(), CoreError>,
) -> Result<(), CoreError> {
transact_oneway(&self.vt, target.ptr, code, writes)
}
pub fn associate(&self, target: &OwnedBinder) -> Result<(), CoreError> {
if target.ptr.is_null() {
return Err(CoreError::binder(-1, "AIBinder_associateClass:null"));
}
let ok = unsafe { (self.vt.associate_class)(target.ptr, self._class) };
if !ok {
return Err(CoreError::binder(-1, "AIBinder_associateClass"));
}
Ok(())
}
pub fn death_recipient(&self, on_died: Box<dyn FnMut() + Send>) -> DeathRecipient {
DeathRecipient::new(self.vt, on_died)
}
pub fn oneway_sender(&self) -> OnewaySender {
OnewaySender { vt: self.vt }
}
}
#[derive(Clone, Copy)]
pub struct OnewaySender {
vt: Vtable,
}
impl OnewaySender {
pub fn push_oneway(
&self,
target: &OwnedBinder,
code: u32,
writes: impl FnOnce(&ParcelWriter<'_>) -> Result<(), CoreError>,
) -> Result<(), CoreError> {
transact_oneway(&self.vt, target.ptr, code, writes)
}
}
struct CallbackSlab {
slots: Vec<Option<Arc<Mutex<Box<dyn FnMut() + Send>>>>>,
}
fn callback_slab() -> &'static Mutex<CallbackSlab> {
static SLAB: OnceLock<Mutex<CallbackSlab>> = OnceLock::new();
SLAB.get_or_init(|| Mutex::new(CallbackSlab { slots: Vec::new() }))
}
unsafe extern "C" fn death_on_died(cookie: *mut c_void) {
let index = cookie as usize;
if index == 0 {
return;
}
let cb = {
let slab = callback_slab();
let slab = slab.lock().unwrap_or_else(|e| e.into_inner());
slab.slots.get(index - 1).and_then(|s| s.as_ref()).cloned()
};
if let Some(cb) = cb {
let mut cb = cb.lock().unwrap_or_else(|e| e.into_inner());
cb();
}
}
pub struct DeathRecipient {
recipient: *mut AIBinder_DeathRecipient,
slot: usize,
cookie: *mut c_void,
delete: unsafe extern "C" fn(*mut AIBinder_DeathRecipient),
link_to_death: unsafe extern "C" fn(
*mut AIBinder,
*mut AIBinder_DeathRecipient,
*mut c_void,
) -> BinderStatus,
unlink_to_death: unsafe extern "C" fn(
*mut AIBinder,
*mut AIBinder_DeathRecipient,
*mut c_void,
) -> BinderStatus,
}
unsafe impl Send for DeathRecipient {}
impl DeathRecipient {
fn new(vt: Vtable, on_died: Box<dyn FnMut() + Send>) -> Self {
let slot = {
let mut slab = callback_slab().lock().unwrap_or_else(|e| e.into_inner());
slab.slots.push(Some(Arc::new(Mutex::new(on_died))));
slab.slots.len() - 1
};
let cookie = (slot + 1) as *mut c_void;
let recipient = unsafe { (vt.death_recipient_new)(death_on_died) };
Self {
recipient,
slot,
cookie,
delete: vt.death_recipient_delete,
link_to_death: vt.link_to_death,
unlink_to_death: vt.unlink_to_death,
}
}
pub fn link(&self, target: &OwnedBinder) -> Result<(), CoreError> {
let status = unsafe { (self.link_to_death)(target.ptr, self.recipient, self.cookie) };
if status == super::sys::STATUS_OK {
Ok(())
} else {
Err(CoreError::binder(status, "AIBinder_linkToDeath"))
}
}
pub fn unlink(&self, target: &OwnedBinder) -> Result<(), CoreError> {
let status = unsafe { (self.unlink_to_death)(target.ptr, self.recipient, self.cookie) };
if status == super::sys::STATUS_OK {
Ok(())
} else {
Err(CoreError::binder(status, "AIBinder_unlinkToDeath"))
}
}
}
impl Drop for DeathRecipient {
fn drop(&mut self) {
if !self.recipient.is_null() {
unsafe { (self.delete)(self.recipient) };
}
let mut slab = callback_slab().lock().unwrap_or_else(|e| e.into_inner());
if let Some(slot) = slab.slots.get_mut(self.slot) {
slot.take();
}
}
}