fix(audio): migrate Android JNI paths

This commit is contained in:
Edison Jwa
2026-06-08 21:24:16 +09:00
parent 901369b072
commit 5a1d902795
3 changed files with 259 additions and 221 deletions
+66 -48
View File
@@ -2448,6 +2448,13 @@ impl std::fmt::Display for AudioModeError {
#[cfg(target_os = "android")]
impl std::error::Error for AudioModeError {}
#[cfg(target_os = "android")]
impl From<jni::errors::Error> for AudioModeError {
fn from(value: jni::errors::Error) -> Self {
Self::JniAttachFailed(value.to_string())
}
}
/// JNI helper shared by `android_get_audio_mode` and
/// `android_set_audio_mode`: attach to the current thread and return
/// the `AudioManager` jobject. Centralised so SDD-108's two platform
@@ -2455,7 +2462,10 @@ impl std::error::Error for AudioModeError {}
#[cfg(target_os = "android")]
fn android_audio_manager_call<F, R>(op: F) -> Result<R, AudioModeError>
where
F: FnOnce(&mut jni::JNIEnv, &jni::objects::JObject) -> Result<R, AudioModeError>
F: for<'local> FnOnce(
&mut jni::Env<'local>,
&jni::objects::JObject<'local>,
) -> Result<R, AudioModeError>
+ std::panic::UnwindSafe,
{
use jni::objects::{JObject, JString, JValue};
@@ -2472,42 +2482,40 @@ where
// at a live JavaVM* set by our bridge_init JNI hook. The
// unsafe block contains only the cast required by
// `JavaVM::from_raw`.
let jvm = unsafe { jni::JavaVM::from_raw(vm_ptr as *mut _) }
.map_err(|e| AudioModeError::JniAttachFailed(format!("jvm from_raw: {e}")))?;
let mut env = jvm
.attach_current_thread()
.map_err(|e| AudioModeError::JniAttachFailed(format!("attach: {e}")))?;
let jvm = unsafe { jni::JavaVM::from_raw(vm_ptr as *mut _) };
jvm.attach_current_thread(|env| -> Result<R, AudioModeError> {
let context_obj = unsafe { JObject::from_raw(env, ctx.context() as jni::sys::jobject) };
let context_obj = unsafe { JObject::from_raw(ctx.context() as jni::sys::jobject) };
let service_name: JString =
env.new_string("audio")
let service_name: JString =
env.new_string("audio")
.map_err(|e| AudioModeError::MethodCallFailed {
method: "new_string",
detail: e.to_string(),
})?;
let service_name_obj = JObject::from(service_name);
let audio_manager = env
.call_method(
&context_obj,
jni::jni_str!("getSystemService"),
jni::jni_sig!("(Ljava/lang/String;)Ljava/lang/Object;"),
&[JValue::Object(&service_name_obj)],
)
.map_err(|e| AudioModeError::MethodCallFailed {
method: "new_string",
method: "getSystemService",
detail: e.to_string(),
})?
.l()
.map_err(|e| AudioModeError::MethodCallFailed {
method: "getSystemService",
detail: format!("obj cast: {e}"),
})?;
let audio_manager = env
.call_method(
&context_obj,
"getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;",
&[JValue::Object(&service_name.into())],
)
.map_err(|e| AudioModeError::MethodCallFailed {
method: "getSystemService",
detail: e.to_string(),
})?
.l()
.map_err(|e| AudioModeError::MethodCallFailed {
method: "getSystemService",
detail: format!("obj cast: {e}"),
})?;
if audio_manager.is_null() {
return Err(AudioModeError::Other(
"AudioManager service is null".to_string(),
));
}
op(&mut env, &audio_manager)
if audio_manager.is_null() {
return Err(AudioModeError::Other(
"AudioManager service is null".to_string(),
));
}
op(env, &audio_manager)
})
});
match result {
Ok(inner) => inner,
@@ -2526,16 +2534,21 @@ where
#[cfg(target_os = "android")]
pub fn android_get_audio_mode() -> Result<i32, AudioModeError> {
android_audio_manager_call(|env, audio_manager| {
env.call_method(audio_manager, "getMode", "()I", &[])
.map_err(|e| AudioModeError::MethodCallFailed {
method: "getMode",
detail: e.to_string(),
})?
.i()
.map_err(|e| AudioModeError::MethodCallFailed {
method: "getMode",
detail: format!("int cast: {e}"),
})
env.call_method(
audio_manager,
jni::jni_str!("getMode"),
jni::jni_sig!("()I"),
&[],
)
.map_err(|e| AudioModeError::MethodCallFailed {
method: "getMode",
detail: e.to_string(),
})?
.i()
.map_err(|e| AudioModeError::MethodCallFailed {
method: "getMode",
detail: format!("int cast: {e}"),
})
})
}
@@ -2549,11 +2562,16 @@ pub fn android_get_audio_mode() -> Result<i32, AudioModeError> {
pub fn android_set_audio_mode(mode: i32) -> Result<(), AudioModeError> {
use jni::objects::JValue;
android_audio_manager_call(move |env, audio_manager| {
env.call_method(audio_manager, "setMode", "(I)V", &[JValue::Int(mode)])
.map_err(|e| AudioModeError::MethodCallFailed {
method: "setMode",
detail: e.to_string(),
})?;
env.call_method(
audio_manager,
jni::jni_str!("setMode"),
jni::jni_sig!("(I)V"),
&[JValue::Int(mode)],
)
.map_err(|e| AudioModeError::MethodCallFailed {
method: "setMode",
detail: e.to_string(),
})?;
Ok(())
})
}