use std::sync::Arc;
use crate::sys::binder::{flat_binder_object, flat_binder_object__bindgen_ty_1};
use crate::{binder::*, error::*, process_state, sys::*};
const _: () = {
use std::mem::{offset_of, size_of};
assert!(size_of::<flat_binder_object>() == FlatBinderObject::SIZE);
assert!(offset_of!(flat_binder_object, flags) == 4);
assert!(offset_of!(flat_binder_object, __bindgen_anon_1) == 8);
assert!(size_of::<flat_binder_object__bindgen_ty_1>() == 8);
assert!(offset_of!(flat_binder_object, cookie) == 16);
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct FlatBinderObject {
type_: u32,
pub(crate) flags: u32,
object: [u8; 8],
pub(crate) cookie: binder_uintptr_t,
}
impl Default for FlatBinderObject {
fn default() -> Self {
Self::new_binder_with_flags(0)
}
}
impl FlatBinderObject {
pub(crate) const SIZE: usize = 24;
pub(crate) fn new_with_fd(fd: i32, take_ownership: bool) -> Self {
let mut obj = FlatBinderObject {
type_: BINDER_TYPE_FD,
flags: 0,
object: [0; 8],
cookie: if take_ownership { 1 } else { 0 },
};
obj.set_handle(fd as u32);
obj
}
pub(crate) fn new_binder_with_flags(flags: u32) -> Self {
FlatBinderObject {
type_: BINDER_TYPE_BINDER,
flags,
object: [0; 8],
cookie: 0,
}
}
pub(crate) fn new_handle(handle: u32, flags: u32) -> Self {
let mut obj = FlatBinderObject {
type_: BINDER_TYPE_HANDLE,
flags,
object: [0; 8],
cookie: 0,
};
obj.set_handle(handle);
obj
}
pub(crate) fn to_bytes(self) -> [u8; Self::SIZE] {
let mut bytes = [0; Self::SIZE];
bytes[0..4].copy_from_slice(&self.type_.to_ne_bytes());
bytes[4..8].copy_from_slice(&self.flags.to_ne_bytes());
bytes[8..16].copy_from_slice(&self.object);
bytes[16..24].copy_from_slice(&self.cookie.to_ne_bytes());
bytes
}
pub(crate) fn from_bytes(bytes: &[u8; Self::SIZE]) -> Self {
FlatBinderObject {
type_: u32::from_ne_bytes(bytes_at(bytes, 0)),
flags: u32::from_ne_bytes(bytes_at(bytes, 4)),
object: bytes_at(bytes, 8),
cookie: binder_uintptr_t::from_ne_bytes(bytes_at(bytes, 16)),
}
}
pub(crate) fn to_uapi(self) -> flat_binder_object {
flat_binder_object {
hdr: binder_object_header { type_: self.type_ },
flags: self.flags,
__bindgen_anon_1: flat_binder_object__bindgen_ty_1 {
binder: self.pointer(),
},
cookie: self.cookie,
}
}
pub(crate) fn header_type(&self) -> u32 {
self.type_
}
pub(crate) fn handle(&self) -> u32 {
let [h0, h1, h2, h3, ..] = self.object;
u32::from_ne_bytes([h0, h1, h2, h3])
}
pub(crate) fn set_handle(&mut self, handle: u32) {
self.object = [0; 8];
self.object[..4].copy_from_slice(&handle.to_ne_bytes());
}
pub(crate) fn pointer(&self) -> binder_uintptr_t {
binder_uintptr_t::from_ne_bytes(self.object)
}
fn set_pointer(&mut self, pointer: binder_uintptr_t) {
self.object = pointer.to_ne_bytes();
}
pub(crate) fn set_cookie(&mut self, cookie: binder_uintptr_t) {
self.cookie = cookie;
}
pub(crate) fn acquire(&self) -> Result<()> {
match self.type_ {
BINDER_TYPE_BINDER => {
if self.pointer() != 0 {
let id = self.pointer();
if !process_state::ProcessState::as_self().incref_publish(id) {
log::error!("flat_binder_object::acquire: unknown native id {id}");
debug_assert!(false, "acquire on unknown native id {id}");
}
}
Ok(())
}
BINDER_TYPE_HANDLE => Ok(()),
BINDER_TYPE_FD => {
Ok(())
}
_ => {
log::error!("Invalid object type {:08x}", self.type_);
Err(StatusCode::InvalidOperation)
}
}
}
pub(crate) fn release(&self) -> Result<()> {
match self.type_ {
BINDER_TYPE_BINDER => {
if self.pointer() != 0 {
let id = self.pointer();
if !process_state::ProcessState::as_self().decref_publish(id) {
log::error!("flat_binder_object::release: unknown native id {id}");
debug_assert!(false, "release on unknown native id {id}");
}
}
Ok(())
}
BINDER_TYPE_HANDLE => Ok(()),
BINDER_TYPE_FD => Ok(()),
_ => {
log::error!("Invalid object type {:08x}", self.type_);
Err(StatusCode::InvalidOperation)
}
}
}
}
const SCHED_NORMAL: u32 = 0;
const FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT: u32 = 9;
const FLAT_BINDER_FLAG_SCHED_POLICY_VALUE_MASK: u32 = 0x3;
fn sched_policy_mask(policy: u32, priority: u32) -> u32 {
(priority & FLAT_BINDER_FLAG_PRIORITY_MASK)
| ((policy & FLAT_BINDER_FLAG_SCHED_POLICY_VALUE_MASK)
<< FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT)
}
impl From<&SIBinder> for FlatBinderObject {
fn from(binder: &SIBinder) -> Self {
let sched_bits = if !process_state::ProcessState::as_self().background_scheduling_disabled()
{
sched_policy_mask(SCHED_NORMAL, 19)
} else {
0
};
if let Some(proxy) = binder.as_proxy() {
FlatBinderObject::new_handle(proxy.handle(), sched_bits)
} else {
let id =
process_state::ProcessState::as_self().publish_native(Arc::clone(binder.as_arc()));
let local = binder.local_binder_flags();
let sched_mask = FLAT_BINDER_FLAG_PRIORITY_MASK
| (FLAT_BINDER_FLAG_SCHED_POLICY_VALUE_MASK << FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT);
let effective_sched = if local & sched_mask != 0 {
local & sched_mask
} else {
sched_bits
};
let mut obj =
FlatBinderObject::new_binder_with_flags((local & !sched_mask) | effective_sched);
obj.set_pointer(id);
obj
}
}
}
pub(crate) fn read_flat_binder(data: &[u8], offset: usize) -> Result<FlatBinderObject> {
let bytes = data
.get(offset..)
.and_then(<[u8]>::first_chunk::<{ FlatBinderObject::SIZE }>)
.ok_or(StatusCode::NotEnoughData)?;
Ok(FlatBinderObject::from_bytes(bytes))
}
pub(crate) fn bytes_at<const N: usize>(bytes: &[u8], at: usize) -> [u8; N] {
let mut field = [0; N];
field.copy_from_slice(&bytes[at..at + N]);
field
}
pub(crate) fn write_flat_binder(
data: &mut [u8],
offset: usize,
obj: &FlatBinderObject,
) -> Result<()> {
let bytes = data
.get_mut(offset..offset + FlatBinderObject::SIZE)
.ok_or(StatusCode::NotEnoughData)?;
bytes.copy_from_slice(&obj.to_bytes());
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn handle_bytes(h: u32) -> [u8; 8] {
let mut b = [0u8; 8];
b[..4].copy_from_slice(&h.to_ne_bytes());
b
}
#[test]
fn new_with_fd_flags_zero_and_full_width_init() {
let fd: i32 = 7;
let obj = FlatBinderObject::new_with_fd(fd, false);
assert_eq!(obj.header_type(), BINDER_TYPE_FD, "must be a FD object");
assert_eq!(
obj.flags, 0,
"FD object flags must be 0 (AOSP Parcel::writeFileDescriptor)"
);
assert_eq!(
obj.pointer().to_ne_bytes(),
handle_bytes(fd as u32),
"the union must hold the fd as `handle` with zeroed upper bytes"
);
assert_eq!(
obj.handle(),
fd as u32,
"handle variant must round-trip the fd"
);
}
#[test]
fn new_handle_full_width_init() {
let handle: u32 = 0xDEAD_BEEF;
let obj = FlatBinderObject::new_handle(handle, 0);
assert_eq!(
obj.header_type(),
BINDER_TYPE_HANDLE,
"must be a HANDLE object"
);
assert_eq!(
obj.pointer().to_ne_bytes(),
handle_bytes(handle),
"the union must hold `handle` with zeroed upper bytes"
);
assert_eq!(
obj.handle(),
handle,
"handle variant must round-trip the handle"
);
}
#[test]
fn handle_acquire_release_do_not_touch_process_state() {
let obj = FlatBinderObject::new_handle(0xDEAD_BEEF, 0);
assert_eq!(obj.acquire(), Ok(()));
assert_eq!(obj.release(), Ok(()));
}
#[test]
fn new_with_fd_cookie_tracks_take_ownership() {
assert_eq!(FlatBinderObject::new_with_fd(3, true).cookie, 1);
assert_eq!(FlatBinderObject::new_with_fd(3, false).cookie, 0);
}
#[test]
fn to_bytes_is_the_uapi_layout_and_round_trips() {
let mut obj = FlatBinderObject::new_handle(0xDEAD_BEEF, 0x17F);
obj.set_cookie(0x0102_0304_0506_0708);
let bytes = obj.to_bytes();
assert_eq!(bytes[0..4], BINDER_TYPE_HANDLE.to_ne_bytes());
assert_eq!(bytes[4..8], 0x17Fu32.to_ne_bytes());
assert_eq!(bytes[8..16], handle_bytes(0xDEAD_BEEF));
assert_eq!(bytes[16..24], 0x0102_0304_0506_0708u64.to_ne_bytes());
assert_eq!(FlatBinderObject::from_bytes(&bytes), obj);
assert_eq!(read_flat_binder(&bytes, 0), Ok(obj));
}
}