use super::probe::{ProbeRead, decode_playback_dispatch};
use super::sys::*;
use crate::CoreError;
use std::os::fd::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd};
use std::os::raw::{c_char, c_void};
use std::sync::Mutex;
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
const AUDIO_SERVICE: &[u8] = b"audio\0";
const AUDIO_DESCRIPTOR: &[u8] = b"android.media.IAudioService\0";
const DISPATCHER_DESCRIPTOR: &[u8] = b"android.media.IPlaybackConfigDispatcher\0";
static AUDIO_EVENTFD: Mutex<Option<OwnedFd>> = Mutex::new(None);
static AUDIO_DISPATCH_CODE: AtomicU32 = AtomicU32::new(1);
static AUDIO_READ_I32: AtomicUsize = AtomicUsize::new(0);
static AUDIO_ACTIVE: AtomicBool = AtomicBool::new(false);
fn audio_eventfd_guard() -> std::sync::MutexGuard<'static, Option<OwnedFd>> {
AUDIO_EVENTFD.lock().unwrap_or_else(|p| p.into_inner())
}
struct AudioInParcel {
parcel: *const AParcel,
read: unsafe extern "C" fn(*const AParcel, *mut i32) -> BinderStatus,
}
impl ProbeRead for AudioInParcel {
fn read_i32(&mut self) -> Result<i32, ()> {
let mut v = 0i32;
if unsafe { (self.read)(self.parcel, &mut v) } == STATUS_OK {
Ok(v)
} else {
Err(())
}
}
fn skip_string(&mut self) -> Result<(), ()> {
Ok(())
}
fn skip_binder(&mut self) -> Result<(), ()> {
Ok(())
}
}
unsafe extern "C" fn audio_cb_on_create(_: *mut c_void) -> *mut c_void {
std::ptr::null_mut()
}
unsafe extern "C" fn audio_cb_on_destroy(_: *mut c_void) {}
unsafe extern "C" fn audio_cb_on_transact(
_: *mut AIBinder,
code: u32,
in_parcel: *const AParcel,
_: *mut AParcel,
) -> BinderStatus {
if code != AUDIO_DISPATCH_CODE.load(Ordering::Relaxed) {
return STATUS_UNKNOWN_TRANSACTION;
}
let read_addr = AUDIO_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 r = AudioInParcel {
parcel: in_parcel,
read: read_fn,
};
match decode_playback_dispatch(&mut r) {
Ok(active) => AUDIO_ACTIVE.store(active, Ordering::Relaxed),
Err(_) => {}
}
}
if let Some(fd) = audio_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 audio_client_on_create(_: *mut c_void) -> *mut c_void {
std::ptr::null_mut()
}
unsafe extern "C" fn audio_client_on_destroy(_: *mut c_void) {}
unsafe extern "C" fn audio_client_on_transact(
_: *mut AIBinder,
_: u32,
_: *const AParcel,
_: *mut AParcel,
) -> BinderStatus {
STATUS_UNKNOWN_TRANSACTION
}
pub struct PlaybackCallback {
_lib: DlHandle,
vt: Vtable,
audio: OwnedBinder,
dispatcher: OwnedBinder,
register_tx: u32,
unregister_tx: u32,
}
unsafe impl Send for PlaybackCallback {}
impl PlaybackCallback {
pub fn open() -> Result<(Self, crate::fd::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;
let vt = load_vtable(handle)?;
let register_tx = crate::android::dex::resolve_register_playback_callback_tx()
.ok_or_else(|| CoreError::binder(-1, "dex:TRANSACTION_registerPlaybackCallback"))?;
let unregister_tx = crate::android::dex::resolve_unregister_playback_callback_tx()
.ok_or_else(|| CoreError::binder(-1, "dex:TRANSACTION_unregisterPlaybackCallback"))?;
let dispatch_code = crate::android::dex::resolve_dispatch_playback_config_change_tx()
.ok_or_else(|| CoreError::binder(-1, "dex:TRANSACTION_dispatchPlaybackConfigChange"))?;
let audio_class = unsafe {
(vt.class_define)(
AUDIO_DESCRIPTOR.as_ptr() as *const c_char,
audio_client_on_create,
audio_client_on_destroy,
audio_client_on_transact,
)
};
if audio_class.is_null() {
return Err(CoreError::binder(-1, "AIBinder_Class_define:IAudioService"));
}
let raw_audio = unsafe { (vt.get_service)(AUDIO_SERVICE.as_ptr() as *const c_char) };
if raw_audio.is_null() {
return Err(CoreError::binder(-1, "AServiceManager_getService:audio"));
}
unsafe { (vt.associate_class)(raw_audio, audio_class) };
let audio = OwnedBinder {
ptr: raw_audio,
dec_strong: vt.dec_strong,
};
let dispatcher_class = unsafe {
(vt.class_define)(
DISPATCHER_DESCRIPTOR.as_ptr() as *const c_char,
audio_cb_on_create,
audio_cb_on_destroy,
audio_cb_on_transact,
)
};
if dispatcher_class.is_null() {
return Err(CoreError::binder(
-1,
"AIBinder_Class_define:PlaybackConfigDispatcher",
));
}
let raw_dispatcher = unsafe { (vt.new_binder)(dispatcher_class, std::ptr::null_mut()) };
if raw_dispatcher.is_null() {
return Err(CoreError::binder(
-1,
"AIBinder_new:PlaybackConfigDispatcher",
));
}
let dispatcher = OwnedBinder {
ptr: raw_dispatcher,
dec_strong: vt.dec_strong,
};
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 efd_owned = owned
.try_clone()
.map_err(|e| CoreError::sys(e.raw_os_error().unwrap_or(-1), "dup:audio"))
.and_then(|dup| unsafe {
crate::fd::Fd::from_owned_raw_fd(dup.into_raw_fd(), "audio.efd")
.map_err(|_| CoreError::binder(-1, "Fd::from_owned_raw_fd:audio.efd"))
})?;
AUDIO_READ_I32.store(vt.read_int32 as usize, Ordering::Relaxed);
AUDIO_DISPATCH_CODE.store(dispatch_code, Ordering::Relaxed);
AUDIO_ACTIVE.store(false, Ordering::Relaxed);
*audio_eventfd_guard() = Some(owned);
start_binder_thread_pool(&vt);
Ok((
Self {
_lib: lib,
vt,
audio,
dispatcher,
register_tx,
unregister_tx,
},
efd_owned,
))
}
pub fn register(&self) -> Result<(), CoreError> {
transact_write_checked(&self.vt, self.audio.ptr, self.register_tx, |w| {
w.write_strong_binder(self.dispatcher.ptr)
})
}
pub fn unregister(&self) -> Result<(), CoreError> {
let res = transact_oneway(&self.vt, self.audio.ptr, self.unregister_tx, |w| {
w.write_strong_binder(self.dispatcher.ptr)
});
*audio_eventfd_guard() = None;
AUDIO_ACTIVE.store(false, Ordering::Relaxed);
res
}
pub fn audio_active(&self) -> bool {
AUDIO_ACTIVE.load(Ordering::Relaxed)
}
}
impl Drop for PlaybackCallback {
fn drop(&mut self) {
let _ = self.unregister();
}
}