Skip to main content

coreshift_core/binder/
mod.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/
4
5//! NDK binder primitives for querying Android system services.
6//!
7//! Uses `dlopen` on `libbinder_ndk.so` to avoid hard-linking against a library
8//! absent from older NDK toolchains or non-Android targets.
9//!
10//! ## Transaction code resolution
11//!
12//! Transaction codes are resolved fresh from `framework.jar` DEX on each
13//! startup; no persistent cache.
14//!
15//! ## Observer mode
16//!
17//! `ActivityManagerBinder::open_with_observer` registers this process as an
18//! `IProcessObserver` with ActivityManager. Callbacks fire when foreground
19//! activities change and signal an `eventfd` that callers can poll via epoll.
20//! After the eventfd fires, call `get_focused_package` to read the new value.
21
22// ─────────────────────────────────────────────────────────────────────────────
23// Android-only implementation
24// ─────────────────────────────────────────────────────────────────────────────
25
26#[cfg(target_os = "android")]
27mod imp {
28    use crate::CoreError;
29    use crate::dex;
30    use std::os::raw::{c_char, c_void};
31    use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
32
33    // ── NDK binder status codes ───────────────────────────────────────────────
34
35    const STATUS_OK: i32 = 0;
36    const STATUS_UNKNOWN_TRANSACTION: i32 = -2;
37    const EX_NONE: i32 = 0;
38
39    // ── Interface constants ───────────────────────────────────────────────────
40
41    const AM_DESCRIPTOR:    &[u8] = b"android.app.IActivityManager\0";
42    const OBS_DESCRIPTOR:   &[u8] = b"android.app.IProcessObserver\0";
43    const ACTIVITY_SERVICE: &[u8] = b"activity\0";
44    #[cfg(target_pointer_width = "64")]
45    const LIBBINDER_PATH: &[u8] = b"/system/lib64/libbinder_ndk.so\0";
46    #[cfg(target_pointer_width = "32")]
47    const LIBBINDER_PATH: &[u8] = b"/system/lib/libbinder_ndk.so\0";
48
49    // ── Tx code cache ─────────────────────────────────────────────────────────
50    // Format (watcher.c compatible): observer_code query_code api_mode fg_code
51    // api_mode: 1 = getFocusedRootTaskInfo, 2 = getFocusedStackInfo (API 29)
52
53    // ── Statics for observer callback (binder thread pool context) ────────────
54    // These are set once during observer setup before joinThreadPool, and then
55    // only read from the callback. Safe to access from multiple binder threads.
56
57    static OBS_FG_CODE: AtomicU32 = AtomicU32::new(0);
58    static OBS_EVENTFD:  AtomicI32 = AtomicI32::new(-1);
59
60    // ── Raw NDK type aliases ──────────────────────────────────────────────────
61
62    type AIBinder = c_void;
63    #[allow(non_camel_case_types)]
64    type AIBinder_Class = c_void;
65    type AParcel = c_void;
66    type BinderStatus = i32;
67    type StringAllocator = unsafe extern "C" fn(*mut c_void, i32, *mut *mut c_char) -> bool;
68
69    // ── AIBinder_Class callbacks ──────────────────────────────────────────────
70
71    // AM client — no-op server side (we're a client only)
72    unsafe extern "C" fn am_on_create(_: *mut c_void) -> *mut c_void { std::ptr::null_mut() }
73    unsafe extern "C" fn am_on_destroy(_: *mut c_void) {}
74    unsafe extern "C" fn am_on_transact(
75        _: *mut AIBinder, _: u32, _: *const AParcel, _: *mut AParcel,
76    ) -> BinderStatus { STATUS_UNKNOWN_TRANSACTION }
77
78    // IProcessObserver server callbacks
79    unsafe extern "C" fn obs_on_create(_: *mut c_void) -> *mut c_void { std::ptr::null_mut() }
80    unsafe extern "C" fn obs_on_destroy(_: *mut c_void) {}
81    unsafe extern "C" fn obs_on_transact(
82        _: *mut AIBinder, code: u32, _: *const AParcel, _: *mut AParcel,
83    ) -> BinderStatus {
84        if code == OBS_FG_CODE.load(Ordering::Relaxed) {
85            let efd = OBS_EVENTFD.load(Ordering::Relaxed);
86            if efd >= 0 {
87                let val: u64 = 1;
88                unsafe { libc::write(efd, &val as *const u64 as *const c_void, 8) };
89            }
90        }
91        STATUS_OK
92    }
93
94    // ── String allocator ─────────────────────────────────────────────────────
95
96    unsafe extern "C" fn string_alloc(
97        cookie: *mut c_void, length: i32, buffer: *mut *mut c_char,
98    ) -> bool {
99        if length < 0 { return true; }
100        let s = unsafe { &mut *(cookie as *mut StringBuf) };
101        s.0.reserve_exact(length as usize + 1);
102        unsafe { s.0.as_mut_vec().resize(length as usize + 1, 0) };
103        unsafe { *buffer = s.0.as_mut_ptr() as *mut c_char };
104        true
105    }
106
107    struct StringBuf(String);
108    impl StringBuf {
109        fn new() -> Self { Self(String::new()) }
110        fn finish(mut self) -> Option<String> {
111            if let Some(pos) = self.0.as_bytes().iter().position(|&b| b == 0) {
112                unsafe { self.0.as_mut_vec().truncate(pos) };
113            }
114            if self.0.is_empty() { None } else { Some(self.0) }
115        }
116    }
117
118    // ── Vtable ────────────────────────────────────────────────────────────────
119
120    struct Vtable {
121        get_service:         unsafe extern "C" fn(*const c_char) -> *mut AIBinder,
122        class_define:        unsafe extern "C" fn(
123                                 *const c_char,
124                                 unsafe extern "C" fn(*mut c_void) -> *mut c_void,
125                                 unsafe extern "C" fn(*mut c_void),
126                                 unsafe extern "C" fn(*mut AIBinder, u32, *const AParcel, *mut AParcel) -> BinderStatus,
127                             ) -> *mut AIBinder_Class,
128        associate_class:     unsafe extern "C" fn(*mut AIBinder, *mut AIBinder_Class) -> bool,
129        new_binder:          unsafe extern "C" fn(*const AIBinder_Class, *mut c_void) -> *mut AIBinder,
130        prepare_transaction: unsafe extern "C" fn(*mut AIBinder, *mut *mut AParcel) -> BinderStatus,
131        transact:            unsafe extern "C" fn(*mut AIBinder, u32, *mut *mut AParcel, *mut *mut AParcel, u32) -> BinderStatus,
132        dec_strong:          unsafe extern "C" fn(*mut AIBinder),
133        parcel_delete:       unsafe extern "C" fn(*mut AParcel),
134        read_int32:          unsafe extern "C" fn(*const AParcel, *mut i32) -> BinderStatus,
135        read_string:         unsafe extern "C" fn(*const AParcel, *mut c_void, StringAllocator) -> BinderStatus,
136        write_strong_binder: unsafe extern "C" fn(*mut AParcel, *mut AIBinder) -> BinderStatus,
137        set_thread_pool_max: unsafe extern "C" fn(u32),
138        join_thread_pool:    unsafe extern "C" fn(),
139        write_int32:         unsafe extern "C" fn(*mut AParcel, i32) -> BinderStatus,
140        // Optional: only present on API 29+, but all modern Android has this
141        read_bool:           Option<unsafe extern "C" fn(*const AParcel, *mut bool) -> BinderStatus>,
142    }
143
144    // ── RAII wrappers ─────────────────────────────────────────────────────────
145
146    struct DlHandle(*mut c_void);
147    unsafe impl Send for DlHandle {}
148    impl Drop for DlHandle {
149        fn drop(&mut self) {
150            // Intentionally no dlclose: the binder thread pool spawned in
151            // open_with_observer() keeps executing library code until process
152            // exit. Unloading the library while that thread runs causes
153            // use-after-free. libbinder_ndk.so is never unloaded during the
154            // daemon lifetime; the OS reclaims it on exit.
155        }
156    }
157
158    struct OwnedParcel { ptr: *mut AParcel, delete: unsafe extern "C" fn(*mut AParcel) }
159    impl Drop for OwnedParcel {
160        fn drop(&mut self) { if !self.ptr.is_null() { unsafe { (self.delete)(self.ptr) }; } }
161    }
162
163    struct OwnedBinder { ptr: *mut AIBinder, dec_strong: unsafe extern "C" fn(*mut AIBinder) }
164    unsafe impl Send for OwnedBinder {}
165    impl Drop for OwnedBinder {
166        fn drop(&mut self) { if !self.ptr.is_null() { unsafe { (self.dec_strong)(self.ptr) }; } }
167    }
168
169    // ── dlsym helper ─────────────────────────────────────────────────────────
170
171    macro_rules! dlsym_fn {
172        ($handle:expr, $name:literal, $ty:ty) => {{
173            let sym = unsafe {
174                libc::dlsym($handle, concat!($name, "\0").as_ptr() as *const c_char)
175            };
176            if sym.is_null() {
177                return Err(CoreError::binder(-1, concat!("dlsym:", $name)));
178            }
179            unsafe { std::mem::transmute::<*mut c_void, $ty>(sym) }
180        }};
181    }
182
183    macro_rules! dlsym_opt {
184        ($handle:expr, $name:literal, $ty:ty) => {{
185            let sym = unsafe {
186                libc::dlsym($handle, concat!($name, "\0").as_ptr() as *const c_char)
187            };
188            if sym.is_null() { None }
189            else { Some(unsafe { std::mem::transmute::<*mut c_void, $ty>(sym) }) }
190        }};
191    }
192
193    fn load_vtable(handle: *mut c_void) -> Result<Vtable, CoreError> {
194        Ok(Vtable {
195            get_service: dlsym_fn!(handle, "AServiceManager_getService",
196                unsafe extern "C" fn(*const c_char) -> *mut AIBinder),
197            class_define: dlsym_fn!(handle, "AIBinder_Class_define",
198                unsafe extern "C" fn(
199                    *const c_char,
200                    unsafe extern "C" fn(*mut c_void) -> *mut c_void,
201                    unsafe extern "C" fn(*mut c_void),
202                    unsafe extern "C" fn(*mut AIBinder, u32, *const AParcel, *mut AParcel) -> BinderStatus,
203                ) -> *mut AIBinder_Class),
204            associate_class: dlsym_fn!(handle, "AIBinder_associateClass",
205                unsafe extern "C" fn(*mut AIBinder, *mut AIBinder_Class) -> bool),
206            new_binder: dlsym_fn!(handle, "AIBinder_new",
207                unsafe extern "C" fn(*const AIBinder_Class, *mut c_void) -> *mut AIBinder),
208            prepare_transaction: dlsym_fn!(handle, "AIBinder_prepareTransaction",
209                unsafe extern "C" fn(*mut AIBinder, *mut *mut AParcel) -> BinderStatus),
210            transact: dlsym_fn!(handle, "AIBinder_transact",
211                unsafe extern "C" fn(*mut AIBinder, u32, *mut *mut AParcel, *mut *mut AParcel, u32) -> BinderStatus),
212            dec_strong: dlsym_fn!(handle, "AIBinder_decStrong",
213                unsafe extern "C" fn(*mut AIBinder)),
214            parcel_delete: dlsym_fn!(handle, "AParcel_delete",
215                unsafe extern "C" fn(*mut AParcel)),
216            read_int32: dlsym_fn!(handle, "AParcel_readInt32",
217                unsafe extern "C" fn(*const AParcel, *mut i32) -> BinderStatus),
218            read_string: dlsym_fn!(handle, "AParcel_readString",
219                unsafe extern "C" fn(*const AParcel, *mut c_void, StringAllocator) -> BinderStatus),
220            write_strong_binder: dlsym_fn!(handle, "AParcel_writeStrongBinder",
221                unsafe extern "C" fn(*mut AParcel, *mut AIBinder) -> BinderStatus),
222            set_thread_pool_max: dlsym_fn!(handle, "ABinderProcess_setThreadPoolMaxThreadCount",
223                unsafe extern "C" fn(u32)),
224            join_thread_pool: dlsym_fn!(handle, "ABinderProcess_joinThreadPool",
225                unsafe extern "C" fn()),
226            write_int32: dlsym_fn!(handle, "AParcel_writeInt32",
227                unsafe extern "C" fn(*mut AParcel, i32) -> BinderStatus),
228            read_bool: dlsym_opt!(handle, "AParcel_readBool",
229                unsafe extern "C" fn(*const AParcel, *mut bool) -> BinderStatus),
230        })
231    }
232
233    // ── ParcelReader ──────────────────────────────────────────────────────────
234
235    struct ParcelReader<'a> { vt: &'a Vtable, parcel: &'a OwnedParcel }
236
237    impl<'a> ParcelReader<'a> {
238        fn read_i32(&self) -> Result<i32, CoreError> {
239            let mut v = 0i32;
240            let s = unsafe { (self.vt.read_int32)(self.parcel.ptr, &mut v) };
241            if s != STATUS_OK { return Err(CoreError::binder(s, "AParcel_readInt32")); }
242            Ok(v)
243        }
244        fn read_string(&self) -> Result<Option<String>, CoreError> {
245            let mut buf = StringBuf::new();
246            let s = unsafe {
247                (self.vt.read_string)(self.parcel.ptr, &mut buf as *mut StringBuf as *mut c_void, string_alloc)
248            };
249            if s != STATUS_OK { return Err(CoreError::binder(s, "AParcel_readString")); }
250            Ok(buf.finish())
251        }
252        fn skip_i32s(&self, n: usize) -> Result<(), CoreError> {
253            for _ in 0..n { self.read_i32()?; }
254            Ok(())
255        }
256        fn skip_int_array(&self) -> Result<(), CoreError> {
257            let count = self.read_i32()?.max(0) as usize;
258            self.skip_i32s(count)
259        }
260        fn read_first_package_from_names(&self) -> Result<Option<String>, CoreError> {
261            let count = self.read_i32()?.max(0) as usize;
262            let mut first: Option<String> = None;
263            for _ in 0..count {
264                let s = self.read_string()?;
265                if first.is_none() {
266                    first = s.and_then(|c| c.split('/').next().map(str::to_owned));
267                }
268            }
269            Ok(first)
270        }
271    }
272
273    // ── Response parsers ──────────────────────────────────────────────────────
274
275    fn parse_root_task_info_body(r: &ParcelReader<'_>) -> Result<Option<String>, CoreError> {
276        let scratch = r.read_i32()?;
277        if scratch != 0 { r.skip_i32s(4)?; }
278        r.skip_int_array()?;
279        r.read_first_package_from_names()
280    }
281
282    fn parse_stack_info_body(r: &ParcelReader<'_>) -> Result<Option<String>, CoreError> {
283        r.skip_i32s(5)?;
284        r.skip_int_array()?;
285        r.read_first_package_from_names()
286    }
287
288    // ── Tx code resolution ────────────────────────────────────────────────────
289
290    pub struct TxCodes {
291        pub observer_code: u32,
292        pub query_code:    u32,
293        pub api_mode:      u8,  // 1 = RootTaskInfo, 2 = StackInfo
294        pub fg_code:       u32,
295    }
296
297    pub fn resolve_tx_codes() -> Result<TxCodes, CoreError> {
298        let (obs, query, api, fg) = dex::resolve_tx_codes_from_dex()
299            .ok_or_else(|| CoreError::binder(-1, "tx_code_resolution:dex_parse_failed"))?;
300        Ok(TxCodes { observer_code: obs, query_code: query, api_mode: api, fg_code: fg })
301    }
302
303    // ── ActivityManagerBinder ─────────────────────────────────────────────────
304
305    pub struct ActivityManagerBinder {
306        _lib:    DlHandle,
307        vt:      Vtable,
308        _class:  *mut AIBinder_Class,
309        service: OwnedBinder,
310        tx_code: u32,
311        legacy:  bool,
312    }
313    unsafe impl Send for ActivityManagerBinder {}
314
315    impl ActivityManagerBinder {
316        fn open_inner(handle: *mut c_void) -> Result<(DlHandle, Vtable, *mut AIBinder_Class, OwnedBinder), CoreError> {
317            let lib = DlHandle(handle);
318            let vt = load_vtable(handle)?;
319
320            let am_class = unsafe {
321                (vt.class_define)(
322                    AM_DESCRIPTOR.as_ptr() as *const c_char,
323                    am_on_create, am_on_destroy, am_on_transact,
324                )
325            };
326            if am_class.is_null() { return Err(CoreError::binder(-1, "AIBinder_Class_define:AM")); }
327
328            let raw = unsafe { (vt.get_service)(ACTIVITY_SERVICE.as_ptr() as *const c_char) };
329            if raw.is_null() { return Err(CoreError::binder(-1, "AServiceManager_getService:activity")); }
330            unsafe { (vt.associate_class)(raw, am_class) };
331
332            let service = OwnedBinder { ptr: raw, dec_strong: vt.dec_strong };
333            Ok((lib, vt, am_class, service))
334        }
335
336        fn dlopen_libbinder() -> Result<*mut c_void, CoreError> {
337            use std::os::raw::c_char;
338            let handle = unsafe {
339                libc::dlopen(LIBBINDER_PATH.as_ptr() as *const c_char, libc::RTLD_NOW | libc::RTLD_LOCAL)
340            };
341            if handle.is_null() { return Err(CoreError::binder(-1, "dlopen:libbinder_ndk.so")); }
342            Ok(handle)
343        }
344
345        /// Open ActivityManager binder (polling mode — no observer).
346        /// Resolves the query tx code from cache or DEX.
347        pub fn open() -> Result<Self, CoreError> {
348            let handle = Self::dlopen_libbinder()?;
349            let (lib, vt, class, service) = Self::open_inner(handle)?;
350            let codes = resolve_tx_codes()?;
351            let legacy = codes.api_mode == 2;
352            Ok(Self { _lib: lib, vt, _class: class, service, tx_code: codes.query_code, legacy })
353        }
354
355        /// Open ActivityManager binder and register as IProcessObserver.
356        ///
357        /// Returns `(Self, eventfd_raw_fd)`. The eventfd becomes readable
358        /// whenever `onForegroundActivitiesChanged` fires. Caller must add it
359        /// to epoll. After the event fires, call `get_focused_package`.
360        pub fn open_with_observer() -> Result<(Self, i32), CoreError> {
361            let handle = Self::dlopen_libbinder()?;
362            let (lib, vt, am_class, service) = Self::open_inner(handle)?;
363            let codes = resolve_tx_codes()?;
364            let legacy = codes.api_mode == 2;
365
366            // Create eventfd for callback → epoll bridge
367            let efd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) };
368            if efd < 0 { return Err(CoreError::sys(unsafe { *libc::__errno() }, "eventfd")); }
369
370            // Store fg_code and eventfd in statics for the callback
371            OBS_FG_CODE.store(codes.fg_code, Ordering::Relaxed);
372            OBS_EVENTFD.store(efd, Ordering::Relaxed);
373
374            // Define IProcessObserver class (we're the server)
375            let obs_class = unsafe {
376                (vt.class_define)(
377                    OBS_DESCRIPTOR.as_ptr() as *const c_char,
378                    obs_on_create, obs_on_destroy, obs_on_transact,
379                )
380            };
381            if obs_class.is_null() {
382                unsafe { libc::close(efd) };
383                return Err(CoreError::binder(-1, "AIBinder_Class_define:Observer"));
384            }
385
386            // Instantiate our observer binder object
387            let obs_binder = unsafe { (vt.new_binder)(obs_class, std::ptr::null_mut()) };
388            if obs_binder.is_null() {
389                unsafe { libc::close(efd) };
390                return Err(CoreError::binder(-1, "AIBinder_new:Observer"));
391            }
392            unsafe { (vt.associate_class)(obs_binder, obs_class) };
393
394            // Call registerProcessObserver(observer)
395            let mut in_ptr: *mut AParcel = std::ptr::null_mut();
396            let s = unsafe { (vt.prepare_transaction)(service.ptr, &mut in_ptr) };
397            if s != STATUS_OK {
398                unsafe { libc::close(efd) };
399                return Err(CoreError::binder(s, "prepareTransaction:registerObserver"));
400            }
401            unsafe { (vt.write_strong_binder)(in_ptr, obs_binder) };
402            let mut out_ptr: *mut AParcel = std::ptr::null_mut();
403            let s = unsafe {
404                (vt.transact)(service.ptr, codes.observer_code, &mut in_ptr, &mut out_ptr, 0)
405            };
406            if !out_ptr.is_null() { unsafe { (vt.parcel_delete)(out_ptr) }; }
407            if s != STATUS_OK {
408                unsafe { libc::close(efd) };
409                return Err(CoreError::binder(s, "transact:registerProcessObserver"));
410            }
411
412            // Start binder thread pool — blocks forever in background thread
413            unsafe { (vt.set_thread_pool_max)(0) };
414            let join_fn = vt.join_thread_pool;
415            std::thread::spawn(move || unsafe { join_fn() });
416
417            let binder = Self { _lib: lib, vt, _class: am_class, service, tx_code: codes.query_code, legacy };
418            Ok((binder, efd))
419        }
420
421        fn do_transact(&self) -> Result<OwnedParcel, CoreError> {
422            let mut in_ptr: *mut AParcel = std::ptr::null_mut();
423            let s = unsafe { (self.vt.prepare_transaction)(self.service.ptr, &mut in_ptr) };
424            if s != STATUS_OK { return Err(CoreError::binder(s, "AIBinder_prepareTransaction")); }
425            let mut out_ptr: *mut AParcel = std::ptr::null_mut();
426            let s = unsafe {
427                (self.vt.transact)(self.service.ptr, self.tx_code, &mut in_ptr, &mut out_ptr, 0)
428            };
429            let out = OwnedParcel { ptr: out_ptr, delete: self.vt.parcel_delete };
430            if s != STATUS_OK { return Err(CoreError::binder(s, "AIBinder_transact")); }
431            Ok(out)
432        }
433
434        pub fn get_focused_package(&self) -> Result<Option<String>, CoreError> {
435            let out = self.do_transact()?;
436            let r = ParcelReader { vt: &self.vt, parcel: &out };
437            let ex = r.read_i32()?;
438            if ex != EX_NONE { return Err(CoreError::binder(ex, "getFocusedTask:exception")); }
439            let present = r.read_i32()?;
440            if present == 0 { return Ok(None); }
441            if self.legacy { parse_stack_info_body(&r) } else { parse_root_task_info_body(&r) }
442        }
443    }
444
445    // ── DisplayManagerBinder ─────────────────────────────────────────────────
446
447    const DISPLAY_SERVICE:    &[u8] = b"display\0";
448    const DISPLAY_DESCRIPTOR: &[u8] = b"android.hardware.display.IDisplayManager\0";
449    const CALLBACK_DESCRIPTOR: &[u8] = b"android.hardware.display.IDisplayManagerCallback\0";
450    const POWER_SERVICE:      &[u8] = b"power\0";
451
452    const TX_DISPLAY_REGISTER_CALLBACK: u32 = 4;
453
454    static DISP_EVENTFD: AtomicI32 = AtomicI32::new(-1);
455
456    unsafe extern "C" fn disp_cb_on_create(_: *mut c_void) -> *mut c_void { std::ptr::null_mut() }
457    unsafe extern "C" fn disp_cb_on_destroy(_: *mut c_void) {}
458    unsafe extern "C" fn disp_cb_on_transact(
459        _: *mut AIBinder, code: u32, _: *const AParcel, _: *mut AParcel,
460    ) -> BinderStatus {
461        if code == 1 {
462            let efd = DISP_EVENTFD.load(Ordering::Relaxed);
463            if efd >= 0 {
464                let val: u64 = 1;
465                unsafe { libc::write(efd, &val as *const u64 as *const c_void, 8) };
466            }
467        }
468        STATUS_OK
469    }
470
471    pub struct DisplayManagerBinder {
472        _lib:           DlHandle,
473        vt:             Vtable,
474        display:        OwnedBinder,
475        power:          Option<OwnedBinder>,
476        is_interactive_tx: u32,
477    }
478    unsafe impl Send for DisplayManagerBinder {}
479
480    impl DisplayManagerBinder {
481        pub fn open_with_callback() -> Result<(Self, crate::reactor::Fd), CoreError> {
482            let handle = unsafe {
483                libc::dlopen(LIBBINDER_PATH.as_ptr() as *const c_char, libc::RTLD_NOW | libc::RTLD_LOCAL)
484            };
485            if handle.is_null() { return Err(CoreError::binder(-1, "dlopen:libbinder_ndk.so")); }
486            let lib = DlHandle(handle);
487            let vt = load_vtable(handle)?;
488
489            // Blocking eventfd (no EFD_NONBLOCK) — callback writes, caller's
490            // read_u64_blocking() waits. Raw fd stored in static for the callback.
491            let efd = unsafe { libc::eventfd(0, libc::EFD_CLOEXEC) };
492            if efd < 0 { return Err(CoreError::sys(unsafe { *libc::__errno() }, "eventfd")); }
493            DISP_EVENTFD.store(efd, Ordering::Relaxed);
494
495            // Get display service (no class_define needed for client-only)
496            let raw_display = unsafe { (vt.get_service)(DISPLAY_SERVICE.as_ptr() as *const c_char) };
497            if raw_display.is_null() {
498                unsafe { libc::close(efd) };
499                return Err(CoreError::binder(-1, "AServiceManager_getService:display"));
500            }
501            let display = OwnedBinder { ptr: raw_display, dec_strong: vt.dec_strong };
502
503            // Define IDisplayManagerCallback (we're the server receiving callbacks)
504            let cb_class = unsafe {
505                (vt.class_define)(
506                    CALLBACK_DESCRIPTOR.as_ptr() as *const c_char,
507                    disp_cb_on_create, disp_cb_on_destroy, disp_cb_on_transact,
508                )
509            };
510            if cb_class.is_null() {
511                unsafe { libc::close(efd) };
512                return Err(CoreError::binder(-1, "AIBinder_Class_define:DisplayCallback"));
513            }
514
515            let cb_binder = unsafe { (vt.new_binder)(cb_class, std::ptr::null_mut()) };
516            if cb_binder.is_null() {
517                unsafe { libc::close(efd) };
518                return Err(CoreError::binder(-1, "AIBinder_new:DisplayCallback"));
519            }
520
521            // registerCallback(callback) — tx 4
522            let mut in_ptr: *mut AParcel = std::ptr::null_mut();
523            let s = unsafe { (vt.prepare_transaction)(display.ptr, &mut in_ptr) };
524            if s != STATUS_OK {
525                unsafe { libc::close(efd) };
526                return Err(CoreError::binder(s, "prepareTransaction:registerCallback"));
527            }
528            unsafe { (vt.write_strong_binder)(in_ptr, cb_binder) };
529            let mut out_ptr: *mut AParcel = std::ptr::null_mut();
530            let s = unsafe {
531                (vt.transact)(display.ptr, TX_DISPLAY_REGISTER_CALLBACK, &mut in_ptr, &mut out_ptr, 0)
532            };
533            if !out_ptr.is_null() { unsafe { (vt.parcel_delete)(out_ptr) }; }
534            if s != STATUS_OK {
535                unsafe { libc::close(efd) };
536                return Err(CoreError::binder(s, "transact:registerCallback"));
537            }
538
539            // Optional: grab power service for is_interactive()
540            let power = {
541                let raw = unsafe { (vt.get_service)(POWER_SERVICE.as_ptr() as *const c_char) };
542                if raw.is_null() { None } else { Some(OwnedBinder { ptr: raw, dec_strong: vt.dec_strong }) }
543            };
544
545            // Resolve isInteractive tx code from DEX at open time
546            let is_interactive_tx = crate::dex::resolve_is_interactive_tx()
547                .ok_or_else(|| CoreError::binder(-1, "dex:TRANSACTION_isInteractive not found"))?;
548
549            // Join binder thread pool so callbacks can fire
550            unsafe { (vt.set_thread_pool_max)(0) };
551            let join_fn = vt.join_thread_pool;
552            std::thread::spawn(move || unsafe { join_fn() });
553
554            // Wrap raw fd for safe ownership transfer to caller
555            let efd_owned = unsafe {
556                crate::reactor::Fd::from_owned_raw_fd(efd, "display.efd")
557                    .map_err(|_| CoreError::binder(-1, "Fd::from_owned_raw_fd:display.efd"))?
558            };
559            Ok((Self { _lib: lib, vt, display, power, is_interactive_tx }, efd_owned))
560        }
561
562        pub fn is_interactive(&self) -> Result<bool, CoreError> {
563            let power = self.power.as_ref()
564                .ok_or_else(|| CoreError::binder(-1, "power:unavailable"))?;
565            let mut inp: *mut AParcel = std::ptr::null_mut();
566            let s = unsafe { (self.vt.prepare_transaction)(power.ptr, &mut inp) };
567            if s != STATUS_OK { return Err(CoreError::binder(s, "prepareTransaction:isInteractive")); }
568            let mut out: *mut AParcel = std::ptr::null_mut();
569            let s = unsafe {
570                (self.vt.transact)(power.ptr, self.is_interactive_tx, &mut inp, &mut out, 0)
571            };
572            let out = OwnedParcel { ptr: out, delete: self.vt.parcel_delete };
573            if s != STATUS_OK { return Err(CoreError::binder(s, "transact:isInteractive")); }
574            let r = ParcelReader { vt: &self.vt, parcel: &out };
575            let ex = r.read_i32()?;
576            if ex != EX_NONE { return Err(CoreError::binder(ex, "isInteractive:exception")); }
577            if let Some(rb) = self.vt.read_bool {
578                let mut v = false;
579                let s = unsafe { rb(out.ptr as *const AParcel, &mut v) };
580                if s != STATUS_OK { return Err(CoreError::binder(s, "readBool:isInteractive")); }
581                Ok(v)
582            } else {
583                Ok(r.read_i32()? != 0)
584            }
585        }
586    }
587
588    // ── RawBinderService ──────────────────────────────────────────────────────
589
590    /// Generic binder client for any named Android service.
591    ///
592    /// Handles its own `dlopen` on `libbinder_ndk.so`. Callers provide raw
593    /// transaction codes (resolved via [`crate::dex::find_transaction_code`])
594    /// and use [`RawBinderService::transact_bool`] /
595    /// [`RawBinderService::transact_i32`] for typed round-trips.
596    pub struct RawBinderService {
597        _lib:    DlHandle,
598        vt:      Vtable,
599        service: OwnedBinder,
600    }
601    unsafe impl Send for RawBinderService {}
602
603    impl RawBinderService {
604        /// Open a connection to the named service (e.g. `"power"`, `"batterystats"`).
605        pub fn open(service_name: &str) -> Result<Self, CoreError> {
606            use std::ffi::CString;
607            let handle = unsafe {
608                libc::dlopen(LIBBINDER_PATH.as_ptr() as *const c_char, libc::RTLD_NOW | libc::RTLD_LOCAL)
609            };
610            if handle.is_null() {
611                return Err(CoreError::binder(-1, "dlopen:libbinder_ndk.so"));
612            }
613            let lib = DlHandle(handle);
614            let vt = load_vtable(handle)?;
615            let cs = CString::new(service_name)
616                .map_err(|_| CoreError::binder(-1, "service_name:nul_byte"))?;
617            let raw = unsafe { (vt.get_service)(cs.as_ptr()) };
618            if raw.is_null() {
619                return Err(CoreError::binder(-1, "AServiceManager_getService:null"));
620            }
621            let service = OwnedBinder { ptr: raw, dec_strong: vt.dec_strong };
622            Ok(Self { _lib: lib, vt, service })
623        }
624
625        /// Send a no-argument transaction; read exception header then bool reply.
626        pub fn transact_bool(&self, code: u32) -> Result<bool, CoreError> {
627            let out = self.raw_noarg(code)?;
628            let r = ParcelReader { vt: &self.vt, parcel: &out };
629            let ex = r.read_i32()?;
630            if ex != EX_NONE { return Err(CoreError::binder(ex, "transact_bool:exception")); }
631            if let Some(rb) = self.vt.read_bool {
632                let mut v = false;
633                let s = unsafe { rb(out.ptr as *const AParcel, &mut v) };
634                if s != STATUS_OK { return Err(CoreError::binder(s, "AParcel_readBool")); }
635                Ok(v)
636            } else {
637                Ok(r.read_i32()? != 0)
638            }
639        }
640
641        /// Send a transaction with one i32 argument; discard reply.
642        pub fn transact_i32(&self, code: u32, arg: i32) -> Result<(), CoreError> {
643            let mut inp: *mut AParcel = std::ptr::null_mut();
644            let s = unsafe { (self.vt.prepare_transaction)(self.service.ptr, &mut inp) };
645            if s != STATUS_OK { return Err(CoreError::binder(s, "AIBinder_prepareTransaction")); }
646            let s = unsafe { (self.vt.write_int32)(inp, arg) };
647            if s != STATUS_OK { return Err(CoreError::binder(s, "AParcel_writeInt32")); }
648            let mut out: *mut AParcel = std::ptr::null_mut();
649            let s = unsafe { (self.vt.transact)(self.service.ptr, code, &mut inp, &mut out, 0) };
650            if !out.is_null() { unsafe { (self.vt.parcel_delete)(out) }; }
651            if s != STATUS_OK { return Err(CoreError::binder(s, "AIBinder_transact")); }
652            Ok(())
653        }
654
655        fn raw_noarg(&self, code: u32) -> Result<OwnedParcel, CoreError> {
656            let mut inp: *mut AParcel = std::ptr::null_mut();
657            let s = unsafe { (self.vt.prepare_transaction)(self.service.ptr, &mut inp) };
658            if s != STATUS_OK { return Err(CoreError::binder(s, "AIBinder_prepareTransaction")); }
659            let mut out: *mut AParcel = std::ptr::null_mut();
660            let s = unsafe { (self.vt.transact)(self.service.ptr, code, &mut inp, &mut out, 0) };
661            let out = OwnedParcel { ptr: out, delete: self.vt.parcel_delete };
662            if s != STATUS_OK { return Err(CoreError::binder(s, "AIBinder_transact")); }
663            Ok(out)
664        }
665    }
666}
667
668// ── Public re-exports ─────────────────────────────────────────────────────────
669
670#[cfg(target_os = "android")]
671pub use imp::{ActivityManagerBinder, DisplayManagerBinder, RawBinderService, TxCodes, resolve_tx_codes};
672
673// ── Non-Android stubs ─────────────────────────────────────────────────────────
674
675#[cfg(not(target_os = "android"))]
676pub struct ActivityManagerBinder;
677
678#[cfg(not(target_os = "android"))]
679impl ActivityManagerBinder {
680    pub fn open() -> Result<Self, crate::CoreError> {
681        Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
682    }
683    pub fn open_with_observer() -> Result<(Self, i32), crate::CoreError> {
684        Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
685    }
686    pub fn get_focused_package(&self) -> Result<Option<String>, crate::CoreError> {
687        Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
688    }
689}
690
691#[cfg(not(target_os = "android"))]
692pub struct DisplayManagerBinder;
693
694#[cfg(not(target_os = "android"))]
695impl DisplayManagerBinder {
696    pub fn open_with_callback() -> Result<(Self, crate::reactor::Fd), crate::CoreError> {
697        Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
698    }
699    pub fn is_interactive(&self) -> Result<bool, crate::CoreError> {
700        Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
701    }
702}
703
704#[cfg(not(target_os = "android"))]
705pub struct RawBinderService;
706
707#[cfg(not(target_os = "android"))]
708impl RawBinderService {
709    pub fn open(_service_name: &str) -> Result<Self, crate::CoreError> {
710        Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
711    }
712    pub fn transact_bool(&self, _code: u32) -> Result<bool, crate::CoreError> {
713        Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
714    }
715    pub fn transact_i32(&self, _code: u32, _arg: i32) -> Result<(), crate::CoreError> {
716        Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
717    }
718}
719
720#[cfg(not(target_os = "android"))]
721pub struct TxCodes { pub observer_code: u32, pub query_code: u32, pub api_mode: u8, pub fg_code: u32 }
722
723#[cfg(not(target_os = "android"))]
724pub fn resolve_tx_codes() -> Result<TxCodes, crate::CoreError> {
725    Err(crate::CoreError::binder(-1, "binder:unsupported platform"))
726}