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 STATUS_OUT_OF_RESOURCES: i32 = -7;
pub(super) const EX_NONE: i32 = 0;
pub(super) const BUNDLE_MAGIC: i32 = 0x4C444E42;
pub(super) const VAL_IBINDER: i32 = 15;
#[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;
#[allow(non_camel_case_types)]
pub(super) type AIBinder_DeathRecipient = 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;
#[allow(non_camel_case_types)]
pub(super) type AIBinder_DeathRecipient_onDiedCallback = unsafe extern "C" fn(*mut c_void);
pub(super) const FLAG_ONEWAY: u32 = 0x01;
const MAX_BINDER_STRING_LEN: usize = 1024 * 1024;
const STRING8_CAP: 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)
}
}
}
#[derive(Clone, Copy)]
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) write_int32_array:
unsafe extern "C" fn(*mut AParcel, *const i32, u32) -> 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) get_calling_uid: unsafe extern "C" fn() -> u32,
pub(super) get_calling_pid: unsafe extern "C" fn() -> i32,
pub(super) read_strong_binder:
unsafe extern "C" fn(*const AParcel, *mut *mut AIBinder) -> BinderStatus,
pub(super) write_string: unsafe extern "C" fn(*mut AParcel, *const c_char, i32) -> BinderStatus,
pub(super) death_recipient_new: unsafe extern "C" fn(
AIBinder_DeathRecipient_onDiedCallback,
) -> *mut AIBinder_DeathRecipient,
pub(super) death_recipient_delete: unsafe extern "C" fn(*mut AIBinder_DeathRecipient),
pub(super) link_to_death: unsafe extern "C" fn(
*mut AIBinder,
*mut AIBinder_DeathRecipient,
*mut c_void,
) -> BinderStatus,
pub(super) unlink_to_death: unsafe extern "C" fn(
*mut AIBinder,
*mut AIBinder_DeathRecipient,
*mut c_void,
) -> 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 struct OwnedBinder {
pub(super) ptr: *mut AIBinder,
pub(super) dec_strong: unsafe extern "C" fn(*mut AIBinder),
}
unsafe impl Send for OwnedBinder {}
impl OwnedBinder {
pub fn as_raw(&self) -> *mut std::os::raw::c_void {
self.ptr as *mut std::os::raw::c_void
}
}
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
),
write_int32_array: dlsym_fn!(
handle,
"AParcel_writeInt32Array",
unsafe extern "C" fn(*mut AParcel, *const i32, u32) -> 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
),
get_calling_uid: dlsym_fn!(
handle,
"AIBinder_getCallingUid",
unsafe extern "C" fn() -> u32
),
get_calling_pid: dlsym_fn!(
handle,
"AIBinder_getCallingPid",
unsafe extern "C" fn() -> i32
),
read_strong_binder: dlsym_fn!(
handle,
"AParcel_readStrongBinder",
unsafe extern "C" fn(*const AParcel, *mut *mut AIBinder) -> BinderStatus
),
write_string: dlsym_fn!(
handle,
"AParcel_writeString",
unsafe extern "C" fn(*mut AParcel, *const c_char, i32) -> BinderStatus
),
death_recipient_new: dlsym_fn!(
handle,
"AIBinder_DeathRecipient_new",
unsafe extern "C" fn(
AIBinder_DeathRecipient_onDiedCallback,
) -> *mut AIBinder_DeathRecipient
),
death_recipient_delete: dlsym_fn!(
handle,
"AIBinder_DeathRecipient_delete",
unsafe extern "C" fn(*mut AIBinder_DeathRecipient)
),
link_to_death: dlsym_fn!(
handle,
"AIBinder_linkToDeath",
unsafe extern "C" fn(
*mut AIBinder,
*mut AIBinder_DeathRecipient,
*mut c_void,
) -> BinderStatus
),
unlink_to_death: dlsym_fn!(
handle,
"AIBinder_unlinkToDeath",
unsafe extern "C" fn(
*mut AIBinder,
*mut AIBinder_DeathRecipient,
*mut c_void,
) -> BinderStatus
),
})
}
pub struct ParcelReader<'a> {
pub(super) vt: &'a Vtable,
pub(super) parcel: *const AParcel,
}
impl<'a> ParcelReader<'a> {
pub(super) fn owned(vt: &'a Vtable, parcel: &'a OwnedParcel) -> Self {
Self {
vt,
parcel: parcel.ptr,
}
}
pub(super) fn borrowed(vt: &'a Vtable, parcel: *const AParcel) -> Self {
Self { vt, parcel }
}
pub fn read_i32(&self) -> Result<i32, CoreError> {
let mut v = 0i32;
let s = unsafe { (self.vt.read_int32)(self.parcel, &mut v) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_readInt32"));
}
Ok(v)
}
pub fn read_string(&self) -> Result<Option<String>, CoreError> {
let mut buf = StringBuf::new();
let s = unsafe {
(self.vt.read_string)(
self.parcel,
&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 fn read_strong_binder(&self) -> Result<Option<OwnedBinder>, CoreError> {
let mut raw: *mut AIBinder = std::ptr::null_mut();
let s = unsafe { (self.vt.read_strong_binder)(self.parcel, &mut raw) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_readStrongBinder"));
}
if raw.is_null() {
return Ok(None);
}
Ok(Some(OwnedBinder {
ptr: raw,
dec_strong: self.vt.dec_strong,
}))
}
pub 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, &mut v) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_readFloat"));
}
Ok(v)
}
pub 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, &mut v) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_readInt64"));
}
Ok(v)
}
pub 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, &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 read_string8(&self) -> Result<Option<String>, CoreError> {
let len = self.read_i32()?;
if len < 0 {
return Ok(None);
}
if len as usize > STRING8_CAP {
return Err(CoreError::binder(-1, "read_string8:too_long"));
}
let words = (len as usize + 1).div_ceil(4);
let mut buf = Vec::with_capacity(words * 4);
for _ in 0..words {
buf.extend_from_slice(&(self.read_i32()? as u32).to_le_bytes());
}
buf.truncate(len as usize);
let s = String::from_utf8_lossy(&buf).into_owned();
Ok(Some(s))
}
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(())
}
10 => self.skip_char_sequence(),
15 => self.read_strong_binder().map(|_| ()),
18 => self.skip_int_array(),
19 => {
let n = self.read_i32()?.max(0) as usize;
for _ in 0..n {
self.read_int64()?;
}
Ok(())
}
13 => {
let n = self.read_i32()?.max(0) as usize;
self.skip_bytes(n)
}
23 => {
let n = self.read_i32()?.max(0) as usize;
self.skip_i32s(n)
}
14 => {
let n = self.read_i32()?.max(0) as usize;
for _ in 0..n {
self.skip_string16()?;
}
Ok(())
}
26 => self.skip_i32s(2),
27 => {
self.read_float()?;
self.read_float()?;
Ok(())
}
2 | 4 | 11 | 12 | 16 | 17 | 21 => {
let len = self.read_i32()?;
if len < 0 {
return Ok(());
}
self.skip_bytes(len as usize)
}
3 | 25 => self.skip_bundle(),
_ => Err(CoreError::binder(tag, "parcel:unsupported readValue tag")),
}
}
pub(super) fn skip_string8_array(&self) -> Result<(), CoreError> {
let n = self.read_i32()?;
if n < 0 {
return Ok(());
}
for _ in 0..n {
self.skip_string8()?;
}
Ok(())
}
pub(super) fn skip_bundle(&self) -> Result<(), CoreError> {
let length = self.read_i32()?;
if length <= 0 {
return Ok(());
}
let magic = self.read_i32()?;
if magic != BUNDLE_MAGIC {
return Err(CoreError::binder(magic, "parcel:bad bundle magic"));
}
self.skip_bytes(length as usize)
}
pub(super) fn skip_char_sequence(&self) -> Result<(), CoreError> {
match self.read_i32()? {
1 => self.skip_string8().map(|_| ()),
0 => Err(CoreError::binder(
0,
"parcel:spanned CharSequence unsupported",
)),
m => Err(CoreError::binder(m, "parcel:bad CharSequence marker")),
}
}
pub(super) fn skip_sparsearray(&self) -> Result<(), CoreError> {
let n = self.read_i32()?;
if n < 0 {
return Ok(());
}
for _ in 0..n {
self.read_i32()?;
self.skip_value()?;
}
Ok(())
}
pub(super) fn skip_typed_object_array(
&self,
skip_elem: impl Fn(&ParcelReader<'_>) -> Result<(), CoreError>,
) -> Result<(), CoreError> {
let n = self.read_i32()?;
if n < 0 {
return Ok(());
}
for _ in 0..n {
match self.read_i32()? {
1 => skip_elem(self)?,
0 => {}
m => return Err(CoreError::binder(m, "parcel:bad typed-object marker")),
}
}
Ok(())
}
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 struct ParcelWriter<'a> {
pub(super) vt: &'a Vtable,
pub(super) parcel: *mut AParcel,
}
impl<'a> ParcelWriter<'a> {
pub(super) fn owned(vt: &'a Vtable, parcel: &'a OwnedParcel) -> Self {
Self {
vt,
parcel: parcel.ptr,
}
}
pub(super) fn borrowed(vt: &'a Vtable, parcel: *mut AParcel) -> Self {
Self { vt, parcel }
}
pub fn write_i32(&self, v: i32) -> Result<(), CoreError> {
let s = unsafe { (self.vt.write_int32)(self.parcel, v) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_writeInt32"));
}
Ok(())
}
pub fn write_int32_array(&self, v: &[i32]) -> Result<(), CoreError> {
let s = unsafe { (self.vt.write_int32_array)(self.parcel, v.as_ptr(), v.len() as u32) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_writeInt32Array"));
}
Ok(())
}
pub fn write_strong_binder(&self, b: *mut c_void) -> Result<(), CoreError> {
let s = unsafe { (self.vt.write_strong_binder)(self.parcel, b as *mut AIBinder) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_writeStrongBinder"));
}
Ok(())
}
pub fn write_string(&self, s: Option<&str>) -> Result<(), CoreError> {
const MAX: usize = 1024 * 1024;
match s {
None => {
let s = unsafe { (self.vt.write_string)(self.parcel, std::ptr::null(), -1) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_writeString"));
}
}
Some(s) => {
let bytes = s.as_bytes();
if bytes.len() > MAX {
return Err(CoreError::binder(-1, "AParcel_writeString:too_long"));
}
let mut c = Vec::with_capacity(bytes.len() + 1);
c.extend_from_slice(bytes);
c.push(0);
let s = unsafe {
(self.vt.write_string)(
self.parcel,
c.as_ptr() as *const c_char,
bytes.len() as i32,
)
};
if s != STATUS_OK {
return Err(CoreError::binder(s, "AParcel_writeString"));
}
}
}
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::owned(vt, &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) fn transact_oneway(
vt: &Vtable,
binder: *mut AIBinder,
code: u32,
writes: impl FnOnce(&ParcelWriter<'_>) -> Result<(), CoreError>,
) -> Result<(), 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::owned(vt, &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, FLAG_ONEWAY) };
inp.ptr = std::ptr::null_mut();
if !out_ptr.is_null() {
unsafe { (vt.parcel_delete)(out_ptr) };
}
if s != STATUS_OK {
return Err(CoreError::binder(s, "AIBinder_transact"));
}
Ok(())
}
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"));
}
}