//! Android's JNI hand-off. //! //! cpal's AAudio backend (and anything else built on the `ndk` crates) asks //! the `ndk-context` crate for the JavaVM and an Android Context. Somebody has //! to put them there; tao 0.37 does, but the tao this Tauri resolves (0.35) //! keeps its own copy and never publishes it, so the audio thread panicked on //! its first breath with "android context was not initialized". //! //! `JNI_OnLoad` runs the moment `System.loadLibrary("app_lib")` loads us, //! before any activity exists. The Application object is reachable then //! through `ActivityThread.currentApplication()`, which is what every //! "give me a Context from native code" library relies on. Nothing else in //! the tree defines `JNI_OnLoad` (checked: wry 0.55, tao 0.35), so the export //! is ours to make. use jni::objects::JObject; use jni::sys::{jint, JavaVM as RawJavaVm, JNI_VERSION_1_6}; use jni::JavaVM; #[no_mangle] pub extern "system" fn JNI_OnLoad(vm: *mut RawJavaVm, _reserved: *mut std::ffi::c_void) -> jint { let Ok(jvm) = (unsafe { JavaVM::from_raw(vm) }) else { return JNI_VERSION_1_6; }; // The loading thread is a Java thread, already attached. let Ok(mut env) = jvm.get_env() else { return JNI_VERSION_1_6; }; let app: Option = env .call_static_method( "android/app/ActivityThread", "currentApplication", "()Landroid/app/Application;", &[], ) .and_then(|v| v.l()) .ok() .filter(|o| !o.is_null()); match app.and_then(|a| env.new_global_ref(&a).ok()) { Some(global) => { unsafe { ndk_context::initialize_android_context( vm.cast(), global.as_obj().as_raw().cast(), ); } // The context lives as long as the process; the global ref must too. std::mem::forget(global); log::info!("android: JNI context published for the audio backend"); } None => { eprintln!("android: could not reach ActivityThread.currentApplication(); audio output will be unavailable"); } } JNI_VERSION_1_6 }