use crate::binder::AsNative;
use crate::error::{Result, status_result};
use crate::proxy::SpIBinder;
use crate::sys;
use std::ffi::{CStr, CString, c_void};
use std::os::raw::c_char;
use libc::{sockaddr, sockaddr_un, sockaddr_vm, socklen_t};
use std::boxed::Box;
use std::{mem, ptr};
#[derive(Debug)]
pub struct Accessor {
accessor: *mut sys::ABinderRpc_Accessor,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionInfo {
Vsock(sockaddr_vm),
Unix(sockaddr_un),
}
unsafe impl Send for Accessor {}
unsafe impl Sync for Accessor {}
impl Accessor {
pub fn new<F>(instance: &str, callback: F) -> Accessor
where
F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
{
let callback: *mut c_void = Box::into_raw(Box::new(callback)) as *mut c_void;
let inst = CString::new(instance).unwrap();
let accessor = unsafe {
sys::ABinderRpc_Accessor_new(
inst.as_ptr(),
Some(Self::connection_info::<F>),
callback,
Some(Self::cookie_decr_refcount::<F>),
)
};
Accessor { accessor }
}
pub fn from_binder(instance: &str, binder: SpIBinder) -> Option<Accessor> {
let inst = CString::new(instance).unwrap();
let accessor =
unsafe { sys::ABinderRpc_Accessor_fromBinder(inst.as_ptr(), binder.as_raw()) };
if accessor.is_null() {
return None;
}
Some(Accessor { accessor })
}
pub fn as_binder(&self) -> Option<SpIBinder> {
unsafe { SpIBinder::from_raw(sys::ABinderRpc_Accessor_asBinder(self.accessor)) }
}
unsafe fn release(mut self) -> *mut sys::ABinderRpc_Accessor {
if self.accessor.is_null() {
log::error!("Attempting to release an Accessor that was already released");
return ptr::null_mut();
}
let ptr = self.accessor;
self.accessor = ptr::null_mut();
ptr
}
unsafe extern "C" fn connection_info<F>(
instance: *const c_char,
cookie: *mut c_void,
) -> *mut binder_ndk_sys::ABinderRpc_ConnectionInfo
where
F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
{
if cookie.is_null() || instance.is_null() {
log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!");
return ptr::null_mut();
}
let callback = unsafe { (cookie as *const F).as_ref().unwrap() };
let inst = unsafe {
match CStr::from_ptr(instance).to_str() {
Ok(s) => s,
Err(err) => {
log::error!("Failed to get a valid C string! {err:?}");
return ptr::null_mut();
}
}
};
let connection = match callback(inst) {
Some(con) => con,
None => {
return ptr::null_mut();
}
};
match connection {
ConnectionInfo::Vsock(addr) => {
unsafe {
sys::ABinderRpc_ConnectionInfo_new(
&addr as *const sockaddr_vm as *const sockaddr,
mem::size_of::<sockaddr_vm>() as socklen_t,
)
}
}
ConnectionInfo::Unix(addr) => {
unsafe {
sys::ABinderRpc_ConnectionInfo_new(
&addr as *const sockaddr_un as *const sockaddr,
mem::size_of::<sockaddr_un>() as socklen_t,
)
}
}
}
}
unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
where
F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
{
unsafe { std::mem::drop(Box::from_raw(cookie as *mut F)) };
}
}
impl Drop for Accessor {
fn drop(&mut self) {
if self.accessor.is_null() {
return;
}
unsafe {
sys::ABinderRpc_Accessor_delete(self.accessor);
}
}
}
pub fn delegate_accessor(name: &str, mut binder: SpIBinder) -> Result<SpIBinder> {
let instance = CString::new(name).unwrap();
let mut delegator = ptr::null_mut();
let status =
unsafe { sys::ABinderRpc_Accessor_delegateAccessor(instance.as_ptr(),
binder.as_native_mut(), &mut delegator) };
status_result(status)?;
Ok(unsafe { SpIBinder::from_raw(delegator).expect("Expected valid binder at this point") })
}
#[derive(Debug)]
pub struct AccessorProvider {
accessor_provider: *mut sys::ABinderRpc_AccessorProvider,
}
unsafe impl Send for AccessorProvider {}
unsafe impl Sync for AccessorProvider {}
impl AccessorProvider {
pub fn new<F>(instances: &[String], provider: F) -> Option<AccessorProvider>
where
F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static,
{
let callback: *mut c_void = Box::into_raw(Box::new(provider)) as *mut c_void;
let c_str_instances: Vec<CString> =
instances.iter().map(|s| CString::new(s.as_bytes()).unwrap()).collect();
let mut c_instances: Vec<*const c_char> =
c_str_instances.iter().map(|s| s.as_ptr()).collect();
let num_instances: usize = c_instances.len();
let accessor_provider = unsafe {
sys::ABinderRpc_registerAccessorProvider(
Some(Self::get_accessor::<F>),
c_instances.as_mut_ptr(),
num_instances,
callback,
Some(Self::accessor_cookie_decr_refcount::<F>),
)
};
if accessor_provider.is_null() {
return None;
}
Some(AccessorProvider { accessor_provider })
}
unsafe extern "C" fn get_accessor<F>(
instance: *const c_char,
cookie: *mut c_void,
) -> *mut binder_ndk_sys::ABinderRpc_Accessor
where
F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static,
{
if cookie.is_null() || instance.is_null() {
log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!");
return ptr::null_mut();
}
let callback = unsafe { (cookie as *const F).as_ref().unwrap() };
let inst = {
match unsafe { CStr::from_ptr(instance) }.to_str() {
Ok(s) => s,
Err(err) => {
log::error!("Failed to get a valid C string! {err:?}");
return ptr::null_mut();
}
}
};
match callback(inst) {
Some(a) => {
unsafe { a.release() }
}
None => ptr::null_mut(),
}
}
unsafe extern "C" fn accessor_cookie_decr_refcount<F>(cookie: *mut c_void)
where
F: Fn(&str) -> Option<Accessor> + Send + Sync + 'static,
{
unsafe { std::mem::drop(Box::from_raw(cookie as *mut F)) };
}
}
impl Drop for AccessorProvider {
fn drop(&mut self) {
unsafe {
sys::ABinderRpc_unregisterAccessorProvider(self.accessor_provider);
}
}
}