diff --git a/core/src/jni/rac_hybrid_custom_filter_jni.cpp b/core/src/jni/rac_hybrid_custom_filter_jni.cpp index cdaa0b655..66bce85e4 100644 --- a/core/src/jni/rac_hybrid_custom_filter_jni.cpp +++ b/core/src/jni/rac_hybrid_custom_filter_jni.cpp @@ -101,6 +101,15 @@ rac_bool_t custom_filter_predicate(const rac_hybrid_routing_context_t* ctx, void } const jboolean keep = scope.env->CallBooleanMethod(a->predicate, a->mid_evaluate, model_id); scope.env->DeleteLocalRef(model_id); + // The predicate is app code. A throw leaves `keep` undefined and, worse, + // leaves the exception pending on this thread: the next JNI call made by + // whoever we return into aborts the process with "JNI called with pending + // exception". Clear it and keep the candidate, the same answer this + // function already gives when it cannot reach the JVM at all. + if (scope.env->ExceptionCheck() == JNI_TRUE) { + scope.env->ExceptionClear(); + return RAC_TRUE; + } return keep != JNI_FALSE ? RAC_TRUE : RAC_FALSE; } diff --git a/core/src/jni/rac_hybrid_device_state_jni.cpp b/core/src/jni/rac_hybrid_device_state_jni.cpp index 5952208d5..690373174 100644 --- a/core/src/jni/rac_hybrid_device_state_jni.cpp +++ b/core/src/jni/rac_hybrid_device_state_jni.cpp @@ -81,7 +81,16 @@ bool device_state_is_online(void* user_data) { if (scope.env == nullptr) { return true; } - return scope.env->CallBooleanMethod(a->provider, a->mid_is_online) != JNI_FALSE; + const jboolean online = scope.env->CallBooleanMethod(a->provider, a->mid_is_online); + // The provider is app code. A throw leaves the exception pending on this + // thread, and the next JNI call aborts the process with "JNI called with + // pending exception", so clear it and fall back to the same answer used + // when the JVM is unreachable. + if (scope.env->ExceptionCheck() == JNI_TRUE) { + scope.env->ExceptionClear(); + return true; + } + return online != JNI_FALSE; } int32_t device_state_battery_percent(void* user_data) { @@ -93,7 +102,12 @@ int32_t device_state_battery_percent(void* user_data) { if (scope.env == nullptr) { return 100; } - return static_cast(scope.env->CallIntMethod(a->provider, a->mid_battery_percent)); + const jint percent = scope.env->CallIntMethod(a->provider, a->mid_battery_percent); + if (scope.env->ExceptionCheck() == JNI_TRUE) { + scope.env->ExceptionClear(); + return 100; + } + return static_cast(percent); } bool device_state_is_thermal_throttled(void* user_data) { @@ -105,7 +119,12 @@ bool device_state_is_thermal_throttled(void* user_data) { if (scope.env == nullptr) { return false; } - return scope.env->CallBooleanMethod(a->provider, a->mid_is_thermal_throttled) != JNI_FALSE; + const jboolean throttled = scope.env->CallBooleanMethod(a->provider, a->mid_is_thermal_throttled); + if (scope.env->ExceptionCheck() == JNI_TRUE) { + scope.env->ExceptionClear(); + return false; + } + return throttled != JNI_FALSE; } /** Detach commons from the current adapter and free its GlobalRef. */