cloudfox-coreshift-core 2.13.1

Low-level Linux and Android systems primitives for CoreShift (CloudFox)
Documentation
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/

//! Generic binder client for any named Android service.

use super::sys::*;
use crate::CoreError;
use std::os::raw::c_char;

// ── RawBinderService ──────────────────────────────────────────────────────

/// Generic binder client for any named Android service.
///
/// Handles its own `dlopen` on `libbinder_ndk.so`. Callers provide raw
/// transaction codes (resolved via [`crate::android::dex::find_transaction_code`])
/// and use [`RawBinderService::transact_bool`] /
/// [`RawBinderService::transact_i32`] for typed round-trips.
pub struct RawBinderService {
    _lib: DlHandle,
    vt: Vtable,
    service: OwnedBinder,
}
unsafe impl Send for RawBinderService {}

impl RawBinderService {
    /// Open a connection to the named service (e.g. `"power"`, `"batterystats"`).
    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;
        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,
        })
    }

    /// Send a no-argument transaction; read exception header then bool reply.
    pub fn transact_bool(&self, code: u32) -> Result<bool, CoreError> {
        let out = self.raw_noarg(code)?;
        let r = ParcelReader::owned(&self.vt, &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)
        }
    }

    /// Send a transaction with one i32 argument; discard reply.
    pub fn transact_i32(&self, code: u32, arg: i32) -> Result<(), CoreError> {
        let _ = transact_write(&self.vt, self.service.ptr, code, |w| w.write_i32(arg))?;
        Ok(())
    }

    fn raw_noarg(&self, code: u32) -> Result<OwnedParcel, CoreError> {
        transact_write(&self.vt, self.service.ptr, code, |_| Ok(()))
    }
}