use crate::CoreError;
use std::os::raw::c_void;
pub(crate) const BUNDLE_MAGIC: i32 = 0x4C444E42;
pub(crate) const VAL_IBINDER: i32 = 15;
pub(crate) const BUNDLE_KEY: &str = "binder";
pub(crate) const EXEC_KEY: &str = "exec";
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>;
}
pub(crate) fn write_bundle_body(
w: &impl BundleSink,
has_exec: bool,
service_binder: *mut c_void,
exec_binder: *mut c_void,
) -> Result<(), CoreError> {
w.write_i32(if has_exec { 2 } else { 1 })?;
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)?;
}
Ok(())
}
pub(crate) fn bundle_length(has_exec: bool) -> i32 {
let c = ByteCounter::default();
write_bundle_body(&c, has_exec, std::ptr::null_mut(), std::ptr::null_mut()).unwrap();
c.bytes.get() as i32
}
#[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(())
}
}
#[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 single_bundle_body_is_56() {
assert_eq!(bundle_length(false), 56);
}
#[test]
fn two_bundle_body_is_104() {
assert_eq!(bundle_length(true), 104);
}
#[test]
fn write_path_emits_exactly_the_advertised_length() {
let c = ByteCounter::default();
c.write_i32(bundle_length(true)).unwrap();
c.write_i32(BUNDLE_MAGIC).unwrap();
write_bundle_body(&c, true, 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) as usize);
}
#[test]
fn single_bundle_write_path_is_byte_identical_shape() {
let c = ByteCounter::default();
c.write_i32(bundle_length(false)).unwrap();
c.write_i32(BUNDLE_MAGIC).unwrap();
write_bundle_body(&c, false, std::ptr::null_mut(), std::ptr::null_mut()).unwrap();
assert_eq!(c.bytes.get(), 4 + 4 + 56);
}
}