#[cfg(not(android_ndk))]
use crate::binder::Stability;
use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, TransactionCode};
use crate::error::{Result, StatusCode, status_result, status_t};
use crate::parcel::{BorrowedParcel, Serialize};
use crate::proxy::SpIBinder;
use crate::sys;
use alloc::boxed::Box;
use core::convert::TryFrom;
use core::ffi::{c_char, c_void};
use core::mem::ManuallyDrop;
use core::ops::Deref;
use core::str;
#[cfg(feature = "std")]
use std::ffi::CStr;
#[cfg(all(feature = "std", not(android_ndk)))]
use std::io::Read;
#[cfg(feature = "std")]
use std::io::Write;
#[repr(C)]
pub struct Binder<T: Remotable> {
ibinder: *mut sys::AIBinder,
rust_object: *mut T,
}
unsafe impl<T: Remotable> Send for Binder<T> {}
unsafe impl<T: Remotable> Sync for Binder<T> {}
impl<T: Remotable> Binder<T> {
pub fn new(rust_object: T) -> Binder<T> {
#[cfg(not(android_ndk))]
{
Self::new_with_stability(rust_object, Stability::default())
}
#[cfg(android_ndk)]
{
Self::new_unmarked(rust_object)
}
}
#[cfg(not(android_ndk))]
pub fn new_with_stability(rust_object: T, stability: Stability) -> Binder<T> {
let mut binder = Self::new_unmarked(rust_object);
binder.mark_stability(stability);
binder
}
fn new_unmarked(rust_object: T) -> Binder<T> {
let class = T::get_class();
let rust_object = Box::into_raw(Box::new(rust_object));
let ibinder = unsafe { sys::AIBinder_new(class.into(), rust_object as *mut c_void) };
Binder { ibinder, rust_object }
}
pub fn set_extension(&mut self, extension: &mut SpIBinder) -> Result<()> {
let status =
unsafe { sys::AIBinder_setExtension(self.as_native_mut(), extension.as_native_mut()) };
status_result(status)
}
pub fn get_descriptor() -> &'static str {
T::get_descriptor()
}
#[cfg(not(android_ndk))]
fn mark_stability(&mut self, stability: Stability) {
match stability {
Stability::Local => self.mark_local_stability(),
Stability::Vintf => {
unsafe {
sys::AIBinder_markVintfStability(self.as_native_mut());
}
}
}
}
#[cfg(android_vendor)]
fn mark_local_stability(&mut self) {
unsafe {
sys::AIBinder_markVendorStability(self.as_native_mut());
}
}
#[cfg(not(any(android_vendor, android_ndk)))]
fn mark_local_stability(&mut self) {
unsafe {
sys::AIBinder_markSystemStability(self.as_native_mut());
}
}
}
impl<T: Remotable> Interface for Binder<T> {
fn as_binder(&self) -> SpIBinder {
unsafe {
sys::AIBinder_incStrong(self.ibinder);
SpIBinder::from_raw(self.ibinder).unwrap()
}
}
#[cfg(feature = "std")]
fn dump(&self, writer: &mut dyn Write, args: &[&CStr]) -> Result<()> {
self.on_dump(writer, args)
}
#[cfg(all(feature = "std", not(android_ndk)))]
fn shell_command(
&self,
stdin: &mut dyn Read,
stdout: &mut dyn Write,
stderr: &mut dyn Write,
args: &[&CStr],
) -> Result<()> {
self.on_shell_command(stdin, stdout, stderr, args)
}
}
impl<T: Remotable> InterfaceClassMethods for Binder<T> {
fn get_descriptor() -> &'static str {
<T as Remotable>::get_descriptor()
}
unsafe extern "C" fn on_transact(
binder: *mut sys::AIBinder,
code: u32,
data: *const sys::AParcel,
reply: *mut sys::AParcel,
) -> status_t {
let res = {
let mut reply = unsafe { BorrowedParcel::from_raw(reply).unwrap() };
let data = unsafe { BorrowedParcel::from_raw(data as *mut sys::AParcel).unwrap() };
let object = unsafe { sys::AIBinder_getUserData(binder) };
let binder: &T = unsafe { &*(object as *const T) };
binder.on_transact(code, &data, &mut reply)
};
match res {
Ok(()) => 0i32,
Err(e) => e as i32,
}
}
unsafe extern "C" fn on_destroy(object: *mut c_void) {
drop(unsafe { Box::from_raw(object as *mut T) });
}
unsafe extern "C" fn on_create(args: *mut c_void) -> *mut c_void {
args
}
#[cfg(all(not(trusty), feature = "std"))]
unsafe extern "C" fn on_dump(
binder: *mut sys::AIBinder,
fd: i32,
args: *mut *const c_char,
num_args: u32,
) -> status_t {
if fd < 0 {
return StatusCode::UNEXPECTED_NULL as status_t;
}
use std::os::fd::FromRawFd;
let mut file = unsafe { ManuallyDrop::new(std::fs::File::from_raw_fd(fd)) };
if args.is_null() && num_args != 0 {
return StatusCode::UNEXPECTED_NULL as status_t;
}
let args = if args.is_null() || num_args == 0 {
vec![]
} else {
unsafe {
std::slice::from_raw_parts(args, num_args as usize)
.iter()
.map(|s| CStr::from_ptr(*s))
.collect()
}
};
let object = unsafe { sys::AIBinder_getUserData(binder) };
let binder: &T = unsafe { &*(object as *const T) };
let res = binder.on_dump(&mut *file, &args);
match res {
Ok(()) => 0,
Err(e) => e as status_t,
}
}
#[cfg(any(trusty, not(feature = "std")))]
unsafe extern "C" fn on_dump(
_binder: *mut sys::AIBinder,
_fd: i32,
_args: *mut *const c_char,
_num_args: u32,
) -> status_t {
StatusCode::INVALID_OPERATION as status_t
}
#[cfg(all(not(trusty), feature = "std", not(android_ndk)))]
unsafe extern "C" fn on_shell_command(
binder: *mut sys::AIBinder,
stdin: i32,
stdout: i32,
stderr: i32,
args: *mut *const c_char,
num_args: u32,
) -> status_t {
if stdin < 0 || stdout < 0 || stderr < 0 {
return StatusCode::UNEXPECTED_NULL as status_t;
}
use std::fs::File;
use std::os::fd::FromRawFd;
let (mut stdin, mut stdout, mut stderr) = unsafe {
(
ManuallyDrop::new(File::from_raw_fd(stdin)),
ManuallyDrop::new(File::from_raw_fd(stdout)),
ManuallyDrop::new(File::from_raw_fd(stderr)),
)
};
if args.is_null() && num_args != 0 {
return StatusCode::UNEXPECTED_NULL as status_t;
}
let args = if args.is_null() || num_args == 0 {
vec![]
} else {
unsafe {
std::slice::from_raw_parts(args, num_args as usize)
.iter()
.map(|s| CStr::from_ptr(*s))
.collect()
}
};
let object = unsafe { sys::AIBinder_getUserData(binder) };
let binder: &T = unsafe { &*(object as *const T) };
let res = binder.on_shell_command(&mut *stdin, &mut *stdout, &mut *stderr, &args);
match res {
Ok(()) => 0,
Err(e) => e as status_t,
}
}
#[cfg(all(any(trusty, not(feature = "std")), not(android_ndk)))]
unsafe extern "C" fn on_shell_command(
_binder: *mut sys::AIBinder,
_stdin: i32,
_stdout: i32,
_stderr: i32,
_args: *mut *const c_char,
_num_args: u32,
) -> status_t {
StatusCode::INVALID_OPERATION as status_t
}
}
impl<T: Remotable> Drop for Binder<T> {
fn drop(&mut self) {
unsafe {
sys::AIBinder_decStrong(self.ibinder);
}
}
}
impl<T: Remotable> Deref for Binder<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.rust_object }
}
}
impl<B: Remotable> Serialize for Binder<B> {
fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
parcel.write_binder(Some(&self.as_binder()))
}
}
impl<B: Remotable> TryFrom<SpIBinder> for Binder<B> {
type Error = StatusCode;
fn try_from(mut ibinder: SpIBinder) -> Result<Self> {
let class = B::get_class();
if Some(class) != ibinder.get_class() {
return Err(StatusCode::BAD_TYPE);
}
let userdata = unsafe { sys::AIBinder_getUserData(ibinder.as_native_mut()) };
if userdata.is_null() {
return Err(StatusCode::UNEXPECTED_NULL);
}
let mut ibinder = ManuallyDrop::new(ibinder);
Ok(Binder { ibinder: ibinder.as_native_mut(), rust_object: userdata as *mut B })
}
}
unsafe impl<B: Remotable> AsNative<sys::AIBinder> for Binder<B> {
fn as_native(&self) -> *const sys::AIBinder {
self.ibinder
}
fn as_native_mut(&mut self) -> *mut sys::AIBinder {
self.ibinder
}
}
impl Remotable for () {
fn get_descriptor() -> &'static str {
""
}
fn on_transact(
&self,
_code: TransactionCode,
_data: &BorrowedParcel<'_>,
_reply: &mut BorrowedParcel<'_>,
) -> Result<()> {
Ok(())
}
#[cfg(feature = "std")]
fn on_dump(&self, _writer: &mut dyn Write, _args: &[&CStr]) -> Result<()> {
Ok(())
}
#[cfg(all(feature = "std", not(android_ndk)))]
fn on_shell_command(
&self,
_stdin: &mut dyn std::io::Read,
_stdout: &mut dyn Write,
_stderr: &mut dyn Write,
_args: &[&CStr],
) -> Result<()> {
Ok(())
}
binder_fn_get_class!(Binder::<Self>);
}
impl Interface for () {}
#[cfg(feature = "std")]
#[cfg(test)]
mod tests {
use super::*;
struct TestRemotable;
impl Remotable for TestRemotable {
fn get_descriptor() -> &'static str {
"test"
}
fn on_transact(
&self,
_code: TransactionCode,
_data: &BorrowedParcel<'_>,
_reply: &mut BorrowedParcel<'_>,
) -> Result<()> {
Ok(())
}
fn on_dump(&self, writer: &mut dyn Write, args: &[&CStr]) -> Result<()> {
write!(writer, "TestRemotable dumped with {:?}", args).unwrap();
Ok(())
}
#[cfg(not(android_ndk))]
fn on_shell_command(
&self,
_stdin: &mut dyn std::io::Read,
stdout: &mut dyn Write,
_stderr: &mut dyn Write,
args: &[&CStr],
) -> Result<()> {
write!(stdout, "TestRemotable shell command with {:?}", args).unwrap();
Ok(())
}
binder_fn_get_class!(Binder::<Self>);
}
#[test]
fn dump() {
let binder_object = Binder::new(TestRemotable);
let mut buffer: Vec<u8> = Vec::new();
binder_object.dump(&mut buffer, &[c"arg1", c"arg2"]).unwrap();
assert_eq!(str::from_utf8(&buffer), Ok("TestRemotable dumped with [\"arg1\", \"arg2\"]"));
}
#[test]
#[cfg(not(android_ndk))]
fn shell_command() {
let binder_object = Binder::new(TestRemotable);
let mut input: &[u8] = &[];
let mut output: Vec<u8> = Vec::new();
let mut error: Vec<u8> = Vec::new();
binder_object
.shell_command(&mut input, &mut output, &mut error, &[c"arg1", c"arg2"])
.unwrap();
assert_eq!(
str::from_utf8(&output),
Ok("TestRemotable shell command with [\"arg1\", \"arg2\"]")
);
}
}