/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2026 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use miniquad::native::android::{self, ndk_sys}; use std::cell::RefCell; thread_local! { static JNI_ENV: RefCell = RefCell::new(JniEnvHolder { env: std::ptr::null_mut(), vm: std::ptr::null_mut(), }); } struct JniEnvHolder { env: *mut ndk_sys::JNIEnv, vm: *mut ndk_sys::JavaVM, } impl Drop for JniEnvHolder { fn drop(&mut self) { assert!(!self.env.is_null()); unsafe { let detach_current_thread = (**self.vm).DetachCurrentThread.unwrap(); let _ = detach_current_thread(self.vm); } } } /// Get the JNIEnv for the current thread, attaching if necessary. /// The returned pointer is cached per-thread and automatically detached /// when the thread exits. pub unsafe fn get_jni_env() -> *mut ndk_sys::JNIEnv { JNI_ENV.with(|holder| { let mut holder = holder.borrow_mut(); if !holder.env.is_null() { return holder.env; } // Call miniquad's attach_jni_env to get the env let env = android::attach_jni_env(); assert!(!env.is_null()); // Retrieve the JavaVM from the JNIEnv let get_java_vm = (**env).GetJavaVM.unwrap(); let mut vm: *mut ndk_sys::JavaVM = std::ptr::null_mut(); let res = get_java_vm(env, &mut vm); assert!(res == 0); assert!(!vm.is_null()); holder.env = env; holder.vm = vm; env }) } /// Check for pending Java exceptions and log them /// /// This function should be called after any JNI call that might throw an exception. /// It will use ExceptionDescribe to print the exception to logcat, then clear it. unsafe fn check_except(env: *mut ndk_sys::JNIEnv, context: &str) { let exception_check = (**env).ExceptionCheck.unwrap(); if exception_check(env) != 0 { // Use ExceptionDescribe to print the exception stack trace to logcat // This is safe to call even with a pending exception and handles StackOverflowError gracefully let exception_describe = (**env).ExceptionDescribe.unwrap(); exception_describe(env); let exception_clear = (**env).ExceptionClear.unwrap(); exception_clear(env); panic!("Java exception detected in {context}"); } }