use crate::CoreError;
use std::os::raw::{c_char, c_void};
pub(super) const STATUS_OK: i32 = 0;
pub(super) const STATUS_UNKNOWN_TRANSACTION: i32 = -2;
pub(super) const EX_NONE: i32 = 0;
#[cfg(target_pointer_width = "64")]
pub(super) const LIBBINDER_PATH: &[u8] = b"/system/lib64/libbinder_ndk.so\0";
#[cfg(target_pointer_width = "32")]
pub(super) const LIBBINDER_PATH: &[u8] = b"/system/lib/libbinder_ndk.so\0";
pub(super) type AIBinder = c_void;
#[allow(non_camel_case_types)]
pub(super) type AIBinder_Class = c_void;
pub(super) type AParcel = c_void;
pub(super) type BinderStatus = i32;
pub(super) type StringAllocator = unsafe extern "C" fn(*mut c_void, i32, *mut *mut c_char) -> bool;
const MAX_BINDER_STRING_LEN: usize = 1024 * 1024;
unsafe extern "C" fn string_alloc(
cookie: *mut c_void,
length: i32,
buffer: *mut *mut c_char,
) -> bool {
if length == -1 {
return true;
}
if length < 0 {
return false;
}
let len = length as usize;
if len > MAX_BINDER_STRING_LEN {
return false;
}
let s = unsafe { &mut *(cookie as *mut StringBuf) };
s.0.reserve_exact(len + 1);
unsafe { s.0.as_mut_vec().resize(len + 1, 0) };
unsafe { *buffer = s.0.as_mut_ptr() as *mut c_char };
true
}
struct StringBuf(String);
impl StringBuf {
fn new() -> Self {
Self(String::new())
}
fn finish(mut self) -> Option<String> {
if let Some(pos) = self.0.as_bytes().iter().position(|&b| b == 0) {
unsafe { self.0.as_mut_vec().truncate(pos) };
}
if self.0.is_empty() {
None
} else {
Some(self.0)
}
}
}
pub(super) struct Vtable {
pub(super) get_service: unsafe extern "C" fn(*const c_char) -> *mut AIBinder,
pub(super) class_define: unsafe extern "C" fn(
*const c_char,
unsafe extern "C" fn(*mut c_void) -> *mut c_void,
unsafe extern "C" fn(*mut c_void),
unsafe extern "C" fn(*mut AIBinder, u32, *const AParcel, *mut AParcel) -> BinderStatus,
) -> *mut AIBinder_Class,
pub(super) associate_class: unsafe extern "C" fn(*mut AIBinder, *mut AIBinder_Class) -> bool,
pub(super) new_binder: unsafe extern "C" fn(*const AIBinder_Class, *mut c_void) -> *mut AIBinder,
pub(super) prepare_transaction: unsafe extern "C" fn(*mut AIBinder, *mut *mut AParcel) -> BinderStatus,
pub(super) transact: unsafe extern "C" fn(
*mut AIBinder,
u32,
*mut *mut AParcel,
*mut *mut AParcel,
u32,
) -> BinderStatus,
pub(super) dec_strong: unsafe extern "C" fn(*mut AIBinder),
pub(super) parcel_delete: unsafe extern "C" fn(*mut AParcel),
pub(super) read_int32: unsafe extern "C" fn(*const AParcel, *mut i32) -> BinderStatus,
pub(super) read_string:
unsafe extern "C" fn(*const AParcel, *mut c_void, StringAllocator) -> BinderStatus,
pub(super) write_strong_binder: unsafe extern "C" fn(*mut AParcel, *mut AIBinder) -> BinderStatus,
pub(super) set_thread_pool_max: unsafe extern "C" fn(u32),
pub(super) join_thread_pool: unsafe extern "C" fn(),
pub(super) get_user_data: unsafe extern "C" fn(*const AIBinder) -> *mut c_void,
pub(super) write_int32: unsafe extern "C" fn(*mut AParcel, i32) -> BinderStatus,
pub(super) read_float: Option<unsafe extern "C" fn(*const AParcel, *mut f32) -> BinderStatus>,
pub(super) read_int64: Option<unsafe extern "C" fn(*const AParcel, *mut i64) -> BinderStatus>,
pub(super) read_bool: Option<unsafe extern "C" fn(*const AParcel, *mut bool) -> BinderStatus>,
}
pub(super) struct DlHandle;
unsafe impl Send for DlHandle {}
impl Drop for DlHandle {
fn drop(&mut self) {
}
}
pub(super) struct OwnedParcel {
pub(super) ptr: *mut AParcel,
pub(super) delete: unsafe extern "C" fn(*mut AParcel),
}
impl Drop for OwnedParcel {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { (self.delete)(self.ptr) };
}
}
}
pub(super) struct OwnedBinder {
pub(super) ptr: *mut AIBinder,
pub(super) dec_strong: unsafe extern "C" fn(*mut AIBinder),
}
unsafe impl Send for OwnedBinder {}
impl Drop for OwnedBinder {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { (self.dec_strong)(self.ptr) };
}
}
}
macro_rules! dlsym_fn {
($handle:expr, $name:literal, $ty:ty) => {{
let sym =
unsafe { libc::dlsym($handle, concat!($name, "\0").as_ptr() as *const c_char) };
if sym.is_null() {
return Err(CoreError::binder(-1, concat!("dlsym:", $name)));
}
unsafe { std::mem::transmute::<*mut c_void, $ty>(sym) }
}};
}
macro_rules! dlsym_opt {
($handle:expr, $name:literal, $ty:ty) => {{
let sym =
unsafe { libc::dlsym($handle, concat!($name, "\0").as_ptr() as *const c_char) };
if sym.is_null() {
None
} else {
Some(unsafe { std::mem::transmute::<*mut c_void, $ty>(sym) })
}
}};
}
pub(super) fn load_vtable(handle: *mut c_void) -> Result<Vtable, CoreError> {
Ok(Vtable {
get_service: dlsym_fn!(
handle,
"AServiceManager_getService",
unsafe extern "C" fn(*const c_char) -> *mut AIBinder
),
class_define: dlsym_fn!(
handle,
"AIBinder_Class_define",
unsafe extern "C" fn(
*const c_char,
unsafe extern "C" fn(*mut c_void) -> *mut c_void,
unsafe extern "C" fn(*mut c_void),
unsafe extern "C" fn(
*mut AIBinder,
u32,
*const AParcel,
*mut AParcel,
) -> BinderStatus,
) -> *mut AIBinder_Class
),
associate_class: dlsym_fn!(
handle,
"AIBinder_associateClass",
unsafe extern "C" fn(*mut AIBinder, *mut AIBinder_Class) -> bool
),
new_binder: dlsym_fn!(
handle,
"AIBinder_new",
unsafe extern "C" fn(*const AIBinder_Class, *mut c_void) -> *mut AIBinder
),
prepare_transaction: dlsym_fn!(
handle,
"AIBinder_prepareTransaction",
unsafe extern "C" fn(*mut AIBinder, *mut *mut AParcel) -> BinderStatus
),
transact: dlsym_fn!(
handle,
"AIBinder_transact",
unsafe extern "C" fn(
*mut AIBinder,
u32,
*mut *mut AParcel,
*mut *mut AParcel,
u32,
) -> BinderStatus
),
dec_strong: dlsym_fn!(
handle,
"AIBinder_decStrong",
unsafe extern "C" fn(*mut AIBinder)
),
parcel_delete: dlsym_fn!(handle, "AParcel_delete", unsafe extern "C" fn(*mut AParcel)),
read_int32: dlsym_fn!(
handle,
"AParcel_readInt32",
unsafe extern "C" fn(*const AParcel, *mut i32) -> BinderStatus
),
read_string: dlsym_fn!(
handle,
"AParcel_readString",
unsafe extern "C" fn(*const AParcel, *mut c_void, StringAllocator) -> BinderStatus
),
write_strong_binder: dlsym_fn!(
handle,
"AParcel_writeStrongBinder",
unsafe extern "C" fn(*mut AParcel, *mut AIBinder) -> BinderStatus
),
set_thread_pool_max: dlsym_fn!(
handle,
"ABinderProcess_setThreadPoolMaxThreadCount",
unsafe extern "C" fn(u32)
),
join_thread_pool: dlsym_fn!(
handle,
"ABinderProcess_joinThreadPool",
unsafe extern "C" fn()
),
get_user_data: dlsym_fn!(
handle,
"AIBinder_getUserData",
unsafe extern "C" fn(*const AIBinder) -> *mut c_void
),
write_int32: dlsym_fn!(
handle,
"AParcel_writeInt32",
unsafe extern "C" fn(*mut AParcel, i32) -> BinderStatus
),
read_bool: dlsym_opt!(
handle,
"AParcel_readBool",
unsafe extern "C" fn(*const AParcel, *mut bool) -> BinderStatus
),
read_float: dlsym_opt!(
handle,
"AParcel_readFloat",
unsafe extern "C" fn(*const AParcel, *mut f32) -> BinderStatus
),
read_int64: dlsym_opt!(
handle,
"AParcel_readInt64",
unsafe extern "C" fn(*const AParcel, *mut i64) -> BinderStatus
),
})
}
pub(super) struct ParcelReader<'a> {
pub(super) vt: &'a Vtable,
pub(super) parcel: &'a OwnedParcel,
}
impl<'a> ParcelReader<'a> {
pub(super) fn read_i32(&self) -> Result<i32, CoreError> {
let mut v = 0i32;
let s = unsafe { (self.vt.read_int32)(self.parcel.ptr, &mut v) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_readInt32"));
}
Ok(v)
}
pub(super) fn read_string(&self) -> Result<Option<String>, CoreError> {
let mut buf = StringBuf::new();
let s = unsafe {
(self.vt.read_string)(
self.parcel.ptr,
&mut buf as *mut StringBuf as *mut c_void,
string_alloc,
)
};
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_readString"));
}
Ok(buf.finish())
}
pub(super) fn read_float(&self) -> Result<f32, CoreError> {
let r = self
.vt
.read_float
.ok_or_else(|| CoreError::binder(-1, "AParcel_readFloat:unavailable"))?;
let mut v = 0f32;
let s = unsafe { r(self.parcel.ptr, &mut v) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_readFloat"));
}
Ok(v)
}
pub(super) fn read_int64(&self) -> Result<i64, CoreError> {
let r = self
.vt
.read_int64
.ok_or_else(|| CoreError::binder(-1, "AParcel_readInt64:unavailable"))?;
let mut v = 0i64;
let s = unsafe { r(self.parcel.ptr, &mut v) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_readInt64"));
}
Ok(v)
}
pub(super) fn read_bool(&self) -> Result<bool, CoreError> {
if let Some(rb) = self.vt.read_bool {
let mut v = false;
let s = unsafe { rb(self.parcel.ptr, &mut v) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_readBool"));
}
Ok(v)
} else {
Ok(self.read_i32()? != 0)
}
}
pub(super) fn skip_i32s(&self, n: usize) -> Result<(), CoreError> {
for _ in 0..n {
self.read_i32()?;
}
Ok(())
}
pub(super) fn skip_int_array(&self) -> Result<(), CoreError> {
let count = self.read_i32()?.max(0) as usize;
self.skip_i32s(count)
}
pub(super) fn skip_bytes(&self, n: usize) -> Result<(), CoreError> {
self.skip_i32s(n.div_ceil(4))
}
pub(super) fn skip_string16(&self) -> Result<bool, CoreError> {
let len = self.read_i32()?;
if len < 0 {
return Ok(false);
}
let bytes = ((len as usize).saturating_add(1)).saturating_mul(2);
self.skip_bytes(bytes)?;
Ok(true)
}
pub(super) fn skip_string8(&self) -> Result<bool, CoreError> {
let len = self.read_i32()?;
if len < 0 {
return Ok(false);
}
let bytes = (len as usize).saturating_add(1);
self.skip_bytes(bytes)?;
Ok(true)
}
pub(super) fn skip_value(&self) -> Result<(), CoreError> {
let tag = self.read_i32()?;
match tag {
-1 => Ok(()),
0 => self.skip_string16().map(|_| ()),
1 | 5 | 9 | 20 | 29 => self.read_i32().map(|_| ()),
6 => self.read_int64().map(|_| ()),
7 => self.read_float().map(|_| ()),
8 => {
self.read_i32()?;
self.read_i32()?;
Ok(())
}
26 => self.skip_i32s(2),
27 => {
self.read_float()?;
self.read_float()?;
Ok(())
}
4 => {
let len = self.read_i32()?;
if len < 0 {
return Ok(());
}
self.skip_bytes(len as usize)
}
_ => Err(CoreError::binder(
tag,
"display_info:unsupported readValue tag",
)),
}
}
pub(super) fn skip_typed_rect(&self) -> Result<(), CoreError> {
match self.read_i32()? {
0 => Ok(()),
1 => self.skip_i32s(4),
m => Err(CoreError::binder(m, "display_info:bad typed Rect marker")),
}
}
pub(super) fn read_first_package_from_names(&self) -> Result<Option<String>, CoreError> {
let count = self.read_i32()?.max(0) as usize;
let mut first: Option<String> = None;
for _ in 0..count {
let s = self.read_string()?;
if first.is_none() {
first = s.and_then(|c| c.split('/').next().map(str::to_owned));
}
}
Ok(first)
}
}
pub(super) struct ParcelWriter<'a> {
pub(super) vt: &'a Vtable,
pub(super) parcel: &'a OwnedParcel,
}
impl<'a> ParcelWriter<'a> {
pub(super) fn write_i32(&self, v: i32) -> Result<(), CoreError> {
let s = unsafe { (self.vt.write_int32)(self.parcel.ptr, v) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_writeInt32"));
}
Ok(())
}
pub(super) fn write_strong_binder(&self, b: *mut AIBinder) -> Result<(), CoreError> {
let s = unsafe { (self.vt.write_strong_binder)(self.parcel.ptr, b) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_writeStrongBinder"));
}
Ok(())
}
}
pub(super) fn transact_write(
vt: &Vtable,
binder: *mut AIBinder,
code: u32,
writes: impl FnOnce(&ParcelWriter<'_>) -> Result<(), CoreError>,
) -> Result<OwnedParcel, CoreError> {
let mut in_ptr: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (vt.prepare_transaction)(binder, &mut in_ptr) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AIBinder_prepareTransaction"));
}
let mut inp = OwnedParcel {
ptr: in_ptr,
delete: vt.parcel_delete,
};
{
let writer = ParcelWriter { vt, parcel: &inp };
writes(&writer)?;
}
let mut out_ptr: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (vt.transact)(binder, code, &mut inp.ptr, &mut out_ptr, 0) };
inp.ptr = std::ptr::null_mut();
let out = OwnedParcel {
ptr: out_ptr,
delete: vt.parcel_delete,
};
if s != STATUS_OK {
return Err(CoreError::binder(s, "AIBinder_transact"));
}
Ok(out)
}
pub(super) static GET_USER_DATA: std::sync::Mutex<
Option<unsafe extern "C" fn(*const AIBinder) -> *mut c_void>,
> = std::sync::Mutex::new(None);
#[cfg(test)]
mod tests {
use super::*;
fn alloc(length: i32) -> bool {
let mut buf = StringBuf::new();
let mut out: *mut c_char = std::ptr::null_mut();
unsafe { string_alloc(&mut buf as *mut StringBuf as *mut c_void, length, &mut out) }
}
#[test]
fn string_alloc_rejects_oversized() {
assert!(!alloc(MAX_BINDER_STRING_LEN as i32 + 1));
}
#[test]
fn string_alloc_accepts_null_marker_rejects_other_negative() {
assert!(alloc(-1));
assert!(!alloc(-2));
}
#[test]
fn string_alloc_accepts_valid_len_and_nul_terminates() {
let mut buf = StringBuf::new();
let mut out: *mut c_char = std::ptr::null_mut();
let ok =
unsafe { string_alloc(&mut buf as *mut StringBuf as *mut c_void, 4, &mut out) };
assert!(ok);
assert!(!out.is_null());
{
let vec = unsafe { buf.0.as_mut_vec() };
b"ABCD".iter().enumerate().for_each(|(i, &b)| vec[i] = b);
vec[4] = 0;
}
assert_eq!(buf.finish().as_deref(), Some("ABCD"));
}
}