fix(android): run set_audio_mode_communication on Tauri main thread
spawn_blocking uses arbitrary thread-pool threads that don't have the Android JNI context initialized, causing ndk_context::android_context() to panic. Switch to run_on_main_thread (where the context is always valid) via a oneshot channel, with a 2s timeout. Panic is caught and forwarded as an Err so the debug log captures it rather than crashing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -96,6 +96,35 @@ pub fn set_audio_mode_communication() -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run `set_audio_mode_communication` on Tauri's main thread, where the
|
||||
/// Android context is initialized. Calling it from arbitrary Tokio blocking
|
||||
/// workers panics inside `ndk_context::android_context()`.
|
||||
pub async fn set_audio_mode_communication_on_main(
|
||||
app: tauri::AppHandle,
|
||||
) -> Result<(), String> {
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
app.run_on_main_thread(move || {
|
||||
let result = std::panic::catch_unwind(set_audio_mode_communication)
|
||||
.map_err(|panic| {
|
||||
if let Some(s) = panic.downcast_ref::<&str>() {
|
||||
format!("panic: {s}")
|
||||
} else if let Some(s) = panic.downcast_ref::<String>() {
|
||||
format!("panic: {s}")
|
||||
} else {
|
||||
"panic: unknown".to_string()
|
||||
}
|
||||
})
|
||||
.and_then(|r| r);
|
||||
let _ = tx.send(result);
|
||||
})
|
||||
.map_err(|e| format!("run_on_main_thread: {e}"))?;
|
||||
|
||||
tokio::time::timeout(std::time::Duration::from_secs(2), rx)
|
||||
.await
|
||||
.map_err(|_| "set_audio_mode_communication timed out after 2s".to_string())?
|
||||
.map_err(|_| "set_audio_mode_communication result channel closed".to_string())?
|
||||
}
|
||||
|
||||
/// Restore `AudioManager.MODE_NORMAL`. Call when a VoIP call ends.
|
||||
pub fn set_audio_mode_normal() -> Result<(), String> {
|
||||
let (vm, activity) = jvm_and_activity()?;
|
||||
|
||||
@@ -663,15 +663,13 @@ impl CallEngine {
|
||||
"connect:audio_mode_start",
|
||||
serde_json::json!({ "t_ms": call_t0.elapsed().as_millis() }),
|
||||
);
|
||||
let audio_mode_task =
|
||||
tokio::task::spawn_blocking(crate::android_audio::set_audio_mode_communication);
|
||||
match tokio::time::timeout(std::time::Duration::from_secs(2), audio_mode_task).await {
|
||||
Ok(Ok(Ok(()))) => crate::emit_call_debug(
|
||||
match crate::android_audio::set_audio_mode_communication_on_main(app.clone()).await {
|
||||
Ok(()) => crate::emit_call_debug(
|
||||
&app,
|
||||
"connect:audio_mode_done",
|
||||
serde_json::json!({ "t_ms": call_t0.elapsed().as_millis() }),
|
||||
),
|
||||
Ok(Ok(Err(e))) => {
|
||||
Err(e) => {
|
||||
tracing::warn!("set_audio_mode_communication failed: {e}");
|
||||
crate::emit_call_debug(
|
||||
&app,
|
||||
@@ -682,26 +680,6 @@ impl CallEngine {
|
||||
}),
|
||||
);
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
crate::emit_call_debug(
|
||||
&app,
|
||||
"connect:audio_mode_panic",
|
||||
serde_json::json!({
|
||||
"t_ms": call_t0.elapsed().as_millis(),
|
||||
"error": e.to_string(),
|
||||
}),
|
||||
);
|
||||
}
|
||||
Err(_) => {
|
||||
crate::emit_call_debug(
|
||||
&app,
|
||||
"connect:audio_mode_timeout",
|
||||
serde_json::json!({
|
||||
"t_ms": call_t0.elapsed().as_millis(),
|
||||
"timeout_ms": 2000,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user