#[cfg(target_os = "android")]
mod imp {
use crate::CoreError;
use crate::dex;
use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd};
use std::os::raw::{c_char, c_void};
use std::sync::atomic::{AtomicI32, AtomicU32, AtomicUsize, Ordering};
use std::sync::Mutex;
const STATUS_OK: i32 = 0;
const STATUS_UNKNOWN_TRANSACTION: i32 = -2;
const EX_NONE: i32 = 0;
const AM_DESCRIPTOR: &[u8] = b"android.app.IActivityManager\0";
const OBS_DESCRIPTOR: &[u8] = b"android.app.IProcessObserver\0";
const FGPROC_DESCRIPTOR: &[u8] = b"android.app.IForegroundProcessObserver\0";
const ACTIVITY_SERVICE: &[u8] = b"activity\0";
#[cfg(target_pointer_width = "64")]
const LIBBINDER_PATH: &[u8] = b"/system/lib64/libbinder_ndk.so\0";
#[cfg(target_pointer_width = "32")]
const LIBBINDER_PATH: &[u8] = b"/system/lib/libbinder_ndk.so\0";
static OBS_FG_CODE: AtomicU32 = AtomicU32::new(0);
static OBS_EVENTFD: Mutex<Option<OwnedFd>> = Mutex::new(None);
fn obs_eventfd_guard() -> std::sync::MutexGuard<'static, Option<OwnedFd>> {
OBS_EVENTFD.lock().unwrap_or_else(|p| p.into_inner())
}
static FGPROC_FG_CODE: AtomicU32 = AtomicU32::new(0);
static FGPROC_PID: AtomicI32 = AtomicI32::new(0);
static FGPROC_EVENTFD: Mutex<Option<OwnedFd>> = Mutex::new(None);
static FGPROC_READ_I32: AtomicUsize = AtomicUsize::new(0);
static FGPROC_IPROC_MODE: AtomicU32 = AtomicU32::new(0);
fn fgproc_eventfd_guard() -> std::sync::MutexGuard<'static, Option<OwnedFd>> {
FGPROC_EVENTFD.lock().unwrap_or_else(|p| p.into_inner())
}
type AIBinder = c_void;
#[allow(non_camel_case_types)]
type AIBinder_Class = c_void;
type AParcel = c_void;
type BinderStatus = i32;
type StringAllocator = unsafe extern "C" fn(*mut c_void, i32, *mut *mut c_char) -> bool;
unsafe extern "C" fn am_on_create(_: *mut c_void) -> *mut c_void { std::ptr::null_mut() }
unsafe extern "C" fn am_on_destroy(_: *mut c_void) {}
unsafe extern "C" fn am_on_transact(
_: *mut AIBinder, _: u32, _: *const AParcel, _: *mut AParcel,
) -> BinderStatus { STATUS_UNKNOWN_TRANSACTION }
unsafe extern "C" fn obs_on_create(_: *mut c_void) -> *mut c_void { std::ptr::null_mut() }
unsafe extern "C" fn obs_on_destroy(_: *mut c_void) {}
unsafe extern "C" fn obs_on_transact(
_: *mut AIBinder, code: u32, _: *const AParcel, _: *mut AParcel,
) -> BinderStatus {
if code == OBS_FG_CODE.load(Ordering::Relaxed) {
if let Some(fd) = obs_eventfd_guard().as_ref() {
let val: u64 = 1;
unsafe { libc::write(fd.as_raw_fd(), &val as *const u64 as *const c_void, 8) };
}
}
STATUS_OK
}
unsafe extern "C" fn fgproc_on_create(_: *mut c_void) -> *mut c_void { std::ptr::null_mut() }
unsafe extern "C" fn fgproc_on_destroy(_: *mut c_void) {}
unsafe extern "C" fn fgproc_on_transact(
_: *mut AIBinder, code: u32, in_parcel: *const AParcel, _: *mut AParcel,
) -> BinderStatus {
if code != FGPROC_FG_CODE.load(Ordering::Relaxed) {
return STATUS_UNKNOWN_TRANSACTION;
}
let read_addr = FGPROC_READ_I32.load(Ordering::Relaxed);
if read_addr != 0 {
let read_fn: unsafe extern "C" fn(*const AParcel, *mut i32) -> BinderStatus =
unsafe { std::mem::transmute(read_addr) };
if FGPROC_IPROC_MODE.load(Ordering::Relaxed) == 1 {
let mut pid: i32 = 0;
let mut _uid: i32 = 0;
let mut fg: i32 = 0;
let mut ok = unsafe { read_fn(in_parcel, &mut pid) } == STATUS_OK;
ok &= unsafe { read_fn(in_parcel, &mut _uid) } == STATUS_OK;
ok &= unsafe { read_fn(in_parcel, &mut fg) } == STATUS_OK;
if ok {
FGPROC_PID.store(pid, Ordering::Relaxed);
if fg == 0 {
return STATUS_OK;
}
} else {
return STATUS_OK;
}
} else {
let mut pid: i32 = 0;
if unsafe { read_fn(in_parcel, &mut pid) } == STATUS_OK {
FGPROC_PID.store(pid, Ordering::Relaxed);
}
}
}
if let Some(fd) = fgproc_eventfd_guard().as_ref() {
let val: u64 = 1;
unsafe { libc::write(fd.as_raw_fd(), &val as *const u64 as *const c_void, 8) };
}
STATUS_OK
}
pub fn last_foreground_pid() -> i32 {
FGPROC_PID.load(Ordering::Relaxed)
}
unsafe extern "C" fn string_alloc(
cookie: *mut c_void, length: i32, buffer: *mut *mut c_char,
) -> bool {
if length < 0 { return true; }
let s = unsafe { &mut *(cookie as *mut StringBuf) };
s.0.reserve_exact(length as usize + 1);
unsafe { s.0.as_mut_vec().resize(length as usize + 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) }
}
}
struct Vtable {
get_service: unsafe extern "C" fn(*const c_char) -> *mut 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: unsafe extern "C" fn(*mut AIBinder, *mut AIBinder_Class) -> bool,
new_binder: unsafe extern "C" fn(*const AIBinder_Class, *mut c_void) -> *mut AIBinder,
prepare_transaction: unsafe extern "C" fn(*mut AIBinder, *mut *mut AParcel) -> BinderStatus,
transact: unsafe extern "C" fn(*mut AIBinder, u32, *mut *mut AParcel, *mut *mut AParcel, u32) -> BinderStatus,
dec_strong: unsafe extern "C" fn(*mut AIBinder),
parcel_delete: unsafe extern "C" fn(*mut AParcel),
read_int32: unsafe extern "C" fn(*const AParcel, *mut i32) -> BinderStatus,
read_string: unsafe extern "C" fn(*const AParcel, *mut c_void, StringAllocator) -> BinderStatus,
write_strong_binder: unsafe extern "C" fn(*mut AParcel, *mut AIBinder) -> BinderStatus,
set_thread_pool_max: unsafe extern "C" fn(u32),
join_thread_pool: unsafe extern "C" fn(),
get_user_data: unsafe extern "C" fn(*const AIBinder) -> *mut c_void,
write_int32: unsafe extern "C" fn(*mut AParcel, i32) -> BinderStatus,
read_bool: Option<unsafe extern "C" fn(*const AParcel, *mut bool) -> BinderStatus>,
}
struct DlHandle(*mut c_void);
unsafe impl Send for DlHandle {}
impl Drop for DlHandle {
fn drop(&mut self) {
}
}
struct OwnedParcel { ptr: *mut AParcel, 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) }; } }
}
struct OwnedBinder { ptr: *mut AIBinder, dec_strong: unsafe extern "C" fn(*mut AIBinder) }
unsafe impl Send for OwnedBinder {}
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) }) }
}};
}
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),
read_bool: dlsym_opt!(handle, "AParcel_readBool",
unsafe extern "C" fn(*const AParcel, *mut bool) -> BinderStatus),
})
}
struct ParcelReader<'a> { vt: &'a Vtable, parcel: &'a OwnedParcel }
impl<'a> ParcelReader<'a> {
fn read_i32(&self) -> Result<i32, CoreError> {
let mut v = 0i32;
let s = unsafe { (self.vt.read_int32)(self.parcel.ptr, &mut v) };
if s != STATUS_OK { return Err(CoreError::binder(s, "AParcel_readInt32")); }
Ok(v)
}
fn read_string(&self) -> Result<Option<String>, CoreError> {
let mut buf = StringBuf::new();
let s = unsafe {
(self.vt.read_string)(self.parcel.ptr, &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())
}
fn skip_i32s(&self, n: usize) -> Result<(), CoreError> {
for _ in 0..n { self.read_i32()?; }
Ok(())
}
fn skip_int_array(&self) -> Result<(), CoreError> {
let count = self.read_i32()?.max(0) as usize;
self.skip_i32s(count)
}
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)
}
}
fn parse_stack_info_body(r: &ParcelReader<'_>) -> Result<Option<String>, CoreError> {
r.skip_i32s(5)?;
r.skip_int_array()?;
r.read_first_package_from_names()
}
fn parse_root_task_info_task(r: &ParcelReader<'_>) -> Result<(i32, Option<String>), CoreError> {
let scratch = r.read_i32()?;
if scratch != 0 { r.skip_i32s(4)?; }
r.skip_int_array()?; let pkg = r.read_first_package_from_names()?; let bounds_count = r.read_i32()?;
let n = if bounds_count < 0 { 0 } else { bounds_count as usize };
for _ in 0..n {
let entry = r.read_i32()?;
if entry != 0 { r.skip_i32s(4)?; }
}
r.skip_int_array()?; r.skip_i32s(2)?; r.skip_i32s(1)?; let task_id = r.read_i32()?;
Ok((task_id, pkg))
}
pub struct TxCodes {
pub observer_code: u32,
pub query_code: u32,
pub api_mode: u8, pub fg_code: u32,
}
pub fn resolve_tx_codes() -> Result<TxCodes, CoreError> {
let (obs, query, api, fg) = dex::resolve_tx_codes_from_dex()
.ok_or_else(|| CoreError::binder(-1, "tx_code_resolution:dex_parse_failed"))?;
Ok(TxCodes { observer_code: obs, query_code: query, api_mode: api, fg_code: fg })
}
pub struct ActivityManagerBinder {
_lib: DlHandle,
vt: Vtable,
_class: *mut AIBinder_Class,
service: OwnedBinder,
tx_code: u32,
legacy: bool,
}
unsafe impl Send for ActivityManagerBinder {}
impl ActivityManagerBinder {
fn open_inner(handle: *mut c_void) -> Result<(DlHandle, Vtable, *mut AIBinder_Class, OwnedBinder), CoreError> {
let lib = DlHandle(handle);
let vt = load_vtable(handle)?;
let am_class = unsafe {
(vt.class_define)(
AM_DESCRIPTOR.as_ptr() as *const c_char,
am_on_create, am_on_destroy, am_on_transact,
)
};
if am_class.is_null() { return Err(CoreError::binder(-1, "AIBinder_Class_define:AM")); }
let raw = unsafe { (vt.get_service)(ACTIVITY_SERVICE.as_ptr() as *const c_char) };
if raw.is_null() { return Err(CoreError::binder(-1, "AServiceManager_getService:activity")); }
unsafe { (vt.associate_class)(raw, am_class) };
let service = OwnedBinder { ptr: raw, dec_strong: vt.dec_strong };
Ok((lib, vt, am_class, service))
}
fn dlopen_libbinder() -> Result<*mut c_void, CoreError> {
use std::os::raw::c_char;
let handle = unsafe {
libc::dlopen(LIBBINDER_PATH.as_ptr() as *const c_char, libc::RTLD_NOW | libc::RTLD_LOCAL)
};
if handle.is_null() { return Err(CoreError::binder(-1, "dlopen:libbinder_ndk.so")); }
Ok(handle)
}
pub fn open() -> Result<Self, CoreError> {
let handle = Self::dlopen_libbinder()?;
let (lib, vt, class, service) = Self::open_inner(handle)?;
let codes = resolve_tx_codes()?;
let legacy = codes.api_mode == 2;
Ok(Self { _lib: lib, vt, _class: class, service, tx_code: codes.query_code, legacy })
}
pub fn open_with_observer() -> Result<(Self, OwnedFd), CoreError> {
let handle = Self::dlopen_libbinder()?;
let (lib, vt, am_class, service) = Self::open_inner(handle)?;
let codes = resolve_tx_codes()?;
let legacy = codes.api_mode == 2;
let owned = unsafe {
let raw = libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC);
if raw < 0 { return Err(CoreError::sys(*libc::__errno(), "eventfd")); }
OwnedFd::from_raw_fd(raw)
};
let obs_class = unsafe {
(vt.class_define)(
OBS_DESCRIPTOR.as_ptr() as *const c_char,
obs_on_create, obs_on_destroy, obs_on_transact,
)
};
if obs_class.is_null() {
return Err(CoreError::binder(-1, "AIBinder_Class_define:Observer"));
}
let obs_binder = unsafe { (vt.new_binder)(obs_class, std::ptr::null_mut()) };
if obs_binder.is_null() {
return Err(CoreError::binder(-1, "AIBinder_new:Observer"));
}
unsafe { (vt.associate_class)(obs_binder, obs_class) };
let mut in_ptr: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (vt.prepare_transaction)(service.ptr, &mut in_ptr) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "prepareTransaction:registerObserver"));
}
unsafe { (vt.write_strong_binder)(in_ptr, obs_binder) };
let mut out_ptr: *mut AParcel = std::ptr::null_mut();
let s = unsafe {
(vt.transact)(service.ptr, codes.observer_code, &mut in_ptr, &mut out_ptr, 0)
};
if !out_ptr.is_null() { unsafe { (vt.parcel_delete)(out_ptr) }; }
if s != STATUS_OK {
return Err(CoreError::binder(s, "transact:registerProcessObserver"));
}
let consumer = owned.try_clone()
.map_err(|e| CoreError::sys(e.raw_os_error().unwrap_or(-1), "dup:observer"))?;
OBS_FG_CODE.store(codes.fg_code, Ordering::Relaxed);
*obs_eventfd_guard() = Some(owned);
unsafe { (vt.set_thread_pool_max)(0) };
let join_fn = vt.join_thread_pool;
std::thread::spawn(move || unsafe { join_fn() });
let binder = Self { _lib: lib, vt, _class: am_class, service, tx_code: codes.query_code, legacy };
Ok((binder, consumer))
}
pub fn open_with_fgproc_observer() -> Result<(Self, OwnedFd), CoreError> {
let handle = Self::dlopen_libbinder()?;
let (lib, vt, am_class, service) = Self::open_inner(handle)?;
let (register_code, fgproc_code, mode, descriptor): (u32, u32, u32, &[u8]) =
match crate::dex::resolve_fgproc_codes() {
Some((r, c)) => (r, c, 0, FGPROC_DESCRIPTOR),
None => match crate::dex::resolve_fgproc_codes_fallback() {
Some((r, c)) => (r, c, 1, OBS_DESCRIPTOR),
None => {
return Err(CoreError::binder(-1, "tx_code_resolution:fgproc_dex_parse_failed"));
}
},
};
let owned = unsafe {
let raw = libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC);
if raw < 0 { return Err(CoreError::sys(*libc::__errno(), "eventfd")); }
OwnedFd::from_raw_fd(raw)
};
let obs_class = unsafe {
(vt.class_define)(
descriptor.as_ptr() as *const c_char,
fgproc_on_create, fgproc_on_destroy, fgproc_on_transact,
)
};
if obs_class.is_null() {
return Err(CoreError::binder(-1, "AIBinder_Class_define:FGProcessObserver"));
}
let obs_binder = unsafe { (vt.new_binder)(obs_class, std::ptr::null_mut()) };
if obs_binder.is_null() {
return Err(CoreError::binder(-1, "AIBinder_new:FGProcessObserver"));
}
unsafe { (vt.associate_class)(obs_binder, obs_class) };
let mut in_ptr: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (vt.prepare_transaction)(service.ptr, &mut in_ptr) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "prepareTransaction:registerForegroundProcessObserver"));
}
unsafe { (vt.write_strong_binder)(in_ptr, obs_binder) };
let mut out_ptr: *mut AParcel = std::ptr::null_mut();
let s = unsafe {
(vt.transact)(service.ptr, register_code, &mut in_ptr, &mut out_ptr, 0)
};
if !out_ptr.is_null() { unsafe { (vt.parcel_delete)(out_ptr) }; }
if s != STATUS_OK {
return Err(CoreError::binder(s, "transact:registerForegroundProcessObserver"));
}
let consumer = owned.try_clone()
.map_err(|e| CoreError::sys(e.raw_os_error().unwrap_or(-1), "dup:fgproc_observer"))?;
FGPROC_READ_I32.store(vt.read_int32 as usize, Ordering::Relaxed);
FGPROC_IPROC_MODE.store(mode, Ordering::Relaxed);
FGPROC_FG_CODE.store(fgproc_code, Ordering::Relaxed);
FGPROC_PID.store(0, Ordering::Relaxed);
*fgproc_eventfd_guard() = Some(owned);
unsafe { (vt.set_thread_pool_max)(0) };
let join_fn = vt.join_thread_pool;
std::thread::spawn(move || unsafe { join_fn() });
let binder = Self { _lib: lib, vt, _class: am_class, service, tx_code: 0, legacy: false };
Ok((binder, consumer))
}
fn do_transact(&self) -> Result<OwnedParcel, CoreError> {
let mut in_ptr: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (self.vt.prepare_transaction)(self.service.ptr, &mut in_ptr) };
if s != STATUS_OK { return Err(CoreError::binder(s, "AIBinder_prepareTransaction")); }
let mut out_ptr: *mut AParcel = std::ptr::null_mut();
let s = unsafe {
(self.vt.transact)(self.service.ptr, self.tx_code, &mut in_ptr, &mut out_ptr, 0)
};
let out = OwnedParcel { ptr: out_ptr, delete: self.vt.parcel_delete };
if s != STATUS_OK { return Err(CoreError::binder(s, "AIBinder_transact")); }
Ok(out)
}
pub fn get_focused_task(&self) -> Result<Option<(i32, Option<String>)>, CoreError> {
if self.legacy {
return Ok(None);
}
let out = self.do_transact()?;
let r = ParcelReader { vt: &self.vt, parcel: &out };
let ex = r.read_i32()?;
if ex != EX_NONE { return Err(CoreError::binder(ex, "getFocusedTask:exception")); }
let present = r.read_i32()?;
if present == 0 { return Ok(None); }
Ok(Some(parse_root_task_info_task(&r)?))
}
pub fn get_focused_package(&self) -> Result<Option<String>, CoreError> {
if self.legacy {
let out = self.do_transact()?;
let r = ParcelReader { vt: &self.vt, parcel: &out };
let ex = r.read_i32()?;
if ex != EX_NONE { return Err(CoreError::binder(ex, "getFocusedTask:exception")); }
let present = r.read_i32()?;
if present == 0 { return Ok(None); }
return parse_stack_info_body(&r);
}
Ok(self.get_focused_task()?.map(|(_, pkg)| pkg).flatten())
}
pub fn get_focused_task_id(&self) -> Result<Option<i32>, CoreError> {
Ok(self.get_focused_task()?.map(|(task_id, _)| task_id))
}
}
const DISPLAY_SERVICE: &[u8] = b"display\0";
const DISPLAY_DESCRIPTOR: &[u8] = b"android.hardware.display.IDisplayManager\0";
const CALLBACK_DESCRIPTOR: &[u8] = b"android.hardware.display.IDisplayManagerCallback\0";
const POWER_SERVICE: &[u8] = b"power\0";
const TX_DISPLAY_REGISTER_CALLBACK: u32 = 4;
static DISP_EVENTFD: Mutex<Option<OwnedFd>> = Mutex::new(None);
fn disp_eventfd_guard() -> std::sync::MutexGuard<'static, Option<OwnedFd>> {
DISP_EVENTFD.lock().unwrap_or_else(|p| p.into_inner())
}
unsafe extern "C" fn disp_cb_on_create(_: *mut c_void) -> *mut c_void { std::ptr::null_mut() }
unsafe extern "C" fn disp_cb_on_destroy(_: *mut c_void) {}
unsafe extern "C" fn disp_cb_on_transact(
_: *mut AIBinder, code: u32, _: *const AParcel, _: *mut AParcel,
) -> BinderStatus {
if code == 1 {
if let Some(fd) = disp_eventfd_guard().as_ref() {
let val: u64 = 1;
unsafe { libc::write(fd.as_raw_fd(), &val as *const u64 as *const c_void, 8) };
}
}
STATUS_OK
}
pub struct DisplayManagerBinder {
_lib: DlHandle,
vt: Vtable,
display: OwnedBinder,
power: Option<OwnedBinder>,
is_interactive_tx: u32,
}
unsafe impl Send for DisplayManagerBinder {}
impl DisplayManagerBinder {
pub fn open_with_callback() -> Result<(Self, crate::reactor::Fd), CoreError> {
let handle = unsafe {
libc::dlopen(LIBBINDER_PATH.as_ptr() as *const c_char, libc::RTLD_NOW | libc::RTLD_LOCAL)
};
if handle.is_null() { return Err(CoreError::binder(-1, "dlopen:libbinder_ndk.so")); }
let lib = DlHandle(handle);
let vt = load_vtable(handle)?;
let owned = unsafe {
let raw = libc::eventfd(0, libc::EFD_CLOEXEC);
if raw < 0 { return Err(CoreError::sys(*libc::__errno(), "eventfd")); }
OwnedFd::from_raw_fd(raw)
};
let raw_display = unsafe { (vt.get_service)(DISPLAY_SERVICE.as_ptr() as *const c_char) };
if raw_display.is_null() {
return Err(CoreError::binder(-1, "AServiceManager_getService:display"));
}
let display = OwnedBinder { ptr: raw_display, dec_strong: vt.dec_strong };
let cb_class = unsafe {
(vt.class_define)(
CALLBACK_DESCRIPTOR.as_ptr() as *const c_char,
disp_cb_on_create, disp_cb_on_destroy, disp_cb_on_transact,
)
};
if cb_class.is_null() {
return Err(CoreError::binder(-1, "AIBinder_Class_define:DisplayCallback"));
}
let cb_binder = unsafe { (vt.new_binder)(cb_class, std::ptr::null_mut()) };
if cb_binder.is_null() {
return Err(CoreError::binder(-1, "AIBinder_new:DisplayCallback"));
}
let mut in_ptr: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (vt.prepare_transaction)(display.ptr, &mut in_ptr) };
if s != STATUS_OK {
return Err(CoreError::binder(s, "prepareTransaction:registerCallback"));
}
unsafe { (vt.write_strong_binder)(in_ptr, cb_binder) };
let mut out_ptr: *mut AParcel = std::ptr::null_mut();
let s = unsafe {
(vt.transact)(display.ptr, TX_DISPLAY_REGISTER_CALLBACK, &mut in_ptr, &mut out_ptr, 0)
};
if !out_ptr.is_null() { unsafe { (vt.parcel_delete)(out_ptr) }; }
if s != STATUS_OK {
return Err(CoreError::binder(s, "transact:registerCallback"));
}
let power = {
let raw = unsafe { (vt.get_service)(POWER_SERVICE.as_ptr() as *const c_char) };
if raw.is_null() { None } else { Some(OwnedBinder { ptr: raw, dec_strong: vt.dec_strong }) }
};
let is_interactive_tx = crate::dex::resolve_is_interactive_tx()
.ok_or_else(|| CoreError::binder(-1, "dex:TRANSACTION_isInteractive not found"))?;
let efd_owned = owned.try_clone()
.map_err(|e| CoreError::sys(e.raw_os_error().unwrap_or(-1), "dup:display"))
.and_then(|dup| unsafe {
crate::reactor::Fd::from_owned_raw_fd(dup.into_raw_fd(), "display.efd")
.map_err(|_| CoreError::binder(-1, "Fd::from_owned_raw_fd:display.efd"))
})?;
*disp_eventfd_guard() = Some(owned);
unsafe { (vt.set_thread_pool_max)(0) };
let join_fn = vt.join_thread_pool;
std::thread::spawn(move || unsafe { join_fn() });
Ok((Self { _lib: lib, vt, display, power, is_interactive_tx }, efd_owned))
}
pub fn is_interactive(&self) -> Result<bool, CoreError> {
let power = self.power.as_ref()
.ok_or_else(|| CoreError::binder(-1, "power:unavailable"))?;
let mut inp: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (self.vt.prepare_transaction)(power.ptr, &mut inp) };
if s != STATUS_OK { return Err(CoreError::binder(s, "prepareTransaction:isInteractive")); }
let mut out: *mut AParcel = std::ptr::null_mut();
let s = unsafe {
(self.vt.transact)(power.ptr, self.is_interactive_tx, &mut inp, &mut out, 0)
};
let out = OwnedParcel { ptr: out, delete: self.vt.parcel_delete };
if s != STATUS_OK { return Err(CoreError::binder(s, "transact:isInteractive")); }
let r = ParcelReader { vt: &self.vt, parcel: &out };
let ex = r.read_i32()?;
if ex != EX_NONE { return Err(CoreError::binder(ex, "isInteractive:exception")); }
if let Some(rb) = self.vt.read_bool {
let mut v = false;
let s = unsafe { rb(out.ptr as *const AParcel, &mut v) };
if s != STATUS_OK { return Err(CoreError::binder(s, "readBool:isInteractive")); }
Ok(v)
} else {
Ok(r.read_i32()? != 0)
}
}
}
const WINDOW_SERVICE: &[u8] = b"window\0";
const WM_DESCRIPTOR: &[u8] = b"android.view.IWindowManager\0";
const FPS_DESCRIPTOR: &[u8] = b"android.window.ITaskFpsCallback\0";
static FPS_VALUE: AtomicU32 = AtomicU32::new(0);
static FPS_CODE: AtomicU32 = AtomicU32::new(0);
static FPS_READ_I32: AtomicUsize = AtomicUsize::new(0);
unsafe extern "C" fn wm_on_create(_: *mut c_void) -> *mut c_void { std::ptr::null_mut() }
unsafe extern "C" fn wm_on_destroy(_: *mut c_void) {}
unsafe extern "C" fn wm_on_transact(
_: *mut AIBinder, _: u32, _: *const AParcel, _: *mut AParcel,
) -> BinderStatus { STATUS_OK }
unsafe extern "C" fn fps_on_create(_: *mut c_void) -> *mut c_void { std::ptr::null_mut() }
unsafe extern "C" fn fps_on_destroy(_: *mut c_void) {}
unsafe extern "C" fn fps_on_transact(
binder: *mut AIBinder, code: u32, in_parcel: *const AParcel, _: *mut AParcel,
) -> BinderStatus {
if code != FPS_CODE.load(Ordering::Relaxed) {
return STATUS_UNKNOWN_TRANSACTION;
}
let read_addr = FPS_READ_I32.load(Ordering::Relaxed);
if read_addr != 0 {
let read_fn: unsafe extern "C" fn(*const AParcel, *mut i32) -> BinderStatus =
unsafe { std::mem::transmute(read_addr) };
let mut bits: i32 = 0;
if unsafe { read_fn(in_parcel, &mut bits) } == STATUS_OK {
FPS_VALUE.store(bits as u32, Ordering::Relaxed);
let get_user_data = GET_USER_DATA
.lock()
.unwrap_or_else(|p| p.into_inner());
if let Some(get_user_data) = *get_user_data {
let userdata = unsafe { get_user_data(binder) };
if !userdata.is_null() {
let efd = userdata as *mut OwnedFd;
let val: u64 = 1;
unsafe {
libc::write((*efd).as_raw_fd(), &val as *const u64 as *const c_void, 8)
};
}
}
}
}
STATUS_OK
}
pub struct FpsListener {
_lib: DlHandle,
vt: Vtable,
window: OwnedBinder,
cb_binder: *mut AIBinder,
_wm_class: *mut AIBinder_Class,
register_code: u32,
unregister_code: u32,
task_id: i32,
}
unsafe impl Send for FpsListener {}
impl FpsListener {
pub fn open() -> Result<(Self, OwnedFd), CoreError> {
let handle = unsafe {
libc::dlopen(LIBBINDER_PATH.as_ptr() as *const c_char, libc::RTLD_NOW | libc::RTLD_LOCAL)
};
if handle.is_null() { return Err(CoreError::binder(-1, "dlopen:libbinder_ndk.so")); }
let lib = DlHandle(handle);
let vt = load_vtable(handle)?;
let (register_code, unregister_code, on_fps_code) =
crate::dex::resolve_fps_codes()
.ok_or_else(|| CoreError::binder(-1, "dex:TRANSACTION_registerTaskFpsCallback not found"))?;
let window = {
let raw = unsafe { (vt.get_service)(WINDOW_SERVICE.as_ptr() as *const c_char) };
if raw.is_null() { return Err(CoreError::binder(-1, "AServiceManager_getService:window")); }
OwnedBinder { ptr: raw, dec_strong: vt.dec_strong }
};
let wm_class = unsafe {
(vt.class_define)(
WM_DESCRIPTOR.as_ptr() as *const c_char,
wm_on_create, wm_on_destroy, wm_on_transact,
)
};
if wm_class.is_null() {
return Err(CoreError::binder(-1, "AIBinder_Class_define:IWindowManager"));
}
unsafe { (vt.associate_class)(window.ptr, wm_class) };
let cb_class = unsafe {
(vt.class_define)(
FPS_DESCRIPTOR.as_ptr() as *const c_char,
fps_on_create, fps_on_destroy, fps_on_transact,
)
};
if cb_class.is_null() {
return Err(CoreError::binder(-1, "AIBinder_Class_define:ITaskFpsCallback"));
}
let owned = unsafe {
let raw = libc::eventfd(0, libc::EFD_CLOEXEC);
if raw < 0 { return Err(CoreError::sys(*libc::__errno(), "eventfd")); }
OwnedFd::from_raw_fd(raw)
};
let consumer = owned.try_clone()
.map_err(|e| CoreError::sys(e.raw_os_error().unwrap_or(-1), "dup:fps"))?;
let userdata = Box::into_raw(Box::new(owned)) as *mut c_void;
let cb_binder = unsafe { (vt.new_binder)(cb_class, userdata) };
if cb_binder.is_null() {
unsafe { drop(Box::from_raw(userdata as *mut OwnedFd)) };
return Err(CoreError::binder(-1, "AIBinder_new:ITaskFpsCallback"));
}
unsafe { (vt.associate_class)(cb_binder, cb_class) };
FPS_READ_I32.store(vt.read_int32 as usize, Ordering::Relaxed);
FPS_CODE.store(on_fps_code, Ordering::Relaxed);
FPS_VALUE.store(0, Ordering::Relaxed);
*GET_USER_DATA.lock().unwrap_or_else(|p| p.into_inner()) = Some(vt.get_user_data);
unsafe { (vt.set_thread_pool_max)(0) };
let join_fn = vt.join_thread_pool;
std::thread::spawn(move || unsafe { join_fn() });
Ok((Self {
_lib: lib, vt, window, cb_binder, _wm_class: wm_class,
register_code, unregister_code, task_id: -1,
}, consumer))
}
pub fn register(&mut self, task_id: i32) -> Result<(), CoreError> {
if self.task_id == task_id {
return Ok(());
}
if self.task_id >= 0 {
let _ = self.unregister();
}
let mut inp: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (self.vt.prepare_transaction)(self.window.ptr, &mut inp) };
if s != STATUS_OK { return Err(CoreError::binder(s, "prepareTransaction:registerTaskFpsCallback")); }
let s = unsafe { (self.vt.write_int32)(inp, task_id) };
if s != STATUS_OK { return Err(CoreError::binder(s, "AParcel_writeInt32:taskId")); }
let s = unsafe { (self.vt.write_strong_binder)(inp, self.cb_binder) };
if s != STATUS_OK { return Err(CoreError::binder(s, "AParcel_writeStrongBinder")); }
let mut out: *mut AParcel = std::ptr::null_mut();
let s = unsafe {
(self.vt.transact)(self.window.ptr, self.register_code, &mut inp, &mut out, 0)
};
if !out.is_null() { unsafe { (self.vt.parcel_delete)(out) }; }
if s != STATUS_OK {
return Err(CoreError::binder(s, "transact:registerTaskFpsCallback"));
}
self.task_id = task_id;
Ok(())
}
pub fn unregister(&mut self) -> Result<(), CoreError> {
if self.task_id < 0 {
return Ok(());
}
let mut inp: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (self.vt.prepare_transaction)(self.window.ptr, &mut inp) };
if s != STATUS_OK { return Err(CoreError::binder(s, "prepareTransaction:unregisterTaskFpsCallback")); }
let s = unsafe { (self.vt.write_strong_binder)(inp, self.cb_binder) };
if s != STATUS_OK { return Err(CoreError::binder(s, "AParcel_writeStrongBinder")); }
let mut out: *mut AParcel = std::ptr::null_mut();
let s = unsafe {
(self.vt.transact)(self.window.ptr, self.unregister_code, &mut inp, &mut out, 0)
};
if !out.is_null() { unsafe { (self.vt.parcel_delete)(out) }; }
if s != STATUS_OK {
return Err(CoreError::binder(s, "transact:unregisterTaskFpsCallback"));
}
self.task_id = -1;
Ok(())
}
pub fn last_fps(&self) -> Option<f32> {
let bits = FPS_VALUE.load(Ordering::Relaxed);
if bits == 0 { None } else { Some(f32::from_bits(bits)) }
}
pub fn task_id(&self) -> Option<i32> {
(self.task_id >= 0).then_some(self.task_id)
}
}
impl Drop for FpsListener {
fn drop(&mut self) {
let _ = self.unregister();
}
}
const TASK_SERVICE: &[u8] = b"activity_task\0";
const ATM_DESCRIPTOR: &[u8] = b"android.app.IActivityTaskManager\0";
const TASK_STACK_DESCRIPTOR: &[u8] = b"android.app.ITaskStackListener\0";
unsafe extern "C" fn atm_on_create(_: *mut c_void) -> *mut c_void { std::ptr::null_mut() }
unsafe extern "C" fn atm_on_destroy(_: *mut c_void) {}
unsafe extern "C" fn atm_on_transact(
_: *mut AIBinder, _: u32, _: *const AParcel, _: *mut AParcel,
) -> BinderStatus { STATUS_OK }
static GET_USER_DATA: std::sync::Mutex<Option<unsafe extern "C" fn(*const AIBinder) -> *mut c_void>> =
std::sync::Mutex::new(None);
unsafe extern "C" fn task_stack_on_create(userdata: *mut c_void) -> *mut c_void { userdata }
unsafe extern "C" fn task_stack_on_destroy(userdata: *mut c_void) {
if !userdata.is_null() {
unsafe { drop(Box::from_raw(userdata as *mut OwnedFd)) };
}
}
unsafe extern "C" fn task_stack_on_transact(
binder: *mut AIBinder, _code: u32, _in_parcel: *const AParcel, _reply: *mut AParcel,
) -> BinderStatus {
let get_user_data = GET_USER_DATA
.lock()
.unwrap_or_else(|p| p.into_inner());
if let Some(get_user_data) = *get_user_data {
let userdata = unsafe { get_user_data(binder) };
if !userdata.is_null() {
let efd = userdata as *mut OwnedFd;
let val: u64 = 1;
unsafe { libc::write((*efd).as_raw_fd(), &val as *const u64 as *const c_void, 8) };
}
}
STATUS_OK
}
pub struct TaskStackListener {
_lib: DlHandle,
vt: Vtable,
service: OwnedBinder,
cb_binder: *mut AIBinder,
_atm_class: *mut AIBinder_Class,
register_code: u32,
unregister_code: u32,
}
unsafe impl Send for TaskStackListener {}
impl TaskStackListener {
pub fn open() -> Result<(Self, OwnedFd), CoreError> {
let handle = unsafe {
libc::dlopen(LIBBINDER_PATH.as_ptr() as *const c_char, libc::RTLD_NOW | libc::RTLD_LOCAL)
};
if handle.is_null() { return Err(CoreError::binder(-1, "dlopen:libbinder_ndk.so")); }
let lib = DlHandle(handle);
let vt = load_vtable(handle)?;
let (register_code, unregister_code) =
crate::dex::resolve_task_stack_codes()
.ok_or_else(|| CoreError::binder(-1, "dex:TRANSACTION_registerTaskStackListener not found"))?;
let service = {
let raw = unsafe { (vt.get_service)(TASK_SERVICE.as_ptr() as *const c_char) };
if raw.is_null() { return Err(CoreError::binder(-1, "AServiceManager_getService:activity_task")); }
OwnedBinder { ptr: raw, dec_strong: vt.dec_strong }
};
let atm_class = unsafe {
(vt.class_define)(
ATM_DESCRIPTOR.as_ptr() as *const c_char,
atm_on_create, atm_on_destroy, atm_on_transact,
)
};
if atm_class.is_null() {
return Err(CoreError::binder(-1, "AIBinder_Class_define:IActivityTaskManager"));
}
unsafe { (vt.associate_class)(service.ptr, atm_class) };
let cb_class = unsafe {
(vt.class_define)(
TASK_STACK_DESCRIPTOR.as_ptr() as *const c_char,
task_stack_on_create, task_stack_on_destroy, task_stack_on_transact,
)
};
if cb_class.is_null() {
return Err(CoreError::binder(-1, "AIBinder_Class_define:ITaskStackListener"));
}
let owned = unsafe {
let raw = libc::eventfd(0, libc::EFD_CLOEXEC);
if raw < 0 { return Err(CoreError::sys(*libc::__errno(), "eventfd")); }
OwnedFd::from_raw_fd(raw)
};
let consumer = owned.try_clone()
.map_err(|e| CoreError::sys(e.raw_os_error().unwrap_or(-1), "dup:task_stack"))?;
let userdata = Box::into_raw(Box::new(owned)) as *mut c_void;
let cb_binder = unsafe { (vt.new_binder)(cb_class, userdata) };
if cb_binder.is_null() {
unsafe { drop(Box::from_raw(userdata as *mut OwnedFd)) };
return Err(CoreError::binder(-1, "AIBinder_new:ITaskStackListener"));
}
unsafe { (vt.associate_class)(cb_binder, cb_class) };
*GET_USER_DATA.lock().unwrap_or_else(|p| p.into_inner()) = Some(vt.get_user_data);
unsafe { (vt.set_thread_pool_max)(0) };
let join_fn = vt.join_thread_pool;
std::thread::spawn(move || unsafe { join_fn() });
Ok((Self {
_lib: lib, vt, service, cb_binder, _atm_class: atm_class,
register_code, unregister_code,
}, consumer))
}
pub fn register(&self) -> Result<(), CoreError> {
let mut inp: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (self.vt.prepare_transaction)(self.service.ptr, &mut inp) };
if s != STATUS_OK { return Err(CoreError::binder(s, "prepareTransaction:registerTaskStackListener")); }
let s = unsafe { (self.vt.write_strong_binder)(inp, self.cb_binder) };
if s != STATUS_OK { return Err(CoreError::binder(s, "AParcel_writeStrongBinder")); }
let mut out: *mut AParcel = std::ptr::null_mut();
let s = unsafe {
(self.vt.transact)(self.service.ptr, self.register_code, &mut inp, &mut out, 0)
};
if !out.is_null() { unsafe { (self.vt.parcel_delete)(out) }; }
if s != STATUS_OK {
return Err(CoreError::binder(s, "transact:registerTaskStackListener"));
}
Ok(())
}
pub fn unregister(&self) -> Result<(), CoreError> {
let mut inp: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (self.vt.prepare_transaction)(self.service.ptr, &mut inp) };
if s != STATUS_OK { return Err(CoreError::binder(s, "prepareTransaction:unregisterTaskStackListener")); }
let s = unsafe { (self.vt.write_strong_binder)(inp, self.cb_binder) };
if s != STATUS_OK { return Err(CoreError::binder(s, "AParcel_writeStrongBinder")); }
let mut out: *mut AParcel = std::ptr::null_mut();
let s = unsafe {
(self.vt.transact)(self.service.ptr, self.unregister_code, &mut inp, &mut out, 0)
};
if !out.is_null() { unsafe { (self.vt.parcel_delete)(out) }; }
if s != STATUS_OK {
return Err(CoreError::binder(s, "transact:unregisterTaskStackListener"));
}
Ok(())
}
}
impl Drop for TaskStackListener {
fn drop(&mut self) {
let _ = self.unregister();
}
}
pub struct RawBinderService {
_lib: DlHandle,
vt: Vtable,
service: OwnedBinder,
}
unsafe impl Send for RawBinderService {}
impl RawBinderService {
pub fn open(service_name: &str) -> Result<Self, CoreError> {
use std::ffi::CString;
let handle = unsafe {
libc::dlopen(LIBBINDER_PATH.as_ptr() as *const c_char, libc::RTLD_NOW | libc::RTLD_LOCAL)
};
if handle.is_null() {
return Err(CoreError::binder(-1, "dlopen:libbinder_ndk.so"));
}
let lib = DlHandle(handle);
let vt = load_vtable(handle)?;
let cs = CString::new(service_name)
.map_err(|_| CoreError::binder(-1, "service_name:nul_byte"))?;
let raw = unsafe { (vt.get_service)(cs.as_ptr()) };
if raw.is_null() {
return Err(CoreError::binder(-1, "AServiceManager_getService:null"));
}
let service = OwnedBinder { ptr: raw, dec_strong: vt.dec_strong };
Ok(Self { _lib: lib, vt, service })
}
pub fn transact_bool(&self, code: u32) -> Result<bool, CoreError> {
let out = self.raw_noarg(code)?;
let r = ParcelReader { vt: &self.vt, parcel: &out };
let ex = r.read_i32()?;
if ex != EX_NONE { return Err(CoreError::binder(ex, "transact_bool:exception")); }
if let Some(rb) = self.vt.read_bool {
let mut v = false;
let s = unsafe { rb(out.ptr as *const AParcel, &mut v) };
if s != STATUS_OK { return Err(CoreError::binder(s, "AParcel_readBool")); }
Ok(v)
} else {
Ok(r.read_i32()? != 0)
}
}
pub fn transact_i32(&self, code: u32, arg: i32) -> Result<(), CoreError> {
let mut inp: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (self.vt.prepare_transaction)(self.service.ptr, &mut inp) };
if s != STATUS_OK { return Err(CoreError::binder(s, "AIBinder_prepareTransaction")); }
let s = unsafe { (self.vt.write_int32)(inp, arg) };
if s != STATUS_OK { return Err(CoreError::binder(s, "AParcel_writeInt32")); }
let mut out: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (self.vt.transact)(self.service.ptr, code, &mut inp, &mut out, 0) };
if !out.is_null() { unsafe { (self.vt.parcel_delete)(out) }; }
if s != STATUS_OK { return Err(CoreError::binder(s, "AIBinder_transact")); }
Ok(())
}
fn raw_noarg(&self, code: u32) -> Result<OwnedParcel, CoreError> {
let mut inp: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (self.vt.prepare_transaction)(self.service.ptr, &mut inp) };
if s != STATUS_OK { return Err(CoreError::binder(s, "AIBinder_prepareTransaction")); }
let mut out: *mut AParcel = std::ptr::null_mut();
let s = unsafe { (self.vt.transact)(self.service.ptr, code, &mut inp, &mut out, 0) };
let out = OwnedParcel { ptr: out, delete: self.vt.parcel_delete };
if s != STATUS_OK { return Err(CoreError::binder(s, "AIBinder_transact")); }
Ok(out)
}
}
}
#[cfg(target_os = "android")]
pub use imp::{ActivityManagerBinder, DisplayManagerBinder, FpsListener, RawBinderService, TaskStackListener, TxCodes, last_foreground_pid, resolve_tx_codes};
#[cfg(not(target_os = "android"))]
pub struct ActivityManagerBinder;
#[cfg(not(target_os = "android"))]
impl ActivityManagerBinder {
pub fn open() -> Result<Self, crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
pub fn open_with_observer() -> Result<(Self, std::os::fd::OwnedFd), crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
pub fn open_with_fgproc_observer() -> Result<(Self, std::os::fd::OwnedFd), crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
pub fn get_focused_task(&self) -> Result<Option<(i32, Option<String>)>, crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
pub fn get_focused_package(&self) -> Result<Option<String>, crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
pub fn get_focused_task_id(&self) -> Result<Option<i32>, crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
}
#[cfg(not(target_os = "android"))]
pub struct DisplayManagerBinder;
#[cfg(not(target_os = "android"))]
impl DisplayManagerBinder {
pub fn open_with_callback() -> Result<(Self, crate::reactor::Fd), crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
pub fn is_interactive(&self) -> Result<bool, crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
}
#[cfg(not(target_os = "android"))]
pub struct RawBinderService;
#[cfg(not(target_os = "android"))]
impl RawBinderService {
pub fn open(_service_name: &str) -> Result<Self, crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
pub fn transact_bool(&self, _code: u32) -> Result<bool, crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
pub fn transact_i32(&self, _code: u32, _arg: i32) -> Result<(), crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
}
#[cfg(not(target_os = "android"))]
pub struct FpsListener;
#[cfg(not(target_os = "android"))]
impl FpsListener {
pub fn open() -> Result<(Self, std::os::fd::OwnedFd), crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
pub fn register(&mut self, _task_id: i32) -> Result<(), crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
pub fn unregister(&mut self) -> Result<(), crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
pub fn last_fps(&self) -> Option<f32> {
None
}
pub fn task_id(&self) -> Option<i32> {
None
}
}
#[cfg(not(target_os = "android"))]
pub struct TaskStackListener;
#[cfg(not(target_os = "android"))]
impl TaskStackListener {
pub fn open() -> Result<(Self, std::os::fd::OwnedFd), crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
pub fn register(&self) -> Result<(), crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
pub fn unregister(&self) -> Result<(), crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
}
#[cfg(not(target_os = "android"))]
pub struct TxCodes { pub observer_code: u32, pub query_code: u32, pub api_mode: u8, pub fg_code: u32 }
#[cfg(not(target_os = "android"))]
pub fn resolve_tx_codes() -> Result<TxCodes, crate::CoreError> {
Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
}
#[cfg(not(target_os = "android"))]
pub fn last_foreground_pid() -> i32 {
0
}