From 4801094133ad556fdee1f4afbf734980b691d720 Mon Sep 17 00:00:00 2001 From: ivan provalov Date: Wed, 19 Feb 2025 07:02:28 -0800 Subject: [PATCH] JNI Exception Handling (#1452) --- sherpa-onnx/jni/common.h | 60 ++++++++++++++++++++++ sherpa-onnx/jni/offline-recognizer.cc | 16 ++++-- sherpa-onnx/jni/offline-tts.cc | 17 +++--- sherpa-onnx/jni/voice-activity-detector.cc | 32 ++++++++---- 4 files changed, 103 insertions(+), 22 deletions(-) diff --git a/sherpa-onnx/jni/common.h b/sherpa-onnx/jni/common.h index cb7dabe7..c30ace6b 100644 --- a/sherpa-onnx/jni/common.h +++ b/sherpa-onnx/jni/common.h @@ -5,6 +5,8 @@ #ifndef SHERPA_ONNX_JNI_COMMON_H_ #define SHERPA_ONNX_JNI_COMMON_H_ +#include + #if __ANDROID_API__ >= 9 #include @@ -42,4 +44,62 @@ jobject NewInteger(JNIEnv *env, int32_t value); jobject NewFloat(JNIEnv *env, float value); +// Template function for non-void return types +template +ReturnType SafeJNI(JNIEnv *env, const char *functionName, Func func, + ReturnType defaultValue) { + try { + return func(); + } catch (const std::exception &e) { + jclass exClass = env->FindClass("java/lang/RuntimeException"); + if (exClass != nullptr) { + std::string errorMessage = std::string(functionName) + ": " + e.what(); + env->ThrowNew(exClass, errorMessage.c_str()); + } + } catch (...) { + jclass exClass = env->FindClass("java/lang/RuntimeException"); + if (exClass != nullptr) { + std::string errorMessage = std::string(functionName) + + ": Native exception: caught unknown exception"; + env->ThrowNew(exClass, errorMessage.c_str()); + } + } + return defaultValue; +} + +// Specialization for void return type +template +void SafeJNI(JNIEnv *env, const char *functionName, Func func) { + try { + func(); + } catch (const std::exception &e) { + jclass exClass = env->FindClass("java/lang/RuntimeException"); + if (exClass != nullptr) { + std::string errorMessage = std::string(functionName) + ": " + e.what(); + env->ThrowNew(exClass, errorMessage.c_str()); + } + } catch (...) { + jclass exClass = env->FindClass("java/lang/RuntimeException"); + if (exClass != nullptr) { + std::string errorMessage = std::string(functionName) + + ": Native exception: caught unknown exception"; + env->ThrowNew(exClass, errorMessage.c_str()); + } + } +} + +// Helper function to validate JNI pointers +inline bool ValidatePointer(JNIEnv *env, jlong ptr, + const char *functionName, const char *message) { + if (ptr == 0) { + jclass exClass = env->FindClass("java/lang/NullPointerException"); + if (exClass != nullptr) { + std::string errorMessage = std::string(functionName) + ": " + message; + env->ThrowNew(exClass, errorMessage.c_str()); + } + return false; + } + return true; +} + #endif // SHERPA_ONNX_JNI_COMMON_H_ diff --git a/sherpa-onnx/jni/offline-recognizer.cc b/sherpa-onnx/jni/offline-recognizer.cc index e274add2..1515bdb7 100644 --- a/sherpa-onnx/jni/offline-recognizer.cc +++ b/sherpa-onnx/jni/offline-recognizer.cc @@ -353,11 +353,19 @@ Java_com_k2fsa_sherpa_onnx_OfflineRecognizer_createStream(JNIEnv * /*env*/, SHERPA_ONNX_EXTERN_C JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_OfflineRecognizer_decode( - JNIEnv * /*env*/, jobject /*obj*/, jlong ptr, jlong streamPtr) { - auto recognizer = reinterpret_cast(ptr); - auto stream = reinterpret_cast(streamPtr); + JNIEnv *env, jobject /*obj*/, jlong ptr, jlong streamPtr) { + SafeJNI(env, "OfflineRecognizer_decode", [&] { + if (!ValidatePointer(env, ptr, "OfflineRecognizer_decode", + "OfflineRecognizer pointer is null.") || + !ValidatePointer(env, streamPtr, "OfflineRecognizer_decode", + "OfflineStream pointer is null.")) { + return; + } - recognizer->DecodeStream(stream); + auto recognizer = reinterpret_cast(ptr); + auto stream = reinterpret_cast(streamPtr); + recognizer->DecodeStream(stream); + }); } SHERPA_ONNX_EXTERN_C diff --git a/sherpa-onnx/jni/offline-tts.cc b/sherpa-onnx/jni/offline-tts.cc index 14d8cc4f..57969fee 100644 --- a/sherpa-onnx/jni/offline-tts.cc +++ b/sherpa-onnx/jni/offline-tts.cc @@ -220,16 +220,17 @@ JNIEXPORT jlong JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_newFromAsset( SHERPA_ONNX_EXTERN_C JNIEXPORT jlong JNICALL Java_com_k2fsa_sherpa_onnx_OfflineTts_newFromFile( JNIEnv *env, jobject /*obj*/, jobject _config) { - auto config = sherpa_onnx::GetOfflineTtsConfig(env, _config); - SHERPA_ONNX_LOGE("config:\n%s", config.ToString().c_str()); + return SafeJNI(env, "OfflineTts_newFromFile", [&] -> jlong { + auto config = sherpa_onnx::GetOfflineTtsConfig(env, _config); + SHERPA_ONNX_LOGE("config:\n%s", config.ToString().c_str()); - if (!config.Validate()) { - SHERPA_ONNX_LOGE("Errors found in config!"); - } + if (!config.Validate()) { + SHERPA_ONNX_LOGE("Errors found in config!"); + } - auto tts = new sherpa_onnx::OfflineTts(config); - - return (jlong)tts; + auto tts = new sherpa_onnx::OfflineTts(config); + return reinterpret_cast(tts); + }, 0L); } SHERPA_ONNX_EXTERN_C diff --git a/sherpa-onnx/jni/voice-activity-detector.cc b/sherpa-onnx/jni/voice-activity-detector.cc index a30423f7..c6828bc8 100644 --- a/sherpa-onnx/jni/voice-activity-detector.cc +++ b/sherpa-onnx/jni/voice-activity-detector.cc @@ -112,14 +112,20 @@ JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_Vad_delete(JNIEnv * /*env*/, SHERPA_ONNX_EXTERN_C JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_Vad_acceptWaveform( JNIEnv *env, jobject /*obj*/, jlong ptr, jfloatArray samples) { - auto model = reinterpret_cast(ptr); + SafeJNI(env, "Vad_acceptWaveform", [&] { + if (!ValidatePointer(env, ptr, "Vad_acceptWaveform", + "VoiceActivityDetector pointer is null.")) { + return; + } - jfloat *p = env->GetFloatArrayElements(samples, nullptr); - jsize n = env->GetArrayLength(samples); + auto model = reinterpret_cast(ptr); + jfloat *p = env->GetFloatArrayElements(samples, nullptr); + jsize n = env->GetArrayLength(samples); - model->AcceptWaveform(p, n); + model->AcceptWaveform(p, n); - env->ReleaseFloatArrayElements(samples, p, JNI_ABORT); + env->ReleaseFloatArrayElements(samples, p, JNI_ABORT); + }); } SHERPA_ONNX_EXTERN_C @@ -173,11 +179,17 @@ JNIEXPORT bool JNICALL Java_com_k2fsa_sherpa_onnx_Vad_isSpeechDetected( } SHERPA_ONNX_EXTERN_C -JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_Vad_reset(JNIEnv * /*env*/, - jobject /*obj*/, - jlong ptr) { - auto model = reinterpret_cast(ptr); - model->Reset(); +JNIEXPORT void JNICALL Java_com_k2fsa_sherpa_onnx_Vad_reset( + JNIEnv *env, jobject /*obj*/, jlong ptr) { + SafeJNI(env, "Vad_reset", [&] { + if (!ValidatePointer(env, ptr, "Vad_reset", + "VoiceActivityDetector pointer is null.")) { + return; + } + + auto model = reinterpret_cast(ptr); + model->Reset(); + }); } SHERPA_ONNX_EXTERN_C