diff --git a/desktop/src-tauri/src/android_audio.rs b/desktop/src-tauri/src/android_audio.rs index 8f9c001..d49d7f2 100644 --- a/desktop/src-tauri/src/android_audio.rs +++ b/desktop/src-tauri/src/android_audio.rs @@ -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::() { + 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()?; diff --git a/desktop/src-tauri/src/engine.rs b/desktop/src-tauri/src/engine.rs index 793a90c..489dca6 100644 --- a/desktop/src-tauri/src/engine.rs +++ b/desktop/src-tauri/src/engine.rs @@ -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, - }), - ); - } } }