use crate::CoreError;
use std::os::raw::c_void;
pub(crate) const BUNDLE_MAGIC: i32 = 0x4C444E42;
pub(crate) const EX_NONE: i32 = 0;
pub(crate) const VAL_IBINDER: i32 = 15;
pub(crate) const BUNDLE_KEY: &str = "binder";
pub(crate) const EXEC_KEY: &str = "exec";
pub(crate) const EPOCH_KEY: &str = "epoch";
pub(crate) const VAL_BYTEARRAY: i32 = 13;
pub(crate) const VAL_INTEGER: i32 = 1;
pub(crate) const HANDOFF_STATUS_KEY: &str = "status";
pub(crate) const HANDOFF_STATUS_OK: i32 = 0;
#[cfg(test)]
pub(crate) const HANDOFF_STATUS_REJECTED_UID: i32 = 1;
#[cfg(test)]
pub(crate) const HANDOFF_STATUS_REJECTED_DESCRIPTOR: i32 = 2;
pub(crate) trait BundleSource {
fn read_i32(&self) -> Result<i32, CoreError>;
fn read_string(&self) -> Result<Option<String>, CoreError>;
}
pub(crate) fn read_handoff_status(r: &impl BundleSource) -> Result<i32, CoreError> {
let length = r.read_i32()?;
if length == -1 {
return Ok(HANDOFF_STATUS_OK);
}
let magic = r.read_i32()?;
if magic != BUNDLE_MAGIC {
return Err(CoreError::binder(magic, "call:reply_bad_bundle_magic"));
}
let n = r.read_i32()?;
if n != 1 {
return Err(CoreError::binder(n, "call:reply_unexpected_entry_count"));
}
let key = r.read_string()?;
if key.as_deref() != Some(HANDOFF_STATUS_KEY) {
return Err(CoreError::binder(-1, "call:reply_missing_status_key"));
}
let tag = r.read_i32()?;
if tag != VAL_INTEGER {
return Err(CoreError::binder(tag, "call:reply_status_not_integer"));
}
r.read_i32()
}
pub(crate) trait BundleSink {
fn write_i32(&self, v: i32) -> Result<(), CoreError>;
fn write_string(&self, s: Option<&str>) -> Result<(), CoreError>;
fn write_strong_binder(&self, b: *mut c_void) -> Result<(), CoreError>;
fn write_byte_array(&self, b: &[u8]) -> Result<(), CoreError>;
}
pub(crate) trait RegisterBodySink: BundleSink {
fn write_int32_array(&self, v: &[i32]) -> Result<(), CoreError>;
}
pub(crate) fn write_bundle_body(
w: &impl BundleSink,
has_exec: bool,
epoch: Option<&[u8; 16]>,
service_binder: *mut c_void,
exec_binder: *mut c_void,
) -> Result<(), CoreError> {
let n = if has_exec { 2 } else { 1 } + if epoch.is_some() { 1 } else { 0 };
w.write_i32(n)?;
w.write_string(Some(BUNDLE_KEY))?;
w.write_i32(VAL_IBINDER)?;
w.write_strong_binder(service_binder)?;
if has_exec {
w.write_string(Some(EXEC_KEY))?;
w.write_i32(VAL_IBINDER)?;
w.write_strong_binder(exec_binder)?;
}
if let Some(epoch) = epoch {
w.write_string(Some(EPOCH_KEY))?;
w.write_i32(VAL_BYTEARRAY)?;
w.write_byte_array(epoch)?;
}
Ok(())
}
pub(crate) fn bundle_length(has_exec: bool, epoch: Option<&[u8; 16]>) -> i32 {
let c = ByteCounter::default();
write_bundle_body(
&c,
has_exec,
epoch,
std::ptr::null_mut(),
std::ptr::null_mut(),
)
.unwrap();
c.bytes.get() as i32
}
pub(crate) fn check_reply_exception(ex: i32) -> Result<(), CoreError> {
if ex == EX_NONE {
Ok(())
} else {
Err(CoreError::binder(ex, "register:reply_exception"))
}
}
pub(crate) fn write_uid_observer_body(
w: &impl RegisterBodySink,
which: i32,
cutpoint: i32,
calling_package: Option<&str>,
has_for_uids: bool,
watched_uid: i32,
) -> Result<(), CoreError> {
w.write_i32(which)?;
w.write_i32(cutpoint)?;
w.write_string(calling_package)?;
if has_for_uids {
w.write_int32_array(&[watched_uid])?;
}
Ok(())
}
#[derive(Default)]
struct ByteCounter {
bytes: std::cell::Cell<usize>,
}
fn pad4(n: usize) -> usize {
(n + 3) & !3
}
fn string16_size(s: &str) -> usize {
4 + pad4((s.len() + 1) * 2)
}
impl BundleSink for ByteCounter {
fn write_i32(&self, _v: i32) -> Result<(), CoreError> {
self.bytes.set(self.bytes.get() + 4);
Ok(())
}
fn write_string(&self, s: Option<&str>) -> Result<(), CoreError> {
let n = match s {
None => 4, Some(s) => string16_size(s),
};
self.bytes.set(self.bytes.get() + n);
Ok(())
}
fn write_strong_binder(&self, _b: *mut c_void) -> Result<(), CoreError> {
self.bytes.set(self.bytes.get() + 28);
Ok(())
}
fn write_byte_array(&self, b: &[u8]) -> Result<(), CoreError> {
self.bytes.set(self.bytes.get() + 4 + pad4(b.len()));
Ok(())
}
}
impl RegisterBodySink for ByteCounter {
fn write_int32_array(&self, v: &[i32]) -> Result<(), CoreError> {
self.bytes.set(self.bytes.get() + 4 + 4 * v.len());
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn string16_sizes_match_aosp_write_path() {
assert_eq!(string16_size("binder"), 20);
assert_eq!(string16_size("exec"), 16);
}
#[test]
fn null_string_is_four_bytes() {
let c = ByteCounter::default();
c.write_string(None).unwrap();
assert_eq!(c.bytes.get(), 4);
}
#[test]
fn write_value_tags_pin_aosp_literals() {
assert_eq!(VAL_IBINDER, 15);
assert_eq!(VAL_INTEGER, 1);
assert_eq!(EX_NONE, 0);
}
#[test]
fn single_bundle_body_is_56() {
assert_eq!(bundle_length(false, None), 56);
}
#[test]
fn two_bundle_body_is_104() {
assert_eq!(bundle_length(true, None), 104);
}
#[test]
fn three_bundle_body_with_epoch_is_144() {
let epoch = [0xAB; 16];
assert_eq!(bundle_length(true, Some(&epoch)), 144);
assert_eq!(bundle_length(false, Some(&epoch)), 96);
}
#[test]
fn write_path_emits_exactly_the_advertised_length() {
let c = ByteCounter::default();
c.write_i32(bundle_length(true, None)).unwrap();
c.write_i32(BUNDLE_MAGIC).unwrap();
write_bundle_body(&c, true, None, std::ptr::null_mut(), std::ptr::null_mut()).unwrap();
let total = c.bytes.get();
assert_eq!(total, 4 + 4 + 104);
assert_eq!(c.bytes.get() - 8, bundle_length(true, None) as usize);
}
#[test]
fn epoch_write_path_emits_exactly_the_advertised_length() {
let epoch = [0x11; 16];
let c = ByteCounter::default();
c.write_i32(bundle_length(true, Some(&epoch))).unwrap();
c.write_i32(BUNDLE_MAGIC).unwrap();
write_bundle_body(
&c,
true,
Some(&epoch),
std::ptr::null_mut(),
std::ptr::null_mut(),
)
.unwrap();
assert_eq!(c.bytes.get(), 4 + 4 + 144);
assert_eq!(
c.bytes.get() - 8,
bundle_length(true, Some(&epoch)) as usize
);
}
#[test]
fn single_bundle_write_path_is_byte_identical_shape() {
let c = ByteCounter::default();
c.write_i32(bundle_length(false, None)).unwrap();
c.write_i32(BUNDLE_MAGIC).unwrap();
write_bundle_body(&c, false, None, std::ptr::null_mut(), std::ptr::null_mut()).unwrap();
assert_eq!(c.bytes.get(), 4 + 4 + 56);
}
#[test]
fn reply_ex_none_is_ok() {
assert!(check_reply_exception(EX_NONE).is_ok());
}
#[test]
fn reply_non_ex_none_is_err() {
assert!(check_reply_exception(1).is_err());
assert!(check_reply_exception(-1).is_err());
assert!(check_reply_exception(32).is_err());
}
#[test]
fn synthetic_parcel_leading_i32_is_exception_code() {
fn parse_leading_i32(buf: &[u8]) -> Result<(), CoreError> {
let ex = i32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
check_reply_exception(ex)
}
assert!(parse_leading_i32(&0i32.to_le_bytes()).is_ok());
assert!(parse_leading_i32(&1i32.to_le_bytes()).is_err());
assert!(parse_leading_i32(&(-2i32).to_le_bytes()).is_err());
}
struct ByteCursor<'a> {
buf: &'a [u8],
pos: std::cell::Cell<usize>,
}
impl<'a> ByteCursor<'a> {
fn take(&self, n: usize) -> Result<&'a [u8], CoreError> {
let pos = self.pos.get();
if pos + n > self.buf.len() {
return Err(CoreError::binder(-1, "cursor:past_end"));
}
self.pos.set(pos + n);
Ok(&self.buf[pos..pos + n])
}
}
impl<'a> BundleSource for ByteCursor<'a> {
fn read_i32(&self) -> Result<i32, CoreError> {
let b: &[u8] = self.take(4)?;
Ok(i32::from_le_bytes(b.try_into().unwrap()))
}
fn read_string(&self) -> Result<Option<String>, CoreError> {
let len16 = i32::from_le_bytes(self.take(4)?.try_into().unwrap());
if len16 == -1 {
return Ok(None);
}
if len16 < 0 {
return Err(CoreError::binder(len16, "cursor:bad_string_len"));
}
let padded = ((len16 as usize + 1) * 2).next_multiple_of(4);
let bytes = self.take(padded)?;
let mut u16s = Vec::with_capacity(len16 as usize);
for pair in bytes[..len16 as usize * 2].chunks_exact(2) {
u16s.push(u16::from_le_bytes([pair[0], pair[1]]));
}
Ok(Some(String::from_utf16_lossy(&u16s)))
}
}
fn string16(buf: &mut Vec<u8>, s: &str) {
buf.extend((s.len() as i32).to_le_bytes());
let payload = (s.len() + 1) * 2;
for c in s.chars() {
buf.extend((c as u16).to_le_bytes());
}
buf.extend(0u16.to_le_bytes()); buf.extend(std::iter::repeat_n(
0u8,
payload.next_multiple_of(4) - payload,
));
}
fn status_reply(status: i32) -> Vec<u8> {
let mut b: Vec<u8> = Vec::new();
b.extend(EX_NONE.to_le_bytes());
let body = {
let mut m: Vec<u8> = Vec::new();
m.extend(1i32.to_le_bytes());
string16(&mut m, HANDOFF_STATUS_KEY);
m.extend(VAL_INTEGER.to_le_bytes());
m.extend(status.to_le_bytes());
m
};
b.extend((body.len() as i32).to_le_bytes());
b.extend(BUNDLE_MAGIC.to_le_bytes());
b.extend(body);
b
}
fn null_reply() -> Vec<u8> {
let mut b: Vec<u8> = Vec::new();
b.extend(EX_NONE.to_le_bytes());
b.extend((-1i32).to_le_bytes());
b
}
#[test]
fn status_reply_shape_is_pinned() {
assert_eq!(
status_reply(HANDOFF_STATUS_OK).len(),
4 + 4 + 4 + 4 + 20 + 4 + 4
);
}
#[test]
fn read_handoff_status_ok() {
let c = ByteCursor {
buf: &status_reply(HANDOFF_STATUS_OK),
pos: std::cell::Cell::new(4),
};
assert_eq!(read_handoff_status(&c).unwrap(), HANDOFF_STATUS_OK);
}
#[test]
fn read_handoff_status_rejected_uid() {
let c = ByteCursor {
buf: &status_reply(HANDOFF_STATUS_REJECTED_UID),
pos: std::cell::Cell::new(4),
};
assert_eq!(
read_handoff_status(&c).unwrap(),
HANDOFF_STATUS_REJECTED_UID
);
}
#[test]
fn read_handoff_status_rejected_descriptor() {
let c = ByteCursor {
buf: &status_reply(HANDOFF_STATUS_REJECTED_DESCRIPTOR),
pos: std::cell::Cell::new(4),
};
assert_eq!(
read_handoff_status(&c).unwrap(),
HANDOFF_STATUS_REJECTED_DESCRIPTOR
);
}
#[test]
fn read_handoff_status_legacy_null_reply_is_ok() {
let c = ByteCursor {
buf: &null_reply(),
pos: std::cell::Cell::new(4),
};
assert_eq!(read_handoff_status(&c).unwrap(), HANDOFF_STATUS_OK);
}
#[test]
fn read_handoff_status_bad_magic_fails_closed() {
let mut r = status_reply(HANDOFF_STATUS_OK);
r[8..12].copy_from_slice(&0xBAD0u32.to_le_bytes());
let c = ByteCursor {
buf: &r,
pos: std::cell::Cell::new(4),
};
assert!(read_handoff_status(&c).is_err());
}
#[test]
fn read_handoff_status_unknown_key_fails_closed() {
let mut b: Vec<u8> = Vec::new();
b.extend(EX_NONE.to_le_bytes());
let body = {
let mut m: Vec<u8> = Vec::new();
m.extend(1i32.to_le_bytes());
string16(&mut m, "other");
m.extend(VAL_INTEGER.to_le_bytes());
m.extend(0i32.to_le_bytes());
m
};
b.extend((body.len() as i32).to_le_bytes());
b.extend(BUNDLE_MAGIC.to_le_bytes());
b.extend(body);
let c = ByteCursor {
buf: &b,
pos: std::cell::Cell::new(4),
};
assert!(read_handoff_status(&c).is_err());
}
fn uid_observer_body_len(has_for_uids: bool) -> usize {
let c = ByteCounter::default();
write_uid_observer_body(&c, 0x1E, -1, None, has_for_uids, 10123).unwrap();
c.bytes.get()
}
#[test]
fn uid_observer_for_uids_writes_trailing_uids_array() {
assert_eq!(uid_observer_body_len(true), 20);
}
#[test]
fn uid_observer_fallback_omits_trailing_uids_array() {
assert_eq!(uid_observer_body_len(false), 12);
}
#[test]
fn uid_observer_register_bodies_differ_exactly_by_uids_array() {
assert_eq!(
uid_observer_body_len(true) - uid_observer_body_len(false),
8
);
}
}