fix(android): unblock mic permission startup

This commit is contained in:
Edison Jwa
2026-05-20 14:52:34 +09:00
parent 7254f81c65
commit 210a6a0e11
5 changed files with 168 additions and 78 deletions
+61 -8
View File
@@ -1119,17 +1119,13 @@ fn call_voice_service_static(method: &str) -> bool {
return false;
}
};
let class = match env.find_class(ANDROID_VOICE_FG_SERVICE_FQCN) {
Ok(c) => c,
Err(e) => {
let _ = env.exception_clear();
warn!(target: "chanora_audio", error = %e, method, "android: find_class failed");
return false;
}
};
// SAFETY: ndk_context::context() is the application Context
// jobject; valid global ref for process lifetime.
let context_obj = unsafe { JObject::from_raw(ctx.context() as jni::sys::jobject) };
let class = match load_app_class(&mut env, &context_obj, ANDROID_VOICE_FG_SERVICE_FQCN) {
Some(c) => c,
None => return false,
};
match env.call_static_method(
&class,
method,
@@ -1147,3 +1143,60 @@ fn call_voice_service_static(method: &str) -> bool {
}
}
}
fn load_app_class<'local>(
env: &mut jni::JNIEnv<'local>,
context_obj: &jni::objects::JObject<'local>,
slash_name: &str,
) -> Option<jni::objects::JClass<'local>> {
match env.find_class(slash_name) {
Ok(c) => return Some(c),
Err(e) => {
let _ = env.exception_clear();
warn!(target: "chanora_audio", error = %e, class = slash_name, "android: find_class failed; retrying with app ClassLoader");
}
}
let loader = match env
.call_method(
context_obj,
"getClassLoader",
"()Ljava/lang/ClassLoader;",
&[],
)
.and_then(|v| v.l())
{
Ok(loader) => loader,
Err(e) => {
let _ = env.exception_clear();
warn!(target: "chanora_audio", error = %e, "android: Context.getClassLoader failed");
return None;
}
};
let dotted_name = slash_name.replace('/', ".");
let class_name = match env.new_string(&dotted_name) {
Ok(s) => s,
Err(e) => {
let _ = env.exception_clear();
warn!(target: "chanora_audio", error = %e, class = %dotted_name, "android: class-name string allocation failed");
return None;
}
};
let class_name_obj = jni::objects::JObject::from(class_name);
match env
.call_method(
&loader,
"loadClass",
"(Ljava/lang/String;)Ljava/lang/Class;",
&[jni::objects::JValue::Object(&class_name_obj)],
)
.and_then(|v| v.l())
{
Ok(class_obj) => Some(jni::objects::JClass::from(class_obj)),
Err(e) => {
let _ = env.exception_clear();
warn!(target: "chanora_audio", error = %e, class = %dotted_name, "android: ClassLoader.loadClass failed");
None
}
}
}