From 2b4cf56a7071f352cc9ce6cf28cd220bfad970c7 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Thu, 2 Nov 2023 20:15:26 +0000 Subject: [PATCH 1/4] mobile: Replace direct JNIEnv access with JniHelper This PR replaces direct `JNIEnv` access, so that we can slowly migrate the code to use `JniHelper`. Follow-up PRs will migrate the code piecemeal to reduce the risk of breaking existing code. Signed-off-by: Fredy Wijaya --- mobile/library/common/jni/BUILD | 1 + .../common/jni/android_network_utility.cc | 78 +- .../common/jni/android_network_utility.h | 4 +- mobile/library/common/jni/jni_interface.cc | 741 ++++++++++-------- mobile/library/common/jni/jni_utility.cc | 305 +++---- mobile/library/common/jni/jni_utility.h | 114 ++- 6 files changed, 658 insertions(+), 585 deletions(-) diff --git a/mobile/library/common/jni/BUILD b/mobile/library/common/jni/BUILD index 15097aedcb3d2..c26666b3eca2f 100644 --- a/mobile/library/common/jni/BUILD +++ b/mobile/library/common/jni/BUILD @@ -35,6 +35,7 @@ cc_library( "jni_utility.h", ], deps = [ + ":jni_helper_lib", ":jni_support_lib", "//library/common/jni/import:jni_import_lib", "//library/common/jni/types:jni_env_lib", diff --git a/mobile/library/common/jni/android_network_utility.cc b/mobile/library/common/jni/android_network_utility.cc index b2157ef70c615..559852a599a7b 100644 --- a/mobile/library/common/jni/android_network_utility.cc +++ b/mobile/library/common/jni/android_network_utility.cc @@ -12,81 +12,88 @@ // Helper functions call into AndroidNetworkLibrary, but they are not platform dependent // because AndroidNetworkLibray can be called in non-Android platform with mock interfaces. -bool jvm_cert_is_issued_by_known_root(JNIEnv* env, jobject result) { +bool jvm_cert_is_issued_by_known_root(Envoy::JNI::JniHelper& jni_helper, jobject result) { jclass jcls_AndroidCertVerifyResult = Envoy::JNI::find_class("io.envoyproxy.envoymobile.utilities.AndroidCertVerifyResult"); jmethodID jmid_isIssuedByKnownRoot = - env->GetMethodID(jcls_AndroidCertVerifyResult, "isIssuedByKnownRoot", "()Z"); + jni_helper.getEnv()->GetMethodID(jcls_AndroidCertVerifyResult, "isIssuedByKnownRoot", "()Z"); Envoy::JNI::Exception::checkAndClear("jvm_cert_is_issued_by_known_root:GetMethodID"); ASSERT(jmid_isIssuedByKnownRoot); - bool is_issued_by_known_root = env->CallBooleanMethod(result, jmid_isIssuedByKnownRoot); + bool is_issued_by_known_root = + jni_helper.getEnv()->CallBooleanMethod(result, jmid_isIssuedByKnownRoot); Envoy::JNI::Exception::checkAndClear("jvm_cert_is_issued_by_known_root:CallBooleanMethod"); - env->DeleteLocalRef(jcls_AndroidCertVerifyResult); + jni_helper.getEnv()->DeleteLocalRef(jcls_AndroidCertVerifyResult); return is_issued_by_known_root; } -envoy_cert_verify_status_t jvm_cert_get_status(JNIEnv* env, jobject j_result) { +envoy_cert_verify_status_t jvm_cert_get_status(Envoy::JNI::JniHelper& jni_helper, + jobject j_result) { jclass jcls_AndroidCertVerifyResult = Envoy::JNI::find_class("io.envoyproxy.envoymobile.utilities.AndroidCertVerifyResult"); - jmethodID jmid_getStatus = env->GetMethodID(jcls_AndroidCertVerifyResult, "getStatus", "()I"); + jmethodID jmid_getStatus = + jni_helper.getEnv()->GetMethodID(jcls_AndroidCertVerifyResult, "getStatus", "()I"); Envoy::JNI::Exception::checkAndClear("jvm_cert_get_status:GetMethodID"); ASSERT(jmid_getStatus); envoy_cert_verify_status_t result = CERT_VERIFY_STATUS_FAILED; - result = static_cast(env->CallIntMethod(j_result, jmid_getStatus)); + result = static_cast( + jni_helper.getEnv()->CallIntMethod(j_result, jmid_getStatus)); Envoy::JNI::Exception::checkAndClear("jvm_cert_get_status:CallIntMethod"); - env->DeleteLocalRef(jcls_AndroidCertVerifyResult); + jni_helper.getEnv()->DeleteLocalRef(jcls_AndroidCertVerifyResult); return result; } -jobjectArray jvm_cert_get_certificate_chain_encoded(JNIEnv* env, jobject result) { +jobjectArray jvm_cert_get_certificate_chain_encoded(Envoy::JNI::JniHelper& jni_helper, + jobject result) { jclass jcls_AndroidCertVerifyResult = Envoy::JNI::find_class("io.envoyproxy.envoymobile.utilities.AndroidCertVerifyResult"); - jmethodID jmid_getCertificateChainEncoded = - env->GetMethodID(jcls_AndroidCertVerifyResult, "getCertificateChainEncoded", "()[[B"); + jmethodID jmid_getCertificateChainEncoded = jni_helper.getEnv()->GetMethodID( + jcls_AndroidCertVerifyResult, "getCertificateChainEncoded", "()[[B"); Envoy::JNI::Exception::checkAndClear("jvm_cert_get_certificate_chain_encoded:GetMethodID"); - jobjectArray certificate_chain = - static_cast(env->CallObjectMethod(result, jmid_getCertificateChainEncoded)); + jobjectArray certificate_chain = static_cast( + jni_helper.getEnv()->CallObjectMethod(result, jmid_getCertificateChainEncoded)); Envoy::JNI::Exception::checkAndClear("jvm_cert_get_certificate_chain_encoded:CallObjectMethod"); - env->DeleteLocalRef(jcls_AndroidCertVerifyResult); + jni_helper.getEnv()->DeleteLocalRef(jcls_AndroidCertVerifyResult); return certificate_chain; } -static void ExtractCertVerifyResult(JNIEnv* env, jobject result, envoy_cert_verify_status_t* status, +static void ExtractCertVerifyResult(Envoy::JNI::JniHelper& jni_helper, jobject result, + envoy_cert_verify_status_t* status, bool* is_issued_by_known_root, std::vector* verified_chain) { - *status = jvm_cert_get_status(env, result); + *status = jvm_cert_get_status(jni_helper, result); if (*status == CERT_VERIFY_STATUS_OK) { - *is_issued_by_known_root = jvm_cert_is_issued_by_known_root(env, result); - jobjectArray chain_byte_array = jvm_cert_get_certificate_chain_encoded(env, result); + *is_issued_by_known_root = jvm_cert_is_issued_by_known_root(jni_helper, result); + jobjectArray chain_byte_array = jvm_cert_get_certificate_chain_encoded(jni_helper, result); if (chain_byte_array != nullptr) { - Envoy::JNI::JavaArrayOfByteArrayToStringVector(env, chain_byte_array, verified_chain); + Envoy::JNI::JavaArrayOfByteArrayToStringVector(jni_helper, chain_byte_array, verified_chain); } } } // `auth_type` and `host` are expected to be UTF-8 encoded. -jobject call_jvm_verify_x509_cert_chain(JNIEnv* env, const std::vector& cert_chain, +jobject call_jvm_verify_x509_cert_chain(Envoy::JNI::JniHelper& jni_helper, + const std::vector& cert_chain, std::string auth_type, absl::string_view hostname) { jni_log("[Envoy]", "jvm_verify_x509_cert_chain"); jclass jcls_AndroidNetworkLibrary = Envoy::JNI::find_class("io.envoyproxy.envoymobile.utilities.AndroidNetworkLibrary"); - jmethodID jmid_verifyServerCertificates = env->GetStaticMethodID( + jmethodID jmid_verifyServerCertificates = jni_helper.getEnv()->GetStaticMethodID( jcls_AndroidNetworkLibrary, "verifyServerCertificates", "([[B[B[B)Lio/envoyproxy/envoymobile/utilities/AndroidCertVerifyResult;"); Envoy::JNI::Exception::checkAndClear("call_jvm_verify_x509_cert_chain:GetStaticMethodID"); - jobjectArray chain_byte_array = Envoy::JNI::ToJavaArrayOfByteArray(env, cert_chain); - jbyteArray auth_string = Envoy::JNI::ToJavaByteArray(env, auth_type); + jobjectArray chain_byte_array = Envoy::JNI::ToJavaArrayOfByteArray(jni_helper, cert_chain); + jbyteArray auth_string = Envoy::JNI::ToJavaByteArray(jni_helper, auth_type); jbyteArray host_string = Envoy::JNI::ToJavaByteArray( - env, reinterpret_cast(hostname.data()), hostname.length()); - jobject result = - env->CallStaticObjectMethod(jcls_AndroidNetworkLibrary, jmid_verifyServerCertificates, - chain_byte_array, auth_string, host_string); + jni_helper, reinterpret_cast(hostname.data()), hostname.length()); + jobject result = jni_helper.getEnv()->CallStaticObjectMethod( + jcls_AndroidNetworkLibrary, jmid_verifyServerCertificates, chain_byte_array, auth_string, + host_string); Envoy::JNI::Exception::checkAndClear("call_jvm_verify_x509_cert_chain:CallStaticObjectMethod"); - env->DeleteLocalRef(chain_byte_array); - env->DeleteLocalRef(auth_string); - env->DeleteLocalRef(host_string); - env->DeleteLocalRef(jcls_AndroidNetworkLibrary); + jni_helper.getEnv()->DeleteLocalRef(chain_byte_array); + jni_helper.getEnv()->DeleteLocalRef(auth_string); + jni_helper.getEnv()->DeleteLocalRef(host_string); + jni_helper.getEnv()->DeleteLocalRef(jcls_AndroidNetworkLibrary); return result; } @@ -96,18 +103,17 @@ static void jvm_verify_x509_cert_chain(const std::vector& cert_chai envoy_cert_verify_status_t* status, bool* is_issued_by_known_root, std::vector* verified_chain) { - JNIEnv* env = Envoy::JNI::get_env(); - jobject result = call_jvm_verify_x509_cert_chain(env, cert_chain, auth_type, hostname); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); + jobject result = call_jvm_verify_x509_cert_chain(jni_helper, cert_chain, auth_type, hostname); if (Envoy::JNI::Exception::checkAndClear()) { *status = CERT_VERIFY_STATUS_NOT_YET_VALID; } else { - ExtractCertVerifyResult(Envoy::JNI::get_env(), result, status, is_issued_by_known_root, - verified_chain); + ExtractCertVerifyResult(jni_helper, result, status, is_issued_by_known_root, verified_chain); if (Envoy::JNI::Exception::checkAndClear()) { *status = CERT_VERIFY_STATUS_FAILED; } } - env->DeleteLocalRef(result); + jni_helper.getEnv()->DeleteLocalRef(result); } envoy_cert_validation_result verify_x509_cert_chain(const std::vector& certs, diff --git a/mobile/library/common/jni/android_network_utility.h b/mobile/library/common/jni/android_network_utility.h index 81534b185fd19..2ac64bb2e224a 100644 --- a/mobile/library/common/jni/android_network_utility.h +++ b/mobile/library/common/jni/android_network_utility.h @@ -7,12 +7,14 @@ #include "library/common/api/c_types.h" #include "library/common/extensions/cert_validator/platform_bridge/c_types.h" #include "library/common/jni/import/jni_import.h" +#include "library/common/jni/jni_helper.h" // NOLINT(namespace-envoy) /* Calls up through JNI to validate given certificates. */ -jobject call_jvm_verify_x509_cert_chain(JNIEnv* env, const std::vector& cert_chain, +jobject call_jvm_verify_x509_cert_chain(Envoy::JNI::JniHelper& jni_helper, + const std::vector& cert_chain, std::string auth_type, absl::string_view hostname); envoy_cert_validation_result verify_x509_cert_chain(const std::vector& certs, diff --git a/mobile/library/common/jni/jni_interface.cc b/mobile/library/common/jni/jni_interface.cc index 0ef69faba19d4..e7c88a6b3a6ca 100644 --- a/mobile/library/common/jni/jni_interface.cc +++ b/mobile/library/common/jni/jni_interface.cc @@ -40,17 +40,17 @@ static void jvm_on_engine_running(void* context) { } jni_log("[Envoy]", "jvm_on_engine_running"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(context); - jclass jcls_JvmonEngineRunningContext = env->GetObjectClass(j_context); - jmethodID jmid_onEngineRunning = env->GetMethodID( + jclass jcls_JvmonEngineRunningContext = jni_helper.getEnv()->GetObjectClass(j_context); + jmethodID jmid_onEngineRunning = jni_helper.getEnv()->GetMethodID( jcls_JvmonEngineRunningContext, "invokeOnEngineRunning", "()Ljava/lang/Object;"); - Envoy::JNI::callObjectMethod(env, j_context, jmid_onEngineRunning); + Envoy::JNI::callObjectMethod(jni_helper, j_context, jmid_onEngineRunning); - env->DeleteLocalRef(jcls_JvmonEngineRunningContext); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmonEngineRunningContext); // TODO(goaway): This isn't re-used by other engine callbacks, so it's safe to delete here. // This will need to be updated for https://github.com/envoyproxy/envoy-mobile/issues/332 - env->DeleteGlobalRef(j_context); + jni_helper.getEnv()->DeleteGlobalRef(j_context); } static void jvm_on_log(envoy_data data, const void* context) { @@ -58,17 +58,18 @@ static void jvm_on_log(envoy_data data, const void* context) { return; } - JNIEnv* env = Envoy::JNI::get_env(); - jstring str = Envoy::JNI::native_data_to_string(env, data); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); + jstring str = Envoy::JNI::native_data_to_string(jni_helper, data); jobject j_context = static_cast(const_cast(context)); - jclass jcls_JvmLoggerContext = env->GetObjectClass(j_context); - jmethodID jmid_onLog = env->GetMethodID(jcls_JvmLoggerContext, "log", "(Ljava/lang/String;)V"); - Envoy::JNI::callVoidMethod(env, j_context, jmid_onLog, str); + jclass jcls_JvmLoggerContext = jni_helper.getEnv()->GetObjectClass(j_context); + jmethodID jmid_onLog = + jni_helper.getEnv()->GetMethodID(jcls_JvmLoggerContext, "log", "(Ljava/lang/String;)V"); + Envoy::JNI::callVoidMethod(jni_helper, j_context, jmid_onLog, str); release_envoy_data(data); - env->DeleteLocalRef(str); - env->DeleteLocalRef(jcls_JvmLoggerContext); + jni_helper.getEnv()->DeleteLocalRef(str); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmLoggerContext); } static void jvm_on_exit(void*) { @@ -86,17 +87,18 @@ static void jvm_on_track(envoy_map events, const void* context) { return; } - JNIEnv* env = Envoy::JNI::get_env(); - jobject events_hashmap = Envoy::JNI::native_map_to_map(env, events); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); + jobject events_hashmap = Envoy::JNI::native_map_to_map(jni_helper, events); jobject j_context = static_cast(const_cast(context)); - jclass jcls_EnvoyEventTracker = env->GetObjectClass(j_context); - jmethodID jmid_onTrack = env->GetMethodID(jcls_EnvoyEventTracker, "track", "(Ljava/util/Map;)V"); - Envoy::JNI::callVoidMethod(env, j_context, jmid_onTrack, events_hashmap); + jclass jcls_EnvoyEventTracker = jni_helper.getEnv()->GetObjectClass(j_context); + jmethodID jmid_onTrack = + jni_helper.getEnv()->GetMethodID(jcls_EnvoyEventTracker, "track", "(Ljava/util/Map;)V"); + Envoy::JNI::callVoidMethod(jni_helper, j_context, jmid_onTrack, events_hashmap); release_envoy_map(events); - env->DeleteLocalRef(events_hashmap); - env->DeleteLocalRef(jcls_EnvoyEventTracker); + jni_helper.getEnv()->DeleteLocalRef(events_hashmap); + jni_helper.getEnv()->DeleteLocalRef(jcls_EnvoyEventTracker); } extern "C" JNIEXPORT jint JNICALL @@ -167,10 +169,11 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra JNIEnv* env, jclass, // class jlong engine, jstring elements, jobjectArray tags, jint count) { - const char* native_elements = env->GetStringUTFChars(elements, nullptr); - jint result = - record_counter_inc(engine, native_elements, Envoy::JNI::to_native_tags(env, tags), count); - env->ReleaseStringUTFChars(elements, native_elements); + Envoy::JNI::JniHelper jni_helper(env); + const char* native_elements = jni_helper.getEnv()->GetStringUTFChars(elements, nullptr); + jint result = record_counter_inc(engine, native_elements, + Envoy::JNI::to_native_tags(jni_helper, tags), count); + jni_helper.getEnv()->ReleaseStringUTFChars(elements, native_elements); return result; } @@ -193,7 +196,8 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_dumpStats(JNIEnv* env, return env->NewStringUTF(""); } - jstring str = Envoy::JNI::native_data_to_string(env, data); + Envoy::JNI::JniHelper jni_helper(env); + jstring str = Envoy::JNI::native_data_to_string(jni_helper, data); release_envoy_data(data); return str; @@ -203,9 +207,10 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_dumpStats(JNIEnv* env, static void passHeaders(const char* method, const Envoy::Types::ManagedEnvoyHeaders& headers, jobject j_context) { - JNIEnv* env = Envoy::JNI::get_env(); - jclass jcls_JvmCallbackContext = env->GetObjectClass(j_context); - jmethodID jmid_passHeader = env->GetMethodID(jcls_JvmCallbackContext, method, "([B[BZ)V"); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); + jclass jcls_JvmCallbackContext = jni_helper.getEnv()->GetObjectClass(j_context); + jmethodID jmid_passHeader = + jni_helper.getEnv()->GetMethodID(jcls_JvmCallbackContext, method, "([B[BZ)V"); jboolean start_headers = JNI_TRUE; for (envoy_map_size_t i = 0; i < headers.get().length; i++) { @@ -215,21 +220,23 @@ static void passHeaders(const char* method, const Envoy::Types::ManagedEnvoyHead // requires a null-terminated *modified* UTF-8 string. // Create platform byte array for header key - jbyteArray j_key = Envoy::JNI::native_data_to_array(env, headers.get().entries[i].key); + jbyteArray j_key = Envoy::JNI::native_data_to_array(jni_helper, headers.get().entries[i].key); // Create platform byte array for header value - jbyteArray j_value = Envoy::JNI::native_data_to_array(env, headers.get().entries[i].value); + jbyteArray j_value = + Envoy::JNI::native_data_to_array(jni_helper, headers.get().entries[i].value); // Pass this header pair to the platform - Envoy::JNI::callVoidMethod(env, j_context, jmid_passHeader, j_key, j_value, start_headers); - env->DeleteLocalRef(j_key); - env->DeleteLocalRef(j_value); + Envoy::JNI::callVoidMethod(jni_helper, j_context, jmid_passHeader, j_key, j_value, + start_headers); + jni_helper.getEnv()->DeleteLocalRef(j_key); + jni_helper.getEnv()->DeleteLocalRef(j_value); // We don't release local refs currently because we've pushed a large enough frame, but we could // consider this and/or periodically popping the frame. start_headers = JNI_FALSE; } - env->DeleteLocalRef(jcls_JvmCallbackContext); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmCallbackContext); } // Platform callback implementation @@ -239,25 +246,26 @@ static void passHeaders(const char* method, const Envoy::Types::ManagedEnvoyHead static void* jvm_on_headers(const char* method, const Envoy::Types::ManagedEnvoyHeaders& headers, bool end_stream, envoy_stream_intel stream_intel, void* context) { jni_log("[Envoy]", "jvm_on_headers"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(context); passHeaders("passHeader", headers, j_context); - jclass jcls_JvmCallbackContext = env->GetObjectClass(j_context); + jclass jcls_JvmCallbackContext = jni_helper.getEnv()->GetObjectClass(j_context); jmethodID jmid_onHeaders = - env->GetMethodID(jcls_JvmCallbackContext, method, "(JZ[J)Ljava/lang/Object;"); + jni_helper.getEnv()->GetMethodID(jcls_JvmCallbackContext, method, "(JZ[J)Ljava/lang/Object;"); - jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(env, stream_intel); + jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(jni_helper, stream_intel); // Note: be careful of JVM types. Before we casted to jlong we were getting integer problems. // TODO: make this cast safer. - jobject result = env->CallObjectMethod(j_context, jmid_onHeaders, (jlong)headers.get().length, - end_stream ? JNI_TRUE : JNI_FALSE, j_stream_intel); + jobject result = + jni_helper.getEnv()->CallObjectMethod(j_context, jmid_onHeaders, (jlong)headers.get().length, + end_stream ? JNI_TRUE : JNI_FALSE, j_stream_intel); // TODO(Augustyniak): Pass the name of the filter in here so that we can instrument the origin of // the JNI exception better. bool exception_cleared = Envoy::JNI::Exception::checkAndClear(method); - env->DeleteLocalRef(j_stream_intel); - env->DeleteLocalRef(jcls_JvmCallbackContext); + jni_helper.getEnv()->DeleteLocalRef(j_stream_intel); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmCallbackContext); if (!exception_cleared) { return result; @@ -266,21 +274,22 @@ static void* jvm_on_headers(const char* method, const Envoy::Types::ManagedEnvoy // Create a "no operation" result: // 1. Tell the filter chain to continue the iteration. // 2. Return headers received on as method's input as part of the method's output. - jclass jcls_object_array = env->FindClass("java/lang/Object"); - jobjectArray noopResult = env->NewObjectArray(2, jcls_object_array, NULL); + jclass jcls_object_array = jni_helper.getEnv()->FindClass("java/lang/Object"); + jobjectArray noopResult = jni_helper.getEnv()->NewObjectArray(2, jcls_object_array, NULL); - jclass jcls_int = env->FindClass("java/lang/Integer"); - jmethodID jmid_intInit = env->GetMethodID(jcls_int, "", "(I)V"); - jobject j_status = env->NewObject(jcls_int, jmid_intInit, 0); + jclass jcls_int = jni_helper.getEnv()->FindClass("java/lang/Integer"); + jmethodID jmid_intInit = jni_helper.getEnv()->GetMethodID(jcls_int, "", "(I)V"); + jobject j_status = jni_helper.getEnv()->NewObject(jcls_int, jmid_intInit, 0); // Set status to "0" (FilterHeadersStatus::Continue). Signal that the intent // is to continue the iteration of the filter chain. - env->SetObjectArrayElement(noopResult, 0, j_status); + jni_helper.getEnv()->SetObjectArrayElement(noopResult, 0, j_status); // Since the "on headers" call threw an exception set input headers as output headers. - env->SetObjectArrayElement(noopResult, 1, Envoy::JNI::ToJavaArrayOfObjectArray(env, headers)); + jni_helper.getEnv()->SetObjectArrayElement( + noopResult, 1, Envoy::JNI::ToJavaArrayOfObjectArray(jni_helper, headers)); - env->DeleteLocalRef(jcls_object_array); - env->DeleteLocalRef(jcls_int); + jni_helper.getEnv()->DeleteLocalRef(jcls_object_array); + jni_helper.getEnv()->DeleteLocalRef(jcls_int); return noopResult; } @@ -294,26 +303,27 @@ static void* jvm_on_response_headers(envoy_headers headers, bool end_stream, static envoy_filter_headers_status jvm_http_filter_on_request_headers(envoy_headers input_headers, bool end_stream, envoy_stream_intel stream_intel, const void* context) { - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); const auto headers = Envoy::Types::ManagedEnvoyHeaders(input_headers); jobjectArray result = static_cast(jvm_on_headers( "onRequestHeaders", headers, end_stream, stream_intel, const_cast(context))); - if (result == NULL || env->GetArrayLength(result) < 2) { - env->DeleteLocalRef(result); + if (result == NULL || jni_helper.getEnv()->GetArrayLength(result) < 2) { + jni_helper.getEnv()->DeleteLocalRef(result); return (envoy_filter_headers_status){/*status*/ kEnvoyFilterHeadersStatusStopIteration, /*headers*/ {}}; } - jobject status = env->GetObjectArrayElement(result, 0); - jobjectArray j_headers = static_cast(env->GetObjectArrayElement(result, 1)); + jobject status = jni_helper.getEnv()->GetObjectArrayElement(result, 0); + jobjectArray j_headers = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 1)); - int unboxed_status = Envoy::JNI::unbox_integer(env, status); - envoy_headers native_headers = Envoy::JNI::to_native_headers(env, j_headers); + int unboxed_status = Envoy::JNI::unbox_integer(jni_helper, status); + envoy_headers native_headers = Envoy::JNI::to_native_headers(jni_helper, j_headers); - env->DeleteLocalRef(result); - env->DeleteLocalRef(status); - env->DeleteLocalRef(j_headers); + jni_helper.getEnv()->DeleteLocalRef(result); + jni_helper.getEnv()->DeleteLocalRef(status); + jni_helper.getEnv()->DeleteLocalRef(j_headers); return (envoy_filter_headers_status){/*status*/ unboxed_status, /*headers*/ native_headers}; @@ -322,26 +332,27 @@ jvm_http_filter_on_request_headers(envoy_headers input_headers, bool end_stream, static envoy_filter_headers_status jvm_http_filter_on_response_headers(envoy_headers input_headers, bool end_stream, envoy_stream_intel stream_intel, const void* context) { - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); const auto headers = Envoy::Types::ManagedEnvoyHeaders(input_headers); jobjectArray result = static_cast(jvm_on_headers( "onResponseHeaders", headers, end_stream, stream_intel, const_cast(context))); - if (result == NULL || env->GetArrayLength(result) < 2) { - env->DeleteLocalRef(result); + if (result == NULL || jni_helper.getEnv()->GetArrayLength(result) < 2) { + jni_helper.getEnv()->DeleteLocalRef(result); return (envoy_filter_headers_status){/*status*/ kEnvoyFilterHeadersStatusStopIteration, /*headers*/ {}}; } - jobject status = env->GetObjectArrayElement(result, 0); - jobjectArray j_headers = static_cast(env->GetObjectArrayElement(result, 1)); + jobject status = jni_helper.getEnv()->GetObjectArrayElement(result, 0); + jobjectArray j_headers = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 1)); - int unboxed_status = Envoy::JNI::unbox_integer(env, status); - envoy_headers native_headers = Envoy::JNI::to_native_headers(env, j_headers); + int unboxed_status = Envoy::JNI::unbox_integer(jni_helper, status); + envoy_headers native_headers = Envoy::JNI::to_native_headers(jni_helper, j_headers); - env->DeleteLocalRef(result); - env->DeleteLocalRef(status); - env->DeleteLocalRef(j_headers); + jni_helper.getEnv()->DeleteLocalRef(result); + jni_helper.getEnv()->DeleteLocalRef(status); + jni_helper.getEnv()->DeleteLocalRef(j_headers); return (envoy_filter_headers_status){/*status*/ unboxed_status, /*headers*/ native_headers}; @@ -350,21 +361,21 @@ jvm_http_filter_on_response_headers(envoy_headers input_headers, bool end_stream static void* jvm_on_data(const char* method, envoy_data data, bool end_stream, envoy_stream_intel stream_intel, void* context) { jni_log("[Envoy]", "jvm_on_data"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(context); - jclass jcls_JvmCallbackContext = env->GetObjectClass(j_context); - jmethodID jmid_onData = - env->GetMethodID(jcls_JvmCallbackContext, method, "([BZ[J)Ljava/lang/Object;"); + jclass jcls_JvmCallbackContext = jni_helper.getEnv()->GetObjectClass(j_context); + jmethodID jmid_onData = jni_helper.getEnv()->GetMethodID(jcls_JvmCallbackContext, method, + "([BZ[J)Ljava/lang/Object;"); - jbyteArray j_data = Envoy::JNI::native_data_to_array(env, data); - jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(env, stream_intel); - jobject result = Envoy::JNI::callObjectMethod(env, j_context, jmid_onData, j_data, + jbyteArray j_data = Envoy::JNI::native_data_to_array(jni_helper, data); + jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(jni_helper, stream_intel); + jobject result = Envoy::JNI::callObjectMethod(jni_helper, j_context, jmid_onData, j_data, end_stream ? JNI_TRUE : JNI_FALSE, j_stream_intel); - env->DeleteLocalRef(j_stream_intel); - env->DeleteLocalRef(j_data); - env->DeleteLocalRef(jcls_JvmCallbackContext); + jni_helper.getEnv()->DeleteLocalRef(j_stream_intel); + jni_helper.getEnv()->DeleteLocalRef(j_data); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmCallbackContext); release_envoy_data(data); return result; @@ -378,34 +389,35 @@ static void* jvm_on_response_data(envoy_data data, bool end_stream, envoy_stream static envoy_filter_data_status jvm_http_filter_on_request_data(envoy_data data, bool end_stream, envoy_stream_intel stream_intel, const void* context) { - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobjectArray result = static_cast( jvm_on_data("onRequestData", data, end_stream, stream_intel, const_cast(context))); - if (result == NULL || env->GetArrayLength(result) < 2) { - env->DeleteLocalRef(result); + if (result == NULL || jni_helper.getEnv()->GetArrayLength(result) < 2) { + jni_helper.getEnv()->DeleteLocalRef(result); return (envoy_filter_data_status){/*status*/ kEnvoyFilterHeadersStatusStopIteration, /*data*/ {}, /*pending_headers*/ {}}; } - jobject status = env->GetObjectArrayElement(result, 0); - jobject j_data = static_cast(env->GetObjectArrayElement(result, 1)); + jobject status = jni_helper.getEnv()->GetObjectArrayElement(result, 0); + jobject j_data = static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 1)); - int unboxed_status = Envoy::JNI::unbox_integer(env, status); - envoy_data native_data = Envoy::JNI::buffer_to_native_data(env, j_data); + int unboxed_status = Envoy::JNI::unbox_integer(jni_helper, status); + envoy_data native_data = Envoy::JNI::buffer_to_native_data(jni_helper, j_data); envoy_headers* pending_headers = nullptr; // Avoid out-of-bounds access to array when checking for optional pending entities. if (unboxed_status == kEnvoyFilterDataStatusResumeIteration) { - jobjectArray j_headers = static_cast(env->GetObjectArrayElement(result, 2)); - pending_headers = Envoy::JNI::to_native_headers_ptr(env, j_headers); - env->DeleteLocalRef(j_headers); + jobjectArray j_headers = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 2)); + pending_headers = Envoy::JNI::to_native_headers_ptr(jni_helper, j_headers); + jni_helper.getEnv()->DeleteLocalRef(j_headers); } - env->DeleteLocalRef(result); - env->DeleteLocalRef(status); - env->DeleteLocalRef(j_data); + jni_helper.getEnv()->DeleteLocalRef(result); + jni_helper.getEnv()->DeleteLocalRef(status); + jni_helper.getEnv()->DeleteLocalRef(j_data); return (envoy_filter_data_status){/*status*/ unboxed_status, /*data*/ native_data, @@ -415,34 +427,35 @@ static envoy_filter_data_status jvm_http_filter_on_request_data(envoy_data data, static envoy_filter_data_status jvm_http_filter_on_response_data(envoy_data data, bool end_stream, envoy_stream_intel stream_intel, const void* context) { - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobjectArray result = static_cast( jvm_on_data("onResponseData", data, end_stream, stream_intel, const_cast(context))); - if (result == NULL || env->GetArrayLength(result) < 2) { - env->DeleteLocalRef(result); + if (result == NULL || jni_helper.getEnv()->GetArrayLength(result) < 2) { + jni_helper.getEnv()->DeleteLocalRef(result); return (envoy_filter_data_status){/*status*/ kEnvoyFilterHeadersStatusStopIteration, /*data*/ {}, /*pending_headers*/ {}}; } - jobject status = env->GetObjectArrayElement(result, 0); - jobject j_data = static_cast(env->GetObjectArrayElement(result, 1)); + jobject status = jni_helper.getEnv()->GetObjectArrayElement(result, 0); + jobject j_data = static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 1)); - int unboxed_status = Envoy::JNI::unbox_integer(env, status); - envoy_data native_data = Envoy::JNI::buffer_to_native_data(env, j_data); + int unboxed_status = Envoy::JNI::unbox_integer(jni_helper, status); + envoy_data native_data = Envoy::JNI::buffer_to_native_data(jni_helper, j_data); envoy_headers* pending_headers = nullptr; // Avoid out-of-bounds access to array when checking for optional pending entities. if (unboxed_status == kEnvoyFilterDataStatusResumeIteration) { - jobjectArray j_headers = static_cast(env->GetObjectArrayElement(result, 2)); - pending_headers = Envoy::JNI::to_native_headers_ptr(env, j_headers); - env->DeleteLocalRef(j_headers); + jobjectArray j_headers = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 2)); + pending_headers = Envoy::JNI::to_native_headers_ptr(jni_helper, j_headers); + jni_helper.getEnv()->DeleteLocalRef(j_headers); } - env->DeleteLocalRef(result); - env->DeleteLocalRef(status); - env->DeleteLocalRef(j_data); + jni_helper.getEnv()->DeleteLocalRef(result); + jni_helper.getEnv()->DeleteLocalRef(status); + jni_helper.getEnv()->DeleteLocalRef(j_data); return (envoy_filter_data_status){/*status*/ unboxed_status, /*data*/ native_data, @@ -460,23 +473,23 @@ static void* jvm_on_trailers(const char* method, envoy_headers trailers, envoy_stream_intel stream_intel, void* context) { jni_log("[Envoy]", "jvm_on_trailers"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(context); passHeaders("passHeader", trailers, j_context); - jclass jcls_JvmCallbackContext = env->GetObjectClass(j_context); + jclass jcls_JvmCallbackContext = jni_helper.getEnv()->GetObjectClass(j_context); jmethodID jmid_onTrailers = - env->GetMethodID(jcls_JvmCallbackContext, method, "(J[J)Ljava/lang/Object;"); + jni_helper.getEnv()->GetMethodID(jcls_JvmCallbackContext, method, "(J[J)Ljava/lang/Object;"); - jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(env, stream_intel); + jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(jni_helper, stream_intel); // Note: be careful of JVM types. Before we casted to jlong we were getting integer problems. // TODO: make this cast safer. // TODO(Augustyniak): check for pending exceptions after returning from JNI call. - jobject result = Envoy::JNI::callObjectMethod(env, j_context, jmid_onTrailers, + jobject result = Envoy::JNI::callObjectMethod(jni_helper, j_context, jmid_onTrailers, (jlong)trailers.length, j_stream_intel); - env->DeleteLocalRef(j_stream_intel); - env->DeleteLocalRef(jcls_JvmCallbackContext); + jni_helper.getEnv()->DeleteLocalRef(j_stream_intel); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmCallbackContext); return result; } @@ -489,40 +502,42 @@ static void* jvm_on_response_trailers(envoy_headers trailers, envoy_stream_intel static envoy_filter_trailers_status jvm_http_filter_on_request_trailers(envoy_headers trailers, envoy_stream_intel stream_intel, const void* context) { - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobjectArray result = static_cast( jvm_on_trailers("onRequestTrailers", trailers, stream_intel, const_cast(context))); - if (result == NULL || env->GetArrayLength(result) < 2) { - env->DeleteLocalRef(result); + if (result == NULL || jni_helper.getEnv()->GetArrayLength(result) < 2) { + jni_helper.getEnv()->DeleteLocalRef(result); return (envoy_filter_trailers_status){/*status*/ kEnvoyFilterHeadersStatusStopIteration, /*trailers*/ {}, /*pending_headers*/ {}, /*pending_data*/ {}}; } - jobject status = env->GetObjectArrayElement(result, 0); - jobjectArray j_trailers = static_cast(env->GetObjectArrayElement(result, 1)); + jobject status = jni_helper.getEnv()->GetObjectArrayElement(result, 0); + jobjectArray j_trailers = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 1)); - int unboxed_status = Envoy::JNI::unbox_integer(env, status); - envoy_headers native_trailers = Envoy::JNI::to_native_headers(env, j_trailers); + int unboxed_status = Envoy::JNI::unbox_integer(jni_helper, status); + envoy_headers native_trailers = Envoy::JNI::to_native_headers(jni_helper, j_trailers); envoy_headers* pending_headers = nullptr; envoy_data* pending_data = nullptr; // Avoid out-of-bounds access to array when checking for optional pending entities. if (unboxed_status == kEnvoyFilterTrailersStatusResumeIteration) { - jobjectArray j_headers = static_cast(env->GetObjectArrayElement(result, 2)); - pending_headers = Envoy::JNI::to_native_headers_ptr(env, j_headers); - env->DeleteLocalRef(j_headers); - - jobject j_data = static_cast(env->GetObjectArrayElement(result, 3)); - pending_data = Envoy::JNI::buffer_to_native_data_ptr(env, j_data); - env->DeleteLocalRef(j_data); + jobjectArray j_headers = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 2)); + pending_headers = Envoy::JNI::to_native_headers_ptr(jni_helper, j_headers); + jni_helper.getEnv()->DeleteLocalRef(j_headers); + + jobject j_data = static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 3)); + pending_data = Envoy::JNI::buffer_to_native_data_ptr(jni_helper, j_data); + jni_helper.getEnv()->DeleteLocalRef(j_data); } - env->DeleteLocalRef(result); - env->DeleteLocalRef(status); - env->DeleteLocalRef(j_trailers); + jni_helper.getEnv()->DeleteLocalRef(result); + jni_helper.getEnv()->DeleteLocalRef(status); + jni_helper.getEnv()->DeleteLocalRef(j_trailers); return (envoy_filter_trailers_status){/*status*/ unboxed_status, /*trailers*/ native_trailers, @@ -533,40 +548,42 @@ jvm_http_filter_on_request_trailers(envoy_headers trailers, envoy_stream_intel s static envoy_filter_trailers_status jvm_http_filter_on_response_trailers(envoy_headers trailers, envoy_stream_intel stream_intel, const void* context) { - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobjectArray result = static_cast( jvm_on_trailers("onResponseTrailers", trailers, stream_intel, const_cast(context))); - if (result == NULL || env->GetArrayLength(result) < 2) { - env->DeleteLocalRef(result); + if (result == NULL || jni_helper.getEnv()->GetArrayLength(result) < 2) { + jni_helper.getEnv()->DeleteLocalRef(result); return (envoy_filter_trailers_status){/*status*/ kEnvoyFilterHeadersStatusStopIteration, /*trailers*/ {}, /*pending_headers*/ {}, /*pending_data*/ {}}; } - jobject status = env->GetObjectArrayElement(result, 0); - jobjectArray j_trailers = static_cast(env->GetObjectArrayElement(result, 1)); + jobject status = jni_helper.getEnv()->GetObjectArrayElement(result, 0); + jobjectArray j_trailers = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 1)); - int unboxed_status = Envoy::JNI::unbox_integer(env, status); - envoy_headers native_trailers = Envoy::JNI::to_native_headers(env, j_trailers); + int unboxed_status = Envoy::JNI::unbox_integer(jni_helper, status); + envoy_headers native_trailers = Envoy::JNI::to_native_headers(jni_helper, j_trailers); envoy_headers* pending_headers = nullptr; envoy_data* pending_data = nullptr; // Avoid out-of-bounds access to array when checking for optional pending entities. if (unboxed_status == kEnvoyFilterTrailersStatusResumeIteration) { - jobjectArray j_headers = static_cast(env->GetObjectArrayElement(result, 2)); - pending_headers = Envoy::JNI::to_native_headers_ptr(env, j_headers); - env->DeleteLocalRef(j_headers); - - jobject j_data = static_cast(env->GetObjectArrayElement(result, 3)); - pending_data = Envoy::JNI::buffer_to_native_data_ptr(env, j_data); - env->DeleteLocalRef(j_data); + jobjectArray j_headers = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 2)); + pending_headers = Envoy::JNI::to_native_headers_ptr(jni_helper, j_headers); + jni_helper.getEnv()->DeleteLocalRef(j_headers); + + jobject j_data = static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 3)); + pending_data = Envoy::JNI::buffer_to_native_data_ptr(jni_helper, j_data); + jni_helper.getEnv()->DeleteLocalRef(j_data); } - env->DeleteLocalRef(result); - env->DeleteLocalRef(status); - env->DeleteLocalRef(j_trailers); + jni_helper.getEnv()->DeleteLocalRef(result); + jni_helper.getEnv()->DeleteLocalRef(status); + jni_helper.getEnv()->DeleteLocalRef(j_trailers); return (envoy_filter_trailers_status){/*status*/ unboxed_status, /*trailers*/ native_trailers, @@ -579,20 +596,21 @@ static void jvm_http_filter_set_request_callbacks(envoy_http_filter_callbacks ca jni_log("[Envoy]", "jvm_http_filter_set_request_callbacks"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(const_cast(context)); - jclass jcls_JvmCallbackContext = env->GetObjectClass(j_context); + jclass jcls_JvmCallbackContext = jni_helper.getEnv()->GetObjectClass(j_context); envoy_http_filter_callbacks* on_heap_callbacks = static_cast(safe_malloc(sizeof(envoy_http_filter_callbacks))); *on_heap_callbacks = callbacks; jlong callback_handle = reinterpret_cast(on_heap_callbacks); - jmethodID jmid_setRequestFilterCallbacks = - env->GetMethodID(jcls_JvmCallbackContext, "setRequestFilterCallbacks", "(J)V"); - Envoy::JNI::callVoidMethod(env, j_context, jmid_setRequestFilterCallbacks, callback_handle); + jmethodID jmid_setRequestFilterCallbacks = jni_helper.getEnv()->GetMethodID( + jcls_JvmCallbackContext, "setRequestFilterCallbacks", "(J)V"); + Envoy::JNI::callVoidMethod(jni_helper, j_context, jmid_setRequestFilterCallbacks, + callback_handle); - env->DeleteLocalRef(jcls_JvmCallbackContext); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmCallbackContext); } static void jvm_http_filter_set_response_callbacks(envoy_http_filter_callbacks callbacks, @@ -600,20 +618,21 @@ static void jvm_http_filter_set_response_callbacks(envoy_http_filter_callbacks c jni_log("[Envoy]", "jvm_http_filter_set_response_callbacks"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(const_cast(context)); - jclass jcls_JvmCallbackContext = env->GetObjectClass(j_context); + jclass jcls_JvmCallbackContext = jni_helper.getEnv()->GetObjectClass(j_context); envoy_http_filter_callbacks* on_heap_callbacks = static_cast(safe_malloc(sizeof(envoy_http_filter_callbacks))); *on_heap_callbacks = callbacks; jlong callback_handle = reinterpret_cast(on_heap_callbacks); - jmethodID jmid_setResponseFilterCallbacks = - env->GetMethodID(jcls_JvmCallbackContext, "setResponseFilterCallbacks", "(J)V"); - Envoy::JNI::callVoidMethod(env, j_context, jmid_setResponseFilterCallbacks, callback_handle); + jmethodID jmid_setResponseFilterCallbacks = jni_helper.getEnv()->GetMethodID( + jcls_JvmCallbackContext, "setResponseFilterCallbacks", "(J)V"); + Envoy::JNI::callVoidMethod(jni_helper, j_context, jmid_setResponseFilterCallbacks, + callback_handle); - env->DeleteLocalRef(jcls_JvmCallbackContext); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmCallbackContext); } static envoy_filter_resume_status @@ -622,7 +641,7 @@ jvm_http_filter_on_resume(const char* method, envoy_headers* headers, envoy_data const void* context) { jni_log("[Envoy]", "jvm_on_resume"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(const_cast(context)); jlong headers_length = -1; if (headers) { @@ -631,45 +650,47 @@ jvm_http_filter_on_resume(const char* method, envoy_headers* headers, envoy_data } jbyteArray j_in_data = nullptr; if (data) { - j_in_data = Envoy::JNI::native_data_to_array(env, *data); + j_in_data = Envoy::JNI::native_data_to_array(jni_helper, *data); } jlong trailers_length = -1; if (trailers) { trailers_length = (jlong)trailers->length; passHeaders("passTrailer", *trailers, j_context); } - jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(env, stream_intel); + jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(jni_helper, stream_intel); - jclass jcls_JvmCallbackContext = env->GetObjectClass(j_context); - jmethodID jmid_onResume = - env->GetMethodID(jcls_JvmCallbackContext, method, "(J[BJZ[J)Ljava/lang/Object;"); + jclass jcls_JvmCallbackContext = jni_helper.getEnv()->GetObjectClass(j_context); + jmethodID jmid_onResume = jni_helper.getEnv()->GetMethodID(jcls_JvmCallbackContext, method, + "(J[BJZ[J)Ljava/lang/Object;"); // Note: be careful of JVM types. Before we casted to jlong we were getting integer problems. // TODO: make this cast safer. jobjectArray result = static_cast(Envoy::JNI::callObjectMethod( - env, j_context, jmid_onResume, headers_length, j_in_data, trailers_length, + jni_helper, j_context, jmid_onResume, headers_length, j_in_data, trailers_length, end_stream ? JNI_TRUE : JNI_FALSE, j_stream_intel)); - env->DeleteLocalRef(jcls_JvmCallbackContext); - env->DeleteLocalRef(j_stream_intel); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmCallbackContext); + jni_helper.getEnv()->DeleteLocalRef(j_stream_intel); if (j_in_data != nullptr) { - env->DeleteLocalRef(j_in_data); + jni_helper.getEnv()->DeleteLocalRef(j_in_data); } - jobject status = env->GetObjectArrayElement(result, 0); - jobjectArray j_headers = static_cast(env->GetObjectArrayElement(result, 1)); - jobject j_data = static_cast(env->GetObjectArrayElement(result, 2)); - jobjectArray j_trailers = static_cast(env->GetObjectArrayElement(result, 3)); + jobject status = jni_helper.getEnv()->GetObjectArrayElement(result, 0); + jobjectArray j_headers = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 1)); + jobject j_data = static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 2)); + jobjectArray j_trailers = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(result, 3)); - int unboxed_status = Envoy::JNI::unbox_integer(env, status); - envoy_headers* pending_headers = Envoy::JNI::to_native_headers_ptr(env, j_headers); - envoy_data* pending_data = Envoy::JNI::buffer_to_native_data_ptr(env, j_data); - envoy_headers* pending_trailers = Envoy::JNI::to_native_headers_ptr(env, j_trailers); + int unboxed_status = Envoy::JNI::unbox_integer(jni_helper, status); + envoy_headers* pending_headers = Envoy::JNI::to_native_headers_ptr(jni_helper, j_headers); + envoy_data* pending_data = Envoy::JNI::buffer_to_native_data_ptr(jni_helper, j_data); + envoy_headers* pending_trailers = Envoy::JNI::to_native_headers_ptr(jni_helper, j_trailers); - env->DeleteLocalRef(result); - env->DeleteLocalRef(status); - env->DeleteLocalRef(j_headers); - env->DeleteLocalRef(j_data); - env->DeleteLocalRef(j_trailers); + jni_helper.getEnv()->DeleteLocalRef(result); + jni_helper.getEnv()->DeleteLocalRef(status); + jni_helper.getEnv()->DeleteLocalRef(j_headers); + jni_helper.getEnv()->DeleteLocalRef(j_data); + jni_helper.getEnv()->DeleteLocalRef(j_trailers); return (envoy_filter_resume_status){/*status*/ unboxed_status, /*pending_headers*/ pending_headers, @@ -697,51 +718,52 @@ static void* call_jvm_on_complete(envoy_stream_intel stream_intel, envoy_final_stream_intel final_stream_intel, void* context) { jni_log("[Envoy]", "jvm_on_complete"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(context); - jclass jcls_JvmObserverContext = env->GetObjectClass(j_context); - jmethodID jmid_onComplete = - env->GetMethodID(jcls_JvmObserverContext, "onComplete", "([J[J)Ljava/lang/Object;"); + jclass jcls_JvmObserverContext = jni_helper.getEnv()->GetObjectClass(j_context); + jmethodID jmid_onComplete = jni_helper.getEnv()->GetMethodID( + jcls_JvmObserverContext, "onComplete", "([J[J)Ljava/lang/Object;"); - jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(env, stream_intel); + jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(jni_helper, stream_intel); jlongArray j_final_stream_intel = - Envoy::JNI::native_final_stream_intel_to_array(env, final_stream_intel); - jobject result = - env->CallObjectMethod(j_context, jmid_onComplete, j_stream_intel, j_final_stream_intel); + Envoy::JNI::native_final_stream_intel_to_array(jni_helper, final_stream_intel); + jobject result = jni_helper.getEnv()->CallObjectMethod(j_context, jmid_onComplete, j_stream_intel, + j_final_stream_intel); Envoy::JNI::Exception::checkAndClear(); - env->DeleteLocalRef(j_stream_intel); - env->DeleteLocalRef(j_final_stream_intel); - env->DeleteLocalRef(jcls_JvmObserverContext); + jni_helper.getEnv()->DeleteLocalRef(j_stream_intel); + jni_helper.getEnv()->DeleteLocalRef(j_final_stream_intel); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmObserverContext); return result; } static void* call_jvm_on_error(envoy_error error, envoy_stream_intel stream_intel, envoy_final_stream_intel final_stream_intel, void* context) { jni_log("[Envoy]", "jvm_on_error"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(context); - jclass jcls_JvmObserverContext = env->GetObjectClass(j_context); - jmethodID jmid_onError = - env->GetMethodID(jcls_JvmObserverContext, "onError", "(I[BI[J[J)Ljava/lang/Object;"); + jclass jcls_JvmObserverContext = jni_helper.getEnv()->GetObjectClass(j_context); + jmethodID jmid_onError = jni_helper.getEnv()->GetMethodID(jcls_JvmObserverContext, "onError", + "(I[BI[J[J)Ljava/lang/Object;"); - jbyteArray j_error_message = Envoy::JNI::native_data_to_array(env, error.message); - jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(env, stream_intel); + jbyteArray j_error_message = Envoy::JNI::native_data_to_array(jni_helper, error.message); + jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(jni_helper, stream_intel); jlongArray j_final_stream_intel = - Envoy::JNI::native_final_stream_intel_to_array(env, final_stream_intel); + Envoy::JNI::native_final_stream_intel_to_array(jni_helper, final_stream_intel); - jobject result = env->CallObjectMethod(j_context, jmid_onError, error.error_code, j_error_message, - error.attempt_count, j_stream_intel, j_final_stream_intel); + jobject result = jni_helper.getEnv()->CallObjectMethod(j_context, jmid_onError, error.error_code, + j_error_message, error.attempt_count, + j_stream_intel, j_final_stream_intel); Envoy::JNI::Exception::checkAndClear(); - env->DeleteLocalRef(j_stream_intel); - env->DeleteLocalRef(j_final_stream_intel); - env->DeleteLocalRef(j_error_message); - env->DeleteLocalRef(jcls_JvmObserverContext); + jni_helper.getEnv()->DeleteLocalRef(j_stream_intel); + jni_helper.getEnv()->DeleteLocalRef(j_final_stream_intel); + jni_helper.getEnv()->DeleteLocalRef(j_error_message); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmObserverContext); release_envoy_error(error); return result; } @@ -757,25 +779,25 @@ static void* call_jvm_on_cancel(envoy_stream_intel stream_intel, envoy_final_stream_intel final_stream_intel, void* context) { jni_log("[Envoy]", "jvm_on_cancel"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(context); - jclass jcls_JvmObserverContext = env->GetObjectClass(j_context); - jmethodID jmid_onCancel = - env->GetMethodID(jcls_JvmObserverContext, "onCancel", "([J[J)Ljava/lang/Object;"); + jclass jcls_JvmObserverContext = jni_helper.getEnv()->GetObjectClass(j_context); + jmethodID jmid_onCancel = jni_helper.getEnv()->GetMethodID(jcls_JvmObserverContext, "onCancel", + "([J[J)Ljava/lang/Object;"); - jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(env, stream_intel); + jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(jni_helper, stream_intel); jlongArray j_final_stream_intel = - Envoy::JNI::native_final_stream_intel_to_array(env, final_stream_intel); + Envoy::JNI::native_final_stream_intel_to_array(jni_helper, final_stream_intel); - jobject result = - env->CallObjectMethod(j_context, jmid_onCancel, j_stream_intel, j_final_stream_intel); + jobject result = jni_helper.getEnv()->CallObjectMethod(j_context, jmid_onCancel, j_stream_intel, + j_final_stream_intel); Envoy::JNI::Exception::checkAndClear(); - env->DeleteLocalRef(j_stream_intel); - env->DeleteLocalRef(j_final_stream_intel); - env->DeleteLocalRef(jcls_JvmObserverContext); + jni_helper.getEnv()->DeleteLocalRef(j_stream_intel); + jni_helper.getEnv()->DeleteLocalRef(j_final_stream_intel); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmObserverContext); return result; } @@ -808,73 +830,77 @@ static void jvm_http_filter_on_cancel(envoy_stream_intel stream_intel, static void* jvm_on_send_window_available(envoy_stream_intel stream_intel, void* context) { jni_log("[Envoy]", "jvm_on_send_window_available"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(context); - jclass jcls_JvmObserverContext = env->GetObjectClass(j_context); - jmethodID jmid_onSendWindowAvailable = - env->GetMethodID(jcls_JvmObserverContext, "onSendWindowAvailable", "([J)Ljava/lang/Object;"); + jclass jcls_JvmObserverContext = jni_helper.getEnv()->GetObjectClass(j_context); + jmethodID jmid_onSendWindowAvailable = jni_helper.getEnv()->GetMethodID( + jcls_JvmObserverContext, "onSendWindowAvailable", "([J)Ljava/lang/Object;"); - jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(env, stream_intel); + jlongArray j_stream_intel = Envoy::JNI::native_stream_intel_to_array(jni_helper, stream_intel); - jobject result = - Envoy::JNI::callObjectMethod(env, j_context, jmid_onSendWindowAvailable, j_stream_intel); + jobject result = Envoy::JNI::callObjectMethod(jni_helper, j_context, jmid_onSendWindowAvailable, + j_stream_intel); - env->DeleteLocalRef(j_stream_intel); - env->DeleteLocalRef(jcls_JvmObserverContext); + jni_helper.getEnv()->DeleteLocalRef(j_stream_intel); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmObserverContext); return result; } // JvmKeyValueStoreContext static envoy_data jvm_kv_store_read(envoy_data key, const void* context) { jni_log("[Envoy]", "jvm_kv_store_read"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(const_cast(context)); - jclass jcls_JvmKeyValueStoreContext = env->GetObjectClass(j_context); - jmethodID jmid_read = env->GetMethodID(jcls_JvmKeyValueStoreContext, "read", "([B)[B"); - jbyteArray j_key = Envoy::JNI::native_data_to_array(env, key); - jbyteArray j_value = (jbyteArray)Envoy::JNI::callObjectMethod(env, j_context, jmid_read, j_key); - envoy_data native_data = Envoy::JNI::array_to_native_data(env, j_value); + jclass jcls_JvmKeyValueStoreContext = jni_helper.getEnv()->GetObjectClass(j_context); + jmethodID jmid_read = + jni_helper.getEnv()->GetMethodID(jcls_JvmKeyValueStoreContext, "read", "([B)[B"); + jbyteArray j_key = Envoy::JNI::native_data_to_array(jni_helper, key); + jbyteArray j_value = + (jbyteArray)Envoy::JNI::callObjectMethod(jni_helper, j_context, jmid_read, j_key); + envoy_data native_data = Envoy::JNI::array_to_native_data(jni_helper, j_value); - env->DeleteLocalRef(j_value); - env->DeleteLocalRef(j_key); - env->DeleteLocalRef(jcls_JvmKeyValueStoreContext); + jni_helper.getEnv()->DeleteLocalRef(j_value); + jni_helper.getEnv()->DeleteLocalRef(j_key); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmKeyValueStoreContext); return native_data; } static void jvm_kv_store_remove(envoy_data key, const void* context) { jni_log("[Envoy]", "jvm_kv_store_remove"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(const_cast(context)); - jclass jcls_JvmKeyValueStoreContext = env->GetObjectClass(j_context); - jmethodID jmid_remove = env->GetMethodID(jcls_JvmKeyValueStoreContext, "remove", "([B)V"); - jbyteArray j_key = Envoy::JNI::native_data_to_array(env, key); - Envoy::JNI::callVoidMethod(env, j_context, jmid_remove, j_key); + jclass jcls_JvmKeyValueStoreContext = jni_helper.getEnv()->GetObjectClass(j_context); + jmethodID jmid_remove = + jni_helper.getEnv()->GetMethodID(jcls_JvmKeyValueStoreContext, "remove", "([B)V"); + jbyteArray j_key = Envoy::JNI::native_data_to_array(jni_helper, key); + Envoy::JNI::callVoidMethod(jni_helper, j_context, jmid_remove, j_key); - env->DeleteLocalRef(j_key); - env->DeleteLocalRef(jcls_JvmKeyValueStoreContext); + jni_helper.getEnv()->DeleteLocalRef(j_key); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmKeyValueStoreContext); } static void jvm_kv_store_save(envoy_data key, envoy_data value, const void* context) { jni_log("[Envoy]", "jvm_kv_store_save"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(const_cast(context)); - jclass jcls_JvmKeyValueStoreContext = env->GetObjectClass(j_context); - jmethodID jmid_save = env->GetMethodID(jcls_JvmKeyValueStoreContext, "save", "([B[B)V"); - jbyteArray j_key = Envoy::JNI::native_data_to_array(env, key); - jbyteArray j_value = Envoy::JNI::native_data_to_array(env, value); - Envoy::JNI::callVoidMethod(env, j_context, jmid_save, j_key, j_value); + jclass jcls_JvmKeyValueStoreContext = jni_helper.getEnv()->GetObjectClass(j_context); + jmethodID jmid_save = + jni_helper.getEnv()->GetMethodID(jcls_JvmKeyValueStoreContext, "save", "([B[B)V"); + jbyteArray j_key = Envoy::JNI::native_data_to_array(jni_helper, key); + jbyteArray j_value = Envoy::JNI::native_data_to_array(jni_helper, value); + Envoy::JNI::callVoidMethod(jni_helper, j_context, jmid_save, j_key, j_value); - env->DeleteLocalRef(j_value); - env->DeleteLocalRef(j_key); - env->DeleteLocalRef(jcls_JvmKeyValueStoreContext); + jni_helper.getEnv()->DeleteLocalRef(j_value); + jni_helper.getEnv()->DeleteLocalRef(j_key); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmKeyValueStoreContext); } // JvmFilterFactoryContext @@ -882,23 +908,24 @@ static void jvm_kv_store_save(envoy_data key, envoy_data value, const void* cont static const void* jvm_http_filter_init(const void* context) { jni_log("[Envoy]", "jvm_filter_init"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); envoy_http_filter* c_filter = static_cast(const_cast(context)); jobject j_context = static_cast(const_cast(c_filter->static_context)); jni_log_fmt("[Envoy]", "j_context: %p", j_context); - jclass jcls_JvmFilterFactoryContext = env->GetObjectClass(j_context); - jmethodID jmid_create = env->GetMethodID(jcls_JvmFilterFactoryContext, "create", - "()Lio/envoyproxy/envoymobile/engine/JvmFilterContext;"); + jclass jcls_JvmFilterFactoryContext = jni_helper.getEnv()->GetObjectClass(j_context); + jmethodID jmid_create = + jni_helper.getEnv()->GetMethodID(jcls_JvmFilterFactoryContext, "create", + "()Lio/envoyproxy/envoymobile/engine/JvmFilterContext;"); - jobject j_filter = Envoy::JNI::callObjectMethod(env, j_context, jmid_create); + jobject j_filter = Envoy::JNI::callObjectMethod(jni_helper, j_context, jmid_create); jni_log_fmt("[Envoy]", "j_filter: %p", j_filter); - jobject retained_filter = env->NewGlobalRef(j_filter); + jobject retained_filter = jni_helper.getEnv()->NewGlobalRef(j_filter); - env->DeleteLocalRef(jcls_JvmFilterFactoryContext); - env->DeleteLocalRef(j_filter); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmFilterFactoryContext); + jni_helper.getEnv()->DeleteLocalRef(j_filter); return retained_filter; } @@ -906,16 +933,17 @@ static const void* jvm_http_filter_init(const void* context) { // EnvoyStringAccessor static envoy_data jvm_get_string(const void* context) { - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jobject j_context = static_cast(const_cast(context)); - jclass jcls_JvmStringAccessorContext = env->GetObjectClass(j_context); + jclass jcls_JvmStringAccessorContext = jni_helper.getEnv()->GetObjectClass(j_context); jmethodID jmid_getString = - env->GetMethodID(jcls_JvmStringAccessorContext, "getEnvoyString", "()[B"); - jbyteArray j_data = (jbyteArray)Envoy::JNI::callObjectMethod(env, j_context, jmid_getString); - envoy_data native_data = Envoy::JNI::array_to_native_data(env, j_data); + jni_helper.getEnv()->GetMethodID(jcls_JvmStringAccessorContext, "getEnvoyString", "()[B"); + jbyteArray j_data = + (jbyteArray)Envoy::JNI::callObjectMethod(jni_helper, j_context, jmid_getString); + envoy_data native_data = Envoy::JNI::array_to_native_data(jni_helper, j_data); - env->DeleteLocalRef(jcls_JvmStringAccessorContext); - env->DeleteLocalRef(j_data); + jni_helper.getEnv()->DeleteLocalRef(jcls_JvmStringAccessorContext); + jni_helper.getEnv()->DeleteLocalRef(j_data); return native_data; } @@ -1059,12 +1087,13 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_sendData( JNIEnv* env, jclass, jlong engine_handle, jlong stream_handle, jobject data, jint length, jboolean end_stream) { + Envoy::JNI::JniHelper jni_helper(env); if (end_stream) { jni_log("[Envoy]", "jvm_send_data_end_stream"); } return send_data(static_cast(engine_handle), static_cast(stream_handle), - Envoy::JNI::buffer_to_native_data(env, data, length), end_stream); + Envoy::JNI::buffer_to_native_data(jni_helper, data, length), end_stream); } // The Java counterpart guarantees to invoke this method with a non-null jbyteArray where the @@ -1077,28 +1106,31 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_sendDataByteArray(JNIEnv* env, jlong stream_handle, jbyteArray data, jint length, jboolean end_stream) { + Envoy::JNI::JniHelper jni_helper(env); if (end_stream) { jni_log("[Envoy]", "jvm_send_data_end_stream"); } return send_data(static_cast(engine_handle), static_cast(stream_handle), - Envoy::JNI::array_to_native_data(env, data, length), end_stream); + Envoy::JNI::array_to_native_data(jni_helper, data, length), end_stream); } extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_sendHeaders( JNIEnv* env, jclass, jlong engine_handle, jlong stream_handle, jobjectArray headers, jboolean end_stream) { + Envoy::JNI::JniHelper jni_helper(env); return send_headers(static_cast(engine_handle), static_cast(stream_handle), - Envoy::JNI::to_native_headers(env, headers), end_stream); + Envoy::JNI::to_native_headers(jni_helper, headers), end_stream); } extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_sendTrailers( JNIEnv* env, jclass, jlong engine_handle, jlong stream_handle, jobjectArray trailers) { + Envoy::JNI::JniHelper jni_helper(env); jni_log("[Envoy]", "jvm_send_trailers"); return send_trailers(static_cast(engine_handle), static_cast(stream_handle), - Envoy::JNI::to_native_headers(env, trailers)); + Envoy::JNI::to_native_headers(jni_helper, trailers)); } extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_resetStream( @@ -1130,55 +1162,58 @@ Java_io_envoyproxy_envoymobile_engine_JniLibrary_registerStringAccessor(JNIEnv* // Takes a jstring from Java, converts it to a C++ string, calls the supplied // setter on it. -void setString(JNIEnv* env, jstring java_string, EngineBuilder* builder, +void setString(Envoy::JNI::JniHelper& jni_helper, jstring java_string, EngineBuilder* builder, EngineBuilder& (EngineBuilder::*setter)(std::string)) { if (!java_string) { return; } - const char* native_java_string = env->GetStringUTFChars(java_string, nullptr); + const char* native_java_string = jni_helper.getEnv()->GetStringUTFChars(java_string, nullptr); std::string java_string_str(native_java_string); if (!java_string_str.empty()) { (builder->*setter)(java_string_str); - env->ReleaseStringUTFChars(java_string, native_java_string); + jni_helper.getEnv()->ReleaseStringUTFChars(java_string, native_java_string); } } // Convert jstring to std::string -std::string getCppString(JNIEnv* env, jstring java_string) { +std::string getCppString(Envoy::JNI::JniHelper& jni_helper, jstring java_string) { if (!java_string) { return ""; } - const char* native_java_string = env->GetStringUTFChars(java_string, nullptr); + const char* native_java_string = jni_helper.getEnv()->GetStringUTFChars(java_string, nullptr); std::string cpp_string(native_java_string); - env->ReleaseStringUTFChars(java_string, native_java_string); + jni_helper.getEnv()->ReleaseStringUTFChars(java_string, native_java_string); return cpp_string; } // Converts a java byte array to a C++ string. -std::string javaByteArrayToString(JNIEnv* env, jbyteArray j_data) { - size_t data_length = static_cast(env->GetArrayLength(j_data)); - char* critical_data = static_cast(env->GetPrimitiveArrayCritical(j_data, 0)); +std::string javaByteArrayToString(Envoy::JNI::JniHelper& jni_helper, jbyteArray j_data) { + size_t data_length = static_cast(jni_helper.getEnv()->GetArrayLength(j_data)); + char* critical_data = + static_cast(jni_helper.getEnv()->GetPrimitiveArrayCritical(j_data, 0)); std::string ret(critical_data, data_length); - env->ReleasePrimitiveArrayCritical(j_data, critical_data, 0); + jni_helper.getEnv()->ReleasePrimitiveArrayCritical(j_data, critical_data, 0); return ret; } // Converts a java object array to C++ vector of of strings. -std::vector javaObjectArrayToStringVector(JNIEnv* env, jobjectArray entries) { +std::vector javaObjectArrayToStringVector(Envoy::JNI::JniHelper& jni_helper, + jobjectArray entries) { std::vector ret; // Note that headers is a flattened array of key/value pairs. // Therefore, the length of the native header array is n envoy_data or n/2 envoy_map_entry. - envoy_map_size_t length = env->GetArrayLength(entries); + envoy_map_size_t length = jni_helper.getEnv()->GetArrayLength(entries); if (length == 0) { return ret; } for (envoy_map_size_t i = 0; i < length; ++i) { // Copy native byte array for header key - jbyteArray j_str = static_cast(env->GetObjectArrayElement(entries, i)); - std::string str = javaByteArrayToString(env, j_str); - ret.push_back(javaByteArrayToString(env, j_str)); - env->DeleteLocalRef(j_str); + jbyteArray j_str = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(entries, i)); + std::string str = javaByteArrayToString(jni_helper, j_str); + ret.push_back(javaByteArrayToString(jni_helper, j_str)); + jni_helper.getEnv()->DeleteLocalRef(j_str); } return ret; @@ -1186,40 +1221,43 @@ std::vector javaObjectArrayToStringVector(JNIEnv* env, jobjectArray // Converts a java object array to C++ vector of pairs of strings. std::vector> -javaObjectArrayToStringPairVector(JNIEnv* env, jobjectArray entries) { +javaObjectArrayToStringPairVector(Envoy::JNI::JniHelper& jni_helper, jobjectArray entries) { std::vector> ret; // Note that headers is a flattened array of key/value pairs. // Therefore, the length of the native header array is n envoy_data or n/2 envoy_map_entry. - envoy_map_size_t length = env->GetArrayLength(entries); + envoy_map_size_t length = jni_helper.getEnv()->GetArrayLength(entries); if (length == 0) { return ret; } for (envoy_map_size_t i = 0; i < length; i += 2) { // Copy native byte array for header key - jbyteArray j_key = static_cast(env->GetObjectArrayElement(entries, i)); - jbyteArray j_value = static_cast(env->GetObjectArrayElement(entries, i + 1)); - std::string first = javaByteArrayToString(env, j_key); - std::string second = javaByteArrayToString(env, j_value); + jbyteArray j_key = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(entries, i)); + jbyteArray j_value = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(entries, i + 1)); + std::string first = javaByteArrayToString(jni_helper, j_key); + std::string second = javaByteArrayToString(jni_helper, j_value); ret.push_back(std::make_pair(first, second)); - env->DeleteLocalRef(j_key); - env->DeleteLocalRef(j_value); + jni_helper.getEnv()->DeleteLocalRef(j_key); + jni_helper.getEnv()->DeleteLocalRef(j_value); } return ret; } -void configureBuilder(JNIEnv* env, jstring grpc_stats_domain, jlong connect_timeout_seconds, - jlong dns_refresh_seconds, jlong dns_failure_refresh_seconds_base, - jlong dns_failure_refresh_seconds_max, jlong dns_query_timeout_seconds, - jlong dns_min_refresh_seconds, jobjectArray dns_preresolve_hostnames, - jboolean enable_dns_cache, jlong dns_cache_save_interval_seconds, - jboolean enable_drain_post_dns_refresh, jboolean enable_http3, - jstring http3_connection_options, jstring http3_client_connection_options, - jobjectArray quic_hints, jobjectArray quic_canonical_suffixes, - jboolean enable_gzip_decompression, jboolean enable_brotli_decompression, - jboolean enable_socket_tagging, jboolean enable_interface_binding, +void configureBuilder(Envoy::JNI::JniHelper& jni_helper, jstring grpc_stats_domain, + jlong connect_timeout_seconds, jlong dns_refresh_seconds, + jlong dns_failure_refresh_seconds_base, jlong dns_failure_refresh_seconds_max, + jlong dns_query_timeout_seconds, jlong dns_min_refresh_seconds, + jobjectArray dns_preresolve_hostnames, jboolean enable_dns_cache, + jlong dns_cache_save_interval_seconds, jboolean enable_drain_post_dns_refresh, + jboolean enable_http3, jstring http3_connection_options, + jstring http3_client_connection_options, jobjectArray quic_hints, + jobjectArray quic_canonical_suffixes, jboolean enable_gzip_decompression, + jboolean enable_brotli_decompression, jboolean enable_socket_tagging, + jboolean enable_interface_binding, jlong h2_connection_keepalive_idle_interval_milliseconds, jlong h2_connection_keepalive_timeout_seconds, jlong max_connections_per_host, jlong stats_flush_seconds, jlong stream_idle_timeout_seconds, @@ -1241,8 +1279,8 @@ void configureBuilder(JNIEnv* env, jstring grpc_stats_domain, jlong connect_time (h2_connection_keepalive_idle_interval_milliseconds)); builder.addH2ConnectionKeepaliveTimeoutSeconds((h2_connection_keepalive_timeout_seconds)); - setString(env, app_version, &builder, &EngineBuilder::setAppVersion); - setString(env, app_id, &builder, &EngineBuilder::setAppId); + setString(jni_helper, app_version, &builder, &EngineBuilder::setAppVersion); + setString(jni_helper, app_id, &builder, &EngineBuilder::setAppId); builder.setDeviceOs("Android"); builder.setStreamIdleTimeoutSeconds((stream_idle_timeout_seconds)); @@ -1252,13 +1290,15 @@ void configureBuilder(JNIEnv* env, jstring grpc_stats_domain, jlong connect_time builder.enableSocketTagging(enable_socket_tagging == JNI_TRUE); #ifdef ENVOY_ENABLE_QUIC builder.enableHttp3(enable_http3 == JNI_TRUE); - builder.setHttp3ConnectionOptions(getCppString(env, http3_connection_options)); - builder.setHttp3ClientConnectionOptions(getCppString(env, http3_client_connection_options)); - auto hints = javaObjectArrayToStringPairVector(env, quic_hints); + builder.setHttp3ConnectionOptions(getCppString(jni_helper, http3_connection_options)); + builder.setHttp3ClientConnectionOptions( + getCppString(jni_helper, http3_client_connection_options)); + auto hints = javaObjectArrayToStringPairVector(jni_helper, quic_hints); for (std::pair& entry : hints) { builder.addQuicHint(entry.first, stoi(entry.second)); } - std::vector suffixes = javaObjectArrayToStringVector(env, quic_canonical_suffixes); + std::vector suffixes = + javaObjectArrayToStringVector(jni_helper, quic_canonical_suffixes); for (std::string& suffix : suffixes) { builder.addQuicCanonicalSuffix(suffix); } @@ -1270,37 +1310,38 @@ void configureBuilder(JNIEnv* env, jstring grpc_stats_domain, jlong connect_time builder.enablePlatformCertificatesValidation(enable_platform_certificates_validation == JNI_TRUE); builder.setForceAlwaysUsev6(true); - auto guards = javaObjectArrayToStringPairVector(env, runtime_guards); + auto guards = javaObjectArrayToStringPairVector(jni_helper, runtime_guards); for (std::pair& entry : guards) { builder.setRuntimeGuard(entry.first, entry.second == "true"); } - auto filters = javaObjectArrayToStringPairVector(env, filter_chain); + auto filters = javaObjectArrayToStringPairVector(jni_helper, filter_chain); for (std::pair& filter : filters) { builder.addNativeFilter(filter.first, filter.second); } - std::vector sinks = javaObjectArrayToStringVector(env, stat_sinks); + std::vector sinks = javaObjectArrayToStringVector(jni_helper, stat_sinks); #ifdef ENVOY_MOBILE_STATS_REPORTING builder.addStatsSinks(std::move(sinks)); builder.addStatsFlushSeconds((stats_flush_seconds)); - setString(env, grpc_stats_domain, &builder, &EngineBuilder::addGrpcStatsDomain); + setString(jni_helper, grpc_stats_domain, &builder, &EngineBuilder::addGrpcStatsDomain); #endif - std::vector hostnames = javaObjectArrayToStringVector(env, dns_preresolve_hostnames); + std::vector hostnames = + javaObjectArrayToStringVector(jni_helper, dns_preresolve_hostnames); builder.addDnsPreresolveHostnames(hostnames); - std::string native_node_id = getCppString(env, node_id); + std::string native_node_id = getCppString(jni_helper, node_id); if (!native_node_id.empty()) { builder.setNodeId(native_node_id); } - std::string native_node_region = getCppString(env, node_region); + std::string native_node_region = getCppString(jni_helper, node_region); if (!native_node_region.empty()) { - builder.setNodeLocality(native_node_region, getCppString(env, node_zone), - getCppString(env, node_sub_zone)); + builder.setNodeLocality(native_node_region, getCppString(jni_helper, node_zone), + getCppString(jni_helper, node_sub_zone)); } Envoy::ProtobufWkt::Struct node_metadata; - Envoy::JNI::javaByteArrayToProto(env, serialized_node_metadata, &node_metadata); + Envoy::JNI::javaByteArrayToProto(jni_helper, serialized_node_metadata, &node_metadata); builder.setNodeMetadata(node_metadata); } @@ -1325,9 +1366,10 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr jstring node_id, jstring node_region, jstring node_zone, jstring node_sub_zone, jbyteArray serialized_node_metadata, jstring cds_resources_locator, jlong cds_timeout_seconds, jboolean enable_cds) { + Envoy::JNI::JniHelper jni_helper(env); Envoy::Platform::EngineBuilder builder; - configureBuilder(env, grpc_stats_domain, connect_timeout_seconds, dns_refresh_seconds, + configureBuilder(jni_helper, grpc_stats_domain, connect_timeout_seconds, dns_refresh_seconds, dns_failure_refresh_seconds_base, dns_failure_refresh_seconds_max, dns_query_timeout_seconds, dns_min_refresh_seconds, dns_preresolve_hostnames, enable_dns_cache, dns_cache_save_interval_seconds, enable_drain_post_dns_refresh, @@ -1341,35 +1383,35 @@ extern "C" JNIEXPORT jlong JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibr enable_platform_certificates_validation, runtime_guards, node_id, node_region, node_zone, node_sub_zone, serialized_node_metadata, builder); - std::string native_xds_address = getCppString(env, xds_address); + std::string native_xds_address = getCppString(jni_helper, xds_address); if (!native_xds_address.empty()) { #ifdef ENVOY_GOOGLE_GRPC Envoy::Platform::XdsBuilder xds_builder(std::move(native_xds_address), xds_port); - std::string native_xds_auth_header = getCppString(env, xds_auth_header); + std::string native_xds_auth_header = getCppString(jni_helper, xds_auth_header); if (!native_xds_auth_header.empty()) { xds_builder.setAuthenticationToken(std::move(native_xds_auth_header), - getCppString(env, xds_auth_token)); + getCppString(jni_helper, xds_auth_token)); } - std::string native_root_certs = getCppString(env, xds_root_certs); + std::string native_root_certs = getCppString(jni_helper, xds_root_certs); if (!native_root_certs.empty()) { xds_builder.setSslRootCerts(std::move(native_root_certs)); } - std::string native_sni = getCppString(env, xds_sni); + std::string native_sni = getCppString(jni_helper, xds_sni); if (!native_sni.empty()) { xds_builder.setSni(std::move(native_sni)); } - std::string native_rtds_resource_name = getCppString(env, rtds_resource_name); + std::string native_rtds_resource_name = getCppString(jni_helper, rtds_resource_name); if (!native_rtds_resource_name.empty()) { xds_builder.addRuntimeDiscoveryService(std::move(native_rtds_resource_name), rtds_timeout_seconds); } if (enable_cds == JNI_TRUE) { - xds_builder.addClusterDiscoveryService(getCppString(env, cds_resources_locator), + xds_builder.addClusterDiscoveryService(getCppString(jni_helper, cds_resources_locator), cds_timeout_seconds); } builder.setXds(std::move(xds_builder)); #else - Envoy::JNI::throwException(env, "java/lang/UnsupportedOperationException", + Envoy::JNI::throwException(jni_helper, "java/lang/UnsupportedOperationException", "This library does not support xDS. Please use " "io.envoyproxy.envoymobile:envoy-xds instead."); #endif @@ -1413,50 +1455,53 @@ extern "C" JNIEXPORT jint JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibra static void jvm_add_test_root_certificate(const uint8_t* cert, size_t len) { jni_log("[Envoy]", "jvm_add_test_root_certificate"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jclass jcls_AndroidNetworkLibrary = Envoy::JNI::find_class("io.envoyproxy.envoymobile.utilities.AndroidNetworkLibrary"); - jmethodID jmid_addTestRootCertificate = - env->GetStaticMethodID(jcls_AndroidNetworkLibrary, "addTestRootCertificate", "([B)V"); + jmethodID jmid_addTestRootCertificate = jni_helper.getEnv()->GetStaticMethodID( + jcls_AndroidNetworkLibrary, "addTestRootCertificate", "([B)V"); - jbyteArray cert_array = Envoy::JNI::ToJavaByteArray(env, cert, len); - Envoy::JNI::callStaticVoidMethod(env, jcls_AndroidNetworkLibrary, jmid_addTestRootCertificate, - cert_array); - env->DeleteLocalRef(cert_array); - env->DeleteLocalRef(jcls_AndroidNetworkLibrary); + jbyteArray cert_array = Envoy::JNI::ToJavaByteArray(jni_helper, cert, len); + Envoy::JNI::callStaticVoidMethod(jni_helper, jcls_AndroidNetworkLibrary, + jmid_addTestRootCertificate, cert_array); + jni_helper.getEnv()->DeleteLocalRef(cert_array); + jni_helper.getEnv()->DeleteLocalRef(jcls_AndroidNetworkLibrary); } static void jvm_clear_test_root_certificate() { jni_log("[Envoy]", "jvm_clear_test_root_certificate"); - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jclass jcls_AndroidNetworkLibrary = Envoy::JNI::find_class("io.envoyproxy.envoymobile.utilities.AndroidNetworkLibrary"); - jmethodID jmid_clearTestRootCertificates = - env->GetStaticMethodID(jcls_AndroidNetworkLibrary, "clearTestRootCertificates", "()V"); + jmethodID jmid_clearTestRootCertificates = jni_helper.getEnv()->GetStaticMethodID( + jcls_AndroidNetworkLibrary, "clearTestRootCertificates", "()V"); - Envoy::JNI::callStaticVoidMethod(env, jcls_AndroidNetworkLibrary, jmid_clearTestRootCertificates); - env->DeleteLocalRef(jcls_AndroidNetworkLibrary); + Envoy::JNI::callStaticVoidMethod(jni_helper, jcls_AndroidNetworkLibrary, + jmid_clearTestRootCertificates); + jni_helper.getEnv()->DeleteLocalRef(jcls_AndroidNetworkLibrary); } extern "C" JNIEXPORT jobject JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_callCertificateVerificationFromNative( JNIEnv* env, jclass, jobjectArray certChain, jbyteArray jauthType, jbyteArray jhost) { + Envoy::JNI::JniHelper jni_helper(env); std::vector cert_chain; std::string auth_type; std::string host; - Envoy::JNI::JavaArrayOfByteArrayToStringVector(env, certChain, &cert_chain); - Envoy::JNI::JavaArrayOfByteToString(env, jauthType, &auth_type); - Envoy::JNI::JavaArrayOfByteToString(env, jhost, &host); + Envoy::JNI::JavaArrayOfByteArrayToStringVector(jni_helper, certChain, &cert_chain); + Envoy::JNI::JavaArrayOfByteToString(jni_helper, jauthType, &auth_type); + Envoy::JNI::JavaArrayOfByteToString(jni_helper, jhost, &host); - return call_jvm_verify_x509_cert_chain(env, cert_chain, auth_type, host); + return call_jvm_verify_x509_cert_chain(jni_helper, cert_chain, auth_type, host); } extern "C" JNIEXPORT void JNICALL Java_io_envoyproxy_envoymobile_engine_JniLibrary_callAddTestRootCertificateFromNative( JNIEnv* env, jclass, jbyteArray jcert) { + Envoy::JNI::JniHelper jni_helper(env); std::vector cert; - Envoy::JNI::JavaArrayOfByteToBytesVector(env, jcert, &cert); + Envoy::JNI::JavaArrayOfByteToBytesVector(jni_helper, jcert, &cert); jvm_add_test_root_certificate(cert.data(), cert.size()); } diff --git a/mobile/library/common/jni/jni_utility.cc b/mobile/library/common/jni/jni_utility.cc index c021869970075..4e319644e3f8a 100644 --- a/mobile/library/common/jni/jni_utility.cc +++ b/mobile/library/common/jni/jni_utility.cc @@ -50,48 +50,49 @@ void jni_delete_const_global_ref(const void* context) { jni_delete_global_ref(const_cast(context)); } -int unbox_integer(JNIEnv* env, jobject boxedInteger) { - jclass jcls_Integer = env->FindClass("java/lang/Integer"); - jmethodID jmid_intValue = env->GetMethodID(jcls_Integer, "intValue", "()I"); - env->DeleteLocalRef(jcls_Integer); - return callIntMethod(env, boxedInteger, jmid_intValue); +int unbox_integer(JniHelper& jni_helper, jobject boxedInteger) { + jclass jcls_Integer = jni_helper.getEnv()->FindClass("java/lang/Integer"); + jmethodID jmid_intValue = jni_helper.getEnv()->GetMethodID(jcls_Integer, "intValue", "()I"); + jni_helper.getEnv()->DeleteLocalRef(jcls_Integer); + return callIntMethod(jni_helper, boxedInteger, jmid_intValue); } -envoy_data array_to_native_data(JNIEnv* env, jbyteArray j_data) { - size_t data_length = static_cast(env->GetArrayLength(j_data)); - return array_to_native_data(env, j_data, data_length); +envoy_data array_to_native_data(JniHelper& jni_helper, jbyteArray j_data) { + size_t data_length = static_cast(jni_helper.getEnv()->GetArrayLength(j_data)); + return array_to_native_data(jni_helper, j_data, data_length); } -envoy_data array_to_native_data(JNIEnv* env, jbyteArray j_data, size_t data_length) { +envoy_data array_to_native_data(JniHelper& jni_helper, jbyteArray j_data, size_t data_length) { uint8_t* native_bytes = static_cast(safe_malloc(data_length)); - void* critical_data = env->GetPrimitiveArrayCritical(j_data, 0); + void* critical_data = jni_helper.getEnv()->GetPrimitiveArrayCritical(j_data, 0); memcpy(native_bytes, critical_data, data_length); // NOLINT(safe-memcpy) - env->ReleasePrimitiveArrayCritical(j_data, critical_data, 0); + jni_helper.getEnv()->ReleasePrimitiveArrayCritical(j_data, critical_data, 0); return {data_length, native_bytes, free, native_bytes}; } -jstring native_data_to_string(JNIEnv* env, envoy_data data) { +jstring native_data_to_string(JniHelper& jni_helper, envoy_data data) { // Ensure we get a null-terminated string, the data coming in via envoy_data might not be. std::string str(reinterpret_cast(data.bytes), data.length); - jstring jstrBuf = env->NewStringUTF(str.c_str()); + jstring jstrBuf = jni_helper.getEnv()->NewStringUTF(str.c_str()); return jstrBuf; } -jbyteArray native_data_to_array(JNIEnv* env, envoy_data data) { - jbyteArray j_data = env->NewByteArray(data.length); - void* critical_data = env->GetPrimitiveArrayCritical(j_data, nullptr); +jbyteArray native_data_to_array(JniHelper& jni_helper, envoy_data data) { + jbyteArray j_data = jni_helper.getEnv()->NewByteArray(data.length); + void* critical_data = jni_helper.getEnv()->GetPrimitiveArrayCritical(j_data, nullptr); RELEASE_ASSERT(critical_data != nullptr, "unable to allocate memory in jni_utility"); memcpy(critical_data, data.bytes, data.length); // NOLINT(safe-memcpy) // Here '0' (for which there is no named constant) indicates we want to commit the changes back // to the JVM and free the c array, where applicable. // TODO: potential perf improvement. Check if copied via isCopy, and optimize memory handling. - env->ReleasePrimitiveArrayCritical(j_data, critical_data, 0); + jni_helper.getEnv()->ReleasePrimitiveArrayCritical(j_data, critical_data, 0); return j_data; } -jlongArray native_stream_intel_to_array(JNIEnv* env, envoy_stream_intel stream_intel) { - jlongArray j_array = env->NewLongArray(4); - jlong* critical_array = static_cast(env->GetPrimitiveArrayCritical(j_array, nullptr)); +jlongArray native_stream_intel_to_array(JniHelper& jni_helper, envoy_stream_intel stream_intel) { + jlongArray j_array = jni_helper.getEnv()->NewLongArray(4); + jlong* critical_array = + static_cast(jni_helper.getEnv()->GetPrimitiveArrayCritical(j_array, nullptr)); RELEASE_ASSERT(critical_array != nullptr, "unable to allocate memory in jni_utility"); critical_array[0] = static_cast(stream_intel.stream_id); critical_array[1] = static_cast(stream_intel.connection_id); @@ -99,14 +100,15 @@ jlongArray native_stream_intel_to_array(JNIEnv* env, envoy_stream_intel stream_i critical_array[3] = static_cast(stream_intel.consumed_bytes_from_response); // Here '0' (for which there is no named constant) indicates we want to commit the changes back // to the JVM and free the c array, where applicable. - env->ReleasePrimitiveArrayCritical(j_array, critical_array, 0); + jni_helper.getEnv()->ReleasePrimitiveArrayCritical(j_array, critical_array, 0); return j_array; } -jlongArray native_final_stream_intel_to_array(JNIEnv* env, +jlongArray native_final_stream_intel_to_array(JniHelper& jni_helper, envoy_final_stream_intel final_stream_intel) { - jlongArray j_array = env->NewLongArray(16); - jlong* critical_array = static_cast(env->GetPrimitiveArrayCritical(j_array, nullptr)); + jlongArray j_array = jni_helper.getEnv()->NewLongArray(16); + jlong* critical_array = + static_cast(jni_helper.getEnv()->GetPrimitiveArrayCritical(j_array, nullptr)); RELEASE_ASSERT(critical_array != nullptr, "unable to allocate memory in jni_utility"); critical_array[0] = static_cast(final_stream_intel.stream_start_ms); @@ -128,63 +130,64 @@ jlongArray native_final_stream_intel_to_array(JNIEnv* env, // Here '0' (for which there is no named constant) indicates we want to commit the changes back // to the JVM and free the c array, where applicable. - env->ReleasePrimitiveArrayCritical(j_array, critical_array, 0); + jni_helper.getEnv()->ReleasePrimitiveArrayCritical(j_array, critical_array, 0); return j_array; } -jobject native_map_to_map(JNIEnv* env, envoy_map map) { - jclass jcls_hashMap = env->FindClass("java/util/HashMap"); - jmethodID jmid_hashMapInit = env->GetMethodID(jcls_hashMap, "", "(I)V"); - jobject j_hashMap = env->NewObject(jcls_hashMap, jmid_hashMapInit, map.length); - jmethodID jmid_hashMapPut = env->GetMethodID( +jobject native_map_to_map(JniHelper& jni_helper, envoy_map map) { + jclass jcls_hashMap = jni_helper.getEnv()->FindClass("java/util/HashMap"); + jmethodID jmid_hashMapInit = jni_helper.getEnv()->GetMethodID(jcls_hashMap, "", "(I)V"); + jobject j_hashMap = jni_helper.getEnv()->NewObject(jcls_hashMap, jmid_hashMapInit, map.length); + jmethodID jmid_hashMapPut = jni_helper.getEnv()->GetMethodID( jcls_hashMap, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); for (envoy_map_size_t i = 0; i < map.length; i++) { - auto key = native_data_to_string(env, map.entries[i].key); - auto value = native_data_to_string(env, map.entries[i].value); - callObjectMethod(env, j_hashMap, jmid_hashMapPut, key, value); - env->DeleteLocalRef(key); - env->DeleteLocalRef(value); + auto key = native_data_to_string(jni_helper, map.entries[i].key); + auto value = native_data_to_string(jni_helper, map.entries[i].value); + callObjectMethod(jni_helper, j_hashMap, jmid_hashMapPut, key, value); + jni_helper.getEnv()->DeleteLocalRef(key); + jni_helper.getEnv()->DeleteLocalRef(value); } - env->DeleteLocalRef(jcls_hashMap); + jni_helper.getEnv()->DeleteLocalRef(jcls_hashMap); return j_hashMap; } -envoy_data buffer_to_native_data(JNIEnv* env, jobject j_data) { +envoy_data buffer_to_native_data(JniHelper& jni_helper, jobject j_data) { // Returns -1 if the buffer is not a direct buffer. - jlong data_length = env->GetDirectBufferCapacity(j_data); + jlong data_length = jni_helper.getEnv()->GetDirectBufferCapacity(j_data); if (data_length < 0) { - jclass jcls_ByteBuffer = env->FindClass("java/nio/ByteBuffer"); + jclass jcls_ByteBuffer = jni_helper.getEnv()->FindClass("java/nio/ByteBuffer"); // We skip checking hasArray() because only direct ByteBuffers or array-backed ByteBuffers // are supported. We will crash here if this is an invalid buffer, but guards may be // implemented in the JVM layer. - jmethodID jmid_array = env->GetMethodID(jcls_ByteBuffer, "array", "()[B"); - jbyteArray array = static_cast(callObjectMethod(env, j_data, jmid_array)); - env->DeleteLocalRef(jcls_ByteBuffer); + jmethodID jmid_array = jni_helper.getEnv()->GetMethodID(jcls_ByteBuffer, "array", "()[B"); + jbyteArray array = static_cast(callObjectMethod(jni_helper, j_data, jmid_array)); + jni_helper.getEnv()->DeleteLocalRef(jcls_ByteBuffer); - envoy_data native_data = array_to_native_data(env, array); - env->DeleteLocalRef(array); + envoy_data native_data = array_to_native_data(jni_helper, array); + jni_helper.getEnv()->DeleteLocalRef(array); return native_data; } - return buffer_to_native_data(env, j_data, static_cast(data_length)); + return buffer_to_native_data(jni_helper, j_data, static_cast(data_length)); } -envoy_data buffer_to_native_data(JNIEnv* env, jobject j_data, size_t data_length) { +envoy_data buffer_to_native_data(JniHelper& jni_helper, jobject j_data, size_t data_length) { // Returns nullptr if the buffer is not a direct buffer. - uint8_t* direct_address = static_cast(env->GetDirectBufferAddress(j_data)); + uint8_t* direct_address = + static_cast(jni_helper.getEnv()->GetDirectBufferAddress(j_data)); if (direct_address == nullptr) { - jclass jcls_ByteBuffer = env->FindClass("java/nio/ByteBuffer"); + jclass jcls_ByteBuffer = jni_helper.getEnv()->FindClass("java/nio/ByteBuffer"); // We skip checking hasArray() because only direct ByteBuffers or array-backed ByteBuffers // are supported. We will crash here if this is an invalid buffer, but guards may be // implemented in the JVM layer. - jmethodID jmid_array = env->GetMethodID(jcls_ByteBuffer, "array", "()[B"); - jbyteArray array = static_cast(callObjectMethod(env, j_data, jmid_array)); - env->DeleteLocalRef(jcls_ByteBuffer); + jmethodID jmid_array = jni_helper.getEnv()->GetMethodID(jcls_ByteBuffer, "array", "()[B"); + jbyteArray array = static_cast(callObjectMethod(jni_helper, j_data, jmid_array)); + jni_helper.getEnv()->DeleteLocalRef(jcls_ByteBuffer); - envoy_data native_data = array_to_native_data(env, array, data_length); - env->DeleteLocalRef(array); + envoy_data native_data = array_to_native_data(jni_helper, array, data_length); + jni_helper.getEnv()->DeleteLocalRef(array); return native_data; } @@ -192,12 +195,12 @@ envoy_data buffer_to_native_data(JNIEnv* env, jobject j_data, size_t data_length native_data.bytes = direct_address; native_data.length = data_length; native_data.release = jni_delete_global_ref; - native_data.context = env->NewGlobalRef(j_data); + native_data.context = jni_helper.getEnv()->NewGlobalRef(j_data); return native_data; } -envoy_data* buffer_to_native_data_ptr(JNIEnv* env, jobject j_data) { +envoy_data* buffer_to_native_data_ptr(JniHelper& jni_helper, jobject j_data) { // Note: This check works for LocalRefs and GlobalRefs, but will not work for WeakGlobalRefs. // Such usage would generally be inappropriate anyways; like C++ weak_ptrs, one should // acquire a new strong reference before attempting to interact with an object held by @@ -208,15 +211,15 @@ envoy_data* buffer_to_native_data_ptr(JNIEnv* env, jobject j_data) { } envoy_data* native_data = static_cast(safe_malloc(sizeof(envoy_map_entry))); - *native_data = buffer_to_native_data(env, j_data); + *native_data = buffer_to_native_data(jni_helper, j_data); return native_data; } -envoy_headers to_native_headers(JNIEnv* env, jobjectArray headers) { - return to_native_map(env, headers); +envoy_headers to_native_headers(JniHelper& jni_helper, jobjectArray headers) { + return to_native_map(jni_helper, headers); } -envoy_headers* to_native_headers_ptr(JNIEnv* env, jobjectArray headers) { +envoy_headers* to_native_headers_ptr(JniHelper&& jni_helper, jobjectArray headers) { // Note: This check works for LocalRefs and GlobalRefs, but will not work for WeakGlobalRefs. // Such usage would generally be inappropriate anyways; like C++ weak_ptrs, one should // acquire a new strong reference before attempting to interact with an object held by @@ -227,16 +230,18 @@ envoy_headers* to_native_headers_ptr(JNIEnv* env, jobjectArray headers) { } envoy_headers* native_headers = static_cast(safe_malloc(sizeof(envoy_map_entry))); - *native_headers = to_native_headers(env, headers); + *native_headers = to_native_headers(jni_helper, headers); return native_headers; } -envoy_stats_tags to_native_tags(JNIEnv* env, jobjectArray tags) { return to_native_map(env, tags); } +envoy_stats_tags to_native_tags(JniHelper& jni_helper, jobjectArray tags) { + return to_native_map(jni_helper, tags); +} -envoy_map to_native_map(JNIEnv* env, jobjectArray entries) { +envoy_map to_native_map(JniHelper& jni_helper, jobjectArray entries) { // Note that headers is a flattened array of key/value pairs. // Therefore, the length of the native header array is n envoy_data or n/2 envoy_map_entry. - envoy_map_size_t length = env->GetArrayLength(entries); + envoy_map_size_t length = jni_helper.getEnv()->GetArrayLength(entries); if (length == 0) { return {0, nullptr}; } @@ -246,99 +251,105 @@ envoy_map to_native_map(JNIEnv* env, jobjectArray entries) { for (envoy_map_size_t i = 0; i < length; i += 2) { // Copy native byte array for header key - jbyteArray j_key = static_cast(env->GetObjectArrayElement(entries, i)); - envoy_data entry_key = array_to_native_data(env, j_key); + jbyteArray j_key = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(entries, i)); + envoy_data entry_key = array_to_native_data(jni_helper, j_key); // Copy native byte array for header value - jbyteArray j_value = static_cast(env->GetObjectArrayElement(entries, i + 1)); - envoy_data entry_value = array_to_native_data(env, j_value); + jbyteArray j_value = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(entries, i + 1)); + envoy_data entry_value = array_to_native_data(jni_helper, j_value); entry_array[i / 2] = {entry_key, entry_value}; - env->DeleteLocalRef(j_key); - env->DeleteLocalRef(j_value); + jni_helper.getEnv()->DeleteLocalRef(j_key); + jni_helper.getEnv()->DeleteLocalRef(j_value); } envoy_map native_map = {length / 2, entry_array}; return native_map; } -jobjectArray ToJavaArrayOfObjectArray(JNIEnv* env, const Envoy::Types::ManagedEnvoyHeaders& map) { - jclass jcls_byte_array = env->FindClass("java/lang/Object"); - jobjectArray javaArray = env->NewObjectArray(2 * map.get().length, jcls_byte_array, nullptr); +jobjectArray ToJavaArrayOfObjectArray(JniHelper& jni_helper, + const Envoy::Types::ManagedEnvoyHeaders& map) { + jclass jcls_byte_array = jni_helper.getEnv()->FindClass("java/lang/Object"); + jobjectArray javaArray = + jni_helper.getEnv()->NewObjectArray(2 * map.get().length, jcls_byte_array, nullptr); for (envoy_map_size_t i = 0; i < map.get().length; i++) { - jbyteArray key = native_data_to_array(env, map.get().entries[i].key); - jbyteArray value = native_data_to_array(env, map.get().entries[i].value); + jbyteArray key = native_data_to_array(jni_helper, map.get().entries[i].key); + jbyteArray value = native_data_to_array(jni_helper, map.get().entries[i].value); - env->SetObjectArrayElement(javaArray, 2 * i, key); - env->SetObjectArrayElement(javaArray, 2 * i + 1, value); + jni_helper.getEnv()->SetObjectArrayElement(javaArray, 2 * i, key); + jni_helper.getEnv()->SetObjectArrayElement(javaArray, 2 * i + 1, value); } return javaArray; } -jobjectArray ToJavaArrayOfByteArray(JNIEnv* env, const std::vector& v) { - jclass jcls_byte_array = env->FindClass("[B"); - jobjectArray joa = env->NewObjectArray(v.size(), jcls_byte_array, nullptr); +jobjectArray ToJavaArrayOfByteArray(JniHelper& jni_helper, const std::vector& v) { + jclass jcls_byte_array = jni_helper.getEnv()->FindClass("[B"); + jobjectArray joa = jni_helper.getEnv()->NewObjectArray(v.size(), jcls_byte_array, nullptr); for (size_t i = 0; i < v.size(); ++i) { jbyteArray byte_array = - ToJavaByteArray(env, reinterpret_cast(v[i].data()), v[i].length()); - env->SetObjectArrayElement(joa, i, byte_array); + ToJavaByteArray(jni_helper, reinterpret_cast(v[i].data()), v[i].length()); + jni_helper.getEnv()->SetObjectArrayElement(joa, i, byte_array); } return joa; } -jbyteArray ToJavaByteArray(JNIEnv* env, const uint8_t* bytes, size_t len) { - jbyteArray byte_array = env->NewByteArray(len); +jbyteArray ToJavaByteArray(JniHelper& jni_helper, const uint8_t* bytes, size_t len) { + jbyteArray byte_array = jni_helper.getEnv()->NewByteArray(len); const jbyte* jbytes = reinterpret_cast(bytes); - env->SetByteArrayRegion(byte_array, /*start=*/0, len, jbytes); + jni_helper.getEnv()->SetByteArrayRegion(byte_array, /*start=*/0, len, jbytes); return byte_array; } -jbyteArray ToJavaByteArray(JNIEnv* env, const std::string& str) { +jbyteArray ToJavaByteArray(JniHelper& jni_helper, const std::string& str) { const uint8_t* str_bytes = reinterpret_cast(str.data()); - return ToJavaByteArray(env, str_bytes, str.size()); + return ToJavaByteArray(jni_helper, str_bytes, str.size()); } -void JavaArrayOfByteArrayToStringVector(JNIEnv* env, jobjectArray array, +void JavaArrayOfByteArrayToStringVector(JniHelper& jni_helper, jobjectArray array, std::vector* out) { ASSERT(out); ASSERT(array); - size_t len = env->GetArrayLength(array); + size_t len = jni_helper.getEnv()->GetArrayLength(array); out->resize(len); for (size_t i = 0; i < len; ++i) { - jbyteArray bytes_array = static_cast(env->GetObjectArrayElement(array, i)); - jsize bytes_len = env->GetArrayLength(bytes_array); + jbyteArray bytes_array = + static_cast(jni_helper.getEnv()->GetObjectArrayElement(array, i)); + jsize bytes_len = jni_helper.getEnv()->GetArrayLength(bytes_array); // It doesn't matter if the array returned by GetByteArrayElements is a copy // or not, as the data will be simply be copied into C++ owned memory below. - jbyte* bytes = env->GetByteArrayElements(bytes_array, /*isCopy=*/nullptr); + jbyte* bytes = jni_helper.getEnv()->GetByteArrayElements(bytes_array, /*isCopy=*/nullptr); (*out)[i].assign(reinterpret_cast(bytes), bytes_len); // There is nothing to write back, it is always safe to JNI_ABORT. - env->ReleaseByteArrayElements(bytes_array, bytes, JNI_ABORT); + jni_helper.getEnv()->ReleaseByteArrayElements(bytes_array, bytes, JNI_ABORT); // Explicitly delete to keep the local ref count low. - env->DeleteLocalRef(bytes_array); + jni_helper.getEnv()->DeleteLocalRef(bytes_array); } } -void JavaArrayOfByteToString(JNIEnv* env, jbyteArray jbytes, std::string* out) { +void JavaArrayOfByteToString(JniHelper& jni_helper, jbyteArray jbytes, std::string* out) { std::vector bytes; - JavaArrayOfByteToBytesVector(env, jbytes, &bytes); + JavaArrayOfByteToBytesVector(jni_helper, jbytes, &bytes); *out = std::string(bytes.begin(), bytes.end()); } -void JavaArrayOfByteToBytesVector(JNIEnv* env, jbyteArray array, std::vector* out) { - const size_t len = env->GetArrayLength(array); +void JavaArrayOfByteToBytesVector(JniHelper& jni_helper, jbyteArray array, + std::vector* out) { + const size_t len = jni_helper.getEnv()->GetArrayLength(array); out->resize(len); // It doesn't matter if the array returned by GetByteArrayElements is a copy // or not, as the data will be simply be copied into C++ owned memory below. - jbyte* jbytes = env->GetByteArrayElements(array, /*isCopy=*/nullptr); + jbyte* jbytes = jni_helper.getEnv()->GetByteArrayElements(array, /*isCopy=*/nullptr); uint8_t* bytes = reinterpret_cast(jbytes); std::copy(bytes, bytes + len, out->begin()); // There is nothing to write back, it is always safe to JNI_ABORT. - env->ReleaseByteArrayElements(array, jbytes, JNI_ABORT); + jni_helper.getEnv()->ReleaseByteArrayElements(array, jbytes, JNI_ABORT); } MatcherData::Type StringToType(std::string type_as_string) { @@ -357,9 +368,9 @@ MatcherData::Type StringToType(std::string type_as_string) { return MatcherData::EXACT; } -std::vector javaObjectArrayToMatcherData(JNIEnv* env, jobjectArray array, +std::vector javaObjectArrayToMatcherData(JniHelper& jni_helper, jobjectArray array, std::string& cluster_name_out) { - const size_t len = env->GetArrayLength(array); + const size_t len = jni_helper.getEnv()->GetArrayLength(array); std::vector ret; if (len == 0) { return ret; @@ -369,89 +380,99 @@ std::vector javaObjectArrayToMatcherData(JNIEnv* env, jobjectArray return ret; } - JavaArrayOfByteToString(env, static_cast(env->GetObjectArrayElement(array, 0)), - &cluster_name_out); + JavaArrayOfByteToString( + jni_helper, static_cast(jni_helper.getEnv()->GetObjectArrayElement(array, 0)), + &cluster_name_out); for (size_t i = 1; i < len; i += 3) { std::string name; std::string type_as_string; std::string value; - JavaArrayOfByteToString(env, static_cast(env->GetObjectArrayElement(array, i)), - &name); - JavaArrayOfByteToString(env, static_cast(env->GetObjectArrayElement(array, i + 1)), - &type_as_string); - JavaArrayOfByteToString(env, static_cast(env->GetObjectArrayElement(array, i + 2)), - &value); + JavaArrayOfByteToString( + jni_helper, static_cast(jni_helper.getEnv()->GetObjectArrayElement(array, i)), + &name); + JavaArrayOfByteToString( + jni_helper, + static_cast(jni_helper.getEnv()->GetObjectArrayElement(array, i + 1)), + &type_as_string); + JavaArrayOfByteToString( + jni_helper, + static_cast(jni_helper.getEnv()->GetObjectArrayElement(array, i + 2)), &value); ret.emplace_back(MatcherData(name, StringToType(type_as_string), value)); } return ret; } -void javaByteArrayToProto(JNIEnv* env, jbyteArray source, Envoy::Protobuf::MessageLite* dest) { - jbyte* bytes = env->GetByteArrayElements(source, /* isCopy= */ nullptr); - jsize size = env->GetArrayLength(source); +void javaByteArrayToProto(JniHelper& jni_helper, jbyteArray source, + Envoy::Protobuf::MessageLite* dest) { + jbyte* bytes = jni_helper.getEnv()->GetByteArrayElements(source, /* isCopy= */ nullptr); + jsize size = jni_helper.getEnv()->GetArrayLength(source); bool success = dest->ParseFromArray(bytes, size); RELEASE_ASSERT(success, "Failed to parse protobuf message."); - env->ReleaseByteArrayElements(source, bytes, 0); + jni_helper.getEnv()->ReleaseByteArrayElements(source, bytes, 0); } -#define DEFINE_CALL_METHOD(JAVA_TYPE, JNI_TYPE) \ - JNI_TYPE call##JAVA_TYPE##Method(JNIEnv* env, jobject object, jmethodID method_id, ...) { \ +#define JNI_UTILITY_DEFINE_CALL_METHOD(JAVA_TYPE, JNI_TYPE) \ + JNI_TYPE call##JAVA_TYPE##Method(JniHelper& jni_helper, jobject object, jmethodID method_id, \ + ...) { \ va_list args; \ va_start(args, method_id); \ - JNI_TYPE result = env->Call##JAVA_TYPE##MethodV(object, method_id, args); \ + JNI_TYPE result = jni_helper.getEnv()->Call##JAVA_TYPE##MethodV(object, method_id, args); \ va_end(args); \ Envoy::JNI::Exception::checkAndClear(); \ return result; \ } -void throwException(JNIEnv* env, const char* java_class_name, const char* message) { - jclass java_class = env->FindClass(java_class_name); - jint error = env->ThrowNew(java_class, message); +// TODO(fredyw): Delete these functions are replaced them with the ones from JniHelper + +void throwException(JniHelper& jni_helper, const char* java_class_name, const char* message) { + jclass java_class = jni_helper.getEnv()->FindClass(java_class_name); + jint error = jni_helper.getEnv()->ThrowNew(java_class, message); RELEASE_ASSERT(error == JNI_OK, "Failed to throw an exception."); - env->DeleteLocalRef(java_class); + jni_helper.getEnv()->DeleteLocalRef(java_class); } -void callVoidMethod(JNIEnv* env, jobject object, jmethodID method_id, ...) { +void callVoidMethod(JniHelper& jni_helper, jobject object, jmethodID method_id, ...) { va_list args; va_start(args, method_id); - env->CallVoidMethodV(object, method_id, args); + jni_helper.getEnv()->CallVoidMethodV(object, method_id, args); va_end(args); Envoy::JNI::Exception::checkAndClear(); } -DEFINE_CALL_METHOD(Char, jchar) -DEFINE_CALL_METHOD(Short, jshort) -DEFINE_CALL_METHOD(Int, jint) -DEFINE_CALL_METHOD(Long, jlong) -DEFINE_CALL_METHOD(Double, jdouble) -DEFINE_CALL_METHOD(Boolean, jboolean) -DEFINE_CALL_METHOD(Object, jobject) - -#define DEFINE_CALL_STATIC_METHOD(JAVA_TYPE, JNI_TYPE) \ - JNI_TYPE callStatic##JAVA_TYPE##Method(JNIEnv* env, jclass clazz, jmethodID method_id, ...) { \ +JNI_UTILITY_DEFINE_CALL_METHOD(Char, jchar) +JNI_UTILITY_DEFINE_CALL_METHOD(Short, jshort) +JNI_UTILITY_DEFINE_CALL_METHOD(Int, jint) +JNI_UTILITY_DEFINE_CALL_METHOD(Long, jlong) +JNI_UTILITY_DEFINE_CALL_METHOD(Double, jdouble) +JNI_UTILITY_DEFINE_CALL_METHOD(Boolean, jboolean) +JNI_UTILITY_DEFINE_CALL_METHOD(Object, jobject) + +#define JNI_UTILITY_DEFINE_CALL_STATIC_METHOD(JAVA_TYPE, JNI_TYPE) \ + JNI_TYPE callStatic##JAVA_TYPE##Method(JniHelper& jni_helper, jclass clazz, jmethodID method_id, \ + ...) { \ va_list args; \ va_start(args, method_id); \ - JNI_TYPE result = env->CallStatic##JAVA_TYPE##MethodV(clazz, method_id, args); \ + JNI_TYPE result = jni_helper.getEnv()->CallStatic##JAVA_TYPE##MethodV(clazz, method_id, args); \ va_end(args); \ Envoy::JNI::Exception::checkAndClear(); \ return result; \ } -void callStaticVoidMethod(JNIEnv* env, jclass clazz, jmethodID method_id, ...) { +void callStaticVoidMethod(JniHelper& jni_helper, jclass clazz, jmethodID method_id, ...) { va_list args; va_start(args, method_id); - env->CallStaticVoidMethodV(clazz, method_id, args); + jni_helper.getEnv()->CallStaticVoidMethodV(clazz, method_id, args); va_end(args); Envoy::JNI::Exception::checkAndClear(); } -DEFINE_CALL_STATIC_METHOD(Char, jchar) -DEFINE_CALL_STATIC_METHOD(Short, jshort) -DEFINE_CALL_STATIC_METHOD(Int, jint) -DEFINE_CALL_STATIC_METHOD(Long, jlong) -DEFINE_CALL_STATIC_METHOD(Double, jdouble) -DEFINE_CALL_STATIC_METHOD(Boolean, jboolean) -DEFINE_CALL_STATIC_METHOD(Object, jobject) +JNI_UTILITY_DEFINE_CALL_STATIC_METHOD(Char, jchar) +JNI_UTILITY_DEFINE_CALL_STATIC_METHOD(Short, jshort) +JNI_UTILITY_DEFINE_CALL_STATIC_METHOD(Int, jint) +JNI_UTILITY_DEFINE_CALL_STATIC_METHOD(Long, jlong) +JNI_UTILITY_DEFINE_CALL_STATIC_METHOD(Double, jdouble) +JNI_UTILITY_DEFINE_CALL_STATIC_METHOD(Boolean, jboolean) +JNI_UTILITY_DEFINE_CALL_STATIC_METHOD(Object, jobject) } // namespace JNI } // namespace Envoy diff --git a/mobile/library/common/jni/jni_utility.h b/mobile/library/common/jni/jni_utility.h index c4a22ac3d285a..3fc2fa100f4d4 100644 --- a/mobile/library/common/jni/jni_utility.h +++ b/mobile/library/common/jni/jni_utility.h @@ -6,6 +6,7 @@ #include "source/common/protobuf/protobuf.h" #include "library/common/jni/import/jni_import.h" +#include "library/common/jni/jni_helper.h" #include "library/common/types/c_types.h" #include "library/common/types/managed_envoy_headers.h" #include "library/common/types/matcher_data.h" @@ -44,20 +45,11 @@ void jni_delete_global_ref(void* context); void jni_delete_const_global_ref(const void* context); -/** - * Clears any pending exceptions that may have been rides in result to a call into Java code. - * - * @param env, the JNI env pointer. - * - * @return Whether any pending JNI exception was cleared. - */ -bool clear_pending_exceptions(JNIEnv* env); - -int unbox_integer(JNIEnv* env, jobject boxedInteger); +int unbox_integer(JniHelper& jni_helper, jobject boxedInteger); -envoy_data array_to_native_data(JNIEnv* env, jbyteArray j_data); +envoy_data array_to_native_data(JniHelper& jni_helper, jbyteArray j_data); -envoy_data array_to_native_data(JNIEnv* env, jbyteArray j_data, size_t data_length); +envoy_data array_to_native_data(JniHelper& jni_helper, jbyteArray j_data, size_t data_length); /** * Utility function that copies envoy_data to jbyteArray. @@ -67,11 +59,11 @@ envoy_data array_to_native_data(JNIEnv* env, jbyteArray j_data, size_t data_leng * * @return jbyteArray, copied data. It is up to the function caller to clean up memory. */ -jbyteArray native_data_to_array(JNIEnv* env, envoy_data data); +jbyteArray native_data_to_array(JniHelper& jni_helper, envoy_data data); -jlongArray native_stream_intel_to_array(JNIEnv* env, envoy_stream_intel stream_intel); +jlongArray native_stream_intel_to_array(JniHelper& jni_helper, envoy_stream_intel stream_intel); -jlongArray native_final_stream_intel_to_array(JNIEnv* env, +jlongArray native_final_stream_intel_to_array(JniHelper& jni_helper, envoy_final_stream_intel final_stream_intel); /** @@ -82,85 +74,91 @@ jlongArray native_final_stream_intel_to_array(JNIEnv* env, * * @return jobject, copied data. It is up to the function caller to clean up memory. */ -jobject native_map_to_map(JNIEnv* env, envoy_map map); +jobject native_map_to_map(JniHelper& jni_helper, envoy_map map); -jstring native_data_to_string(JNIEnv* env, envoy_data data); +jstring native_data_to_string(JniHelper& jni_helper, envoy_data data); -envoy_data buffer_to_native_data(JNIEnv* env, jobject j_data); +envoy_data buffer_to_native_data(JniHelper& jni_helper, jobject j_data); -envoy_data buffer_to_native_data(JNIEnv* env, jobject j_data, size_t data_length); +envoy_data buffer_to_native_data(JniHelper& jni_helper, jobject j_data, size_t data_length); -envoy_data* buffer_to_native_data_ptr(JNIEnv* env, jobject j_data); +envoy_data* buffer_to_native_data_ptr(JniHelper& jni_helper, jobject j_data); -envoy_headers to_native_headers(JNIEnv* env, jobjectArray headers); +envoy_headers to_native_headers(JniHelper& jni_helper, jobjectArray headers); -envoy_headers* to_native_headers_ptr(JNIEnv* env, jobjectArray headers); +envoy_headers* to_native_headers_ptr(JniHelper& jni_helper, jobjectArray headers); -envoy_stats_tags to_native_tags(JNIEnv* env, jobjectArray tags); +envoy_stats_tags to_native_tags(JniHelper& jni_helper, jobjectArray tags); -envoy_map to_native_map(JNIEnv* env, jobjectArray entries); +envoy_map to_native_map(JniHelper& jni_helper, jobjectArray entries); /** * Utilities to translate C++ std library constructs to their Java counterpart. * The underlying data is always copied to disentangle C++ and Java objects lifetime. */ -jobjectArray ToJavaArrayOfByteArray(JNIEnv* env, const std::vector& v); +jobjectArray ToJavaArrayOfByteArray(JniHelper& jni_helper, const std::vector& v); -jbyteArray ToJavaByteArray(JNIEnv* env, const uint8_t* bytes, size_t len); +jbyteArray ToJavaByteArray(JniHelper& jni_helper, const uint8_t* bytes, size_t len); -jbyteArray ToJavaByteArray(JNIEnv* env, const std::string& str); +jbyteArray ToJavaByteArray(JniHelper& jni_helper, const std::string& str); -jobjectArray ToJavaArrayOfObjectArray(JNIEnv* env, const Envoy::Types::ManagedEnvoyHeaders& map); +jobjectArray ToJavaArrayOfObjectArray(JniHelper& jni_helper, + const Envoy::Types::ManagedEnvoyHeaders& map); -void JavaArrayOfByteArrayToStringVector(JNIEnv* env, jobjectArray array, +void JavaArrayOfByteArrayToStringVector(JniHelper& jni_helper, jobjectArray array, std::vector* out); -void JavaArrayOfByteToBytesVector(JNIEnv* env, jbyteArray array, std::vector* out); +void JavaArrayOfByteToBytesVector(JniHelper& jni_helper, jbyteArray array, + std::vector* out); -void JavaArrayOfByteToString(JNIEnv* env, jbyteArray jbytes, std::string* out); +void JavaArrayOfByteToString(JniHelper& jni_helper, jbyteArray jbytes, std::string* out); -std::vector javaObjectArrayToMatcherData(JNIEnv* env, jobjectArray array, +std::vector javaObjectArrayToMatcherData(JniHelper& jni_helper, jobjectArray array, std::string& cluster_out); /** Parses the proto from Java's byte array and stores the output into `dest` proto. */ -void javaByteArrayToProto(JNIEnv* env, jbyteArray source, Envoy::Protobuf::MessageLite* dest); +void javaByteArrayToProto(JniHelper& jni_helper, jbyteArray source, + Envoy::Protobuf::MessageLite* dest); + +// TODO(fredyw): Delete these functions are replaced them with the ones from JniHelper /** Throws Java exception with the specified class name and error message. */ -void throwException(JNIEnv* env, const char* java_class_name, const char* message); +void throwException(JniHelper& jni_helper, const char* java_class_name, const char* message); // Helper functions for JNI's `CallMethod` with proper exception handling in order to satisfy // -Xcheck:jni. // See // https://docs.oracle.com/en/java/javase/11/docs/specs/jni/functions.html#calling-instance-methods -#define DECLARE_CALL_METHOD(JAVA_TYPE, JNI_TYPE) \ - JNI_TYPE call##JAVA_TYPE##Method(JNIEnv* env, jobject object, jmethodID method_id, ...); - -void callVoidMethod(JNIEnv* env, jobject object, jmethodID method_id, ...); -DECLARE_CALL_METHOD(Byte, jbyte) -DECLARE_CALL_METHOD(Char, jchar) -DECLARE_CALL_METHOD(Short, jshort) -DECLARE_CALL_METHOD(Int, jint) -DECLARE_CALL_METHOD(Long, jlong) -DECLARE_CALL_METHOD(Double, jdouble) -DECLARE_CALL_METHOD(Boolean, jboolean) -DECLARE_CALL_METHOD(Object, jobject) +#define JNI_UTILITY_DECLARE_CALL_METHOD(JAVA_TYPE, JNI_TYPE) \ + JNI_TYPE call##JAVA_TYPE##Method(JniHelper& jni_helper, jobject object, jmethodID method_id, ...); + +void callVoidMethod(JniHelper& jni_helper, jobject object, jmethodID method_id, ...); +JNI_UTILITY_DECLARE_CALL_METHOD(Byte, jbyte) +JNI_UTILITY_DECLARE_CALL_METHOD(Char, jchar) +JNI_UTILITY_DECLARE_CALL_METHOD(Short, jshort) +JNI_UTILITY_DECLARE_CALL_METHOD(Int, jint) +JNI_UTILITY_DECLARE_CALL_METHOD(Long, jlong) +JNI_UTILITY_DECLARE_CALL_METHOD(Double, jdouble) +JNI_UTILITY_DECLARE_CALL_METHOD(Boolean, jboolean) +JNI_UTILITY_DECLARE_CALL_METHOD(Object, jobject) // Helper functions for JNI's `CallStaticMethod` with proper exception handling in order to // satisfy -Xcheck:jni. // See // https://docs.oracle.com/en/java/javase/11/docs/specs/jni/functions.html#calling-static-methods -#define DECLARE_CALL_STATIC_METHOD(JAVA_TYPE, JNI_TYPE) \ - JNI_TYPE callStatic##JAVA_TYPE##Method(JNIEnv* env, jclass clazz, jmethodID method_id, ...); - -void callStaticVoidMethod(JNIEnv* env, jclass clazz, jmethodID method_id, ...); -DECLARE_CALL_STATIC_METHOD(Byte, jbyte) -DECLARE_CALL_STATIC_METHOD(Char, jchar) -DECLARE_CALL_STATIC_METHOD(Short, jshort) -DECLARE_CALL_STATIC_METHOD(Int, jint) -DECLARE_CALL_STATIC_METHOD(Long, jlong) -DECLARE_CALL_STATIC_METHOD(Double, jdouble) -DECLARE_CALL_STATIC_METHOD(Boolean, jboolean) -DECLARE_CALL_STATIC_METHOD(Object, jobject) +#define JNI_DECLARE_CALL_STATIC_METHOD(JAVA_TYPE, JNI_TYPE) \ + JNI_TYPE callStatic##JAVA_TYPE##Method(JniHelper& jni_helper, jclass clazz, jmethodID method_id, \ + ...); + +void callStaticVoidMethod(JniHelper& jni_helper, jclass clazz, jmethodID method_id, ...); +JNI_DECLARE_CALL_STATIC_METHOD(Byte, jbyte) +JNI_DECLARE_CALL_STATIC_METHOD(Char, jchar) +JNI_DECLARE_CALL_STATIC_METHOD(Short, jshort) +JNI_DECLARE_CALL_STATIC_METHOD(Int, jint) +JNI_DECLARE_CALL_STATIC_METHOD(Long, jlong) +JNI_DECLARE_CALL_STATIC_METHOD(Double, jdouble) +JNI_DECLARE_CALL_STATIC_METHOD(Boolean, jboolean) +JNI_DECLARE_CALL_STATIC_METHOD(Object, jobject) } // namespace JNI } // namespace Envoy From 05d6a72665781e0e1d152f741343db5233f4d74d Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Thu, 2 Nov 2023 20:32:48 +0000 Subject: [PATCH 2/4] Update missing code Signed-off-by: Fredy Wijaya --- .../library/common/jni/android_jni_utility.cc | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/mobile/library/common/jni/android_jni_utility.cc b/mobile/library/common/jni/android_jni_utility.cc index ffe656975707b..1f135650d5661 100644 --- a/mobile/library/common/jni/android_jni_utility.cc +++ b/mobile/library/common/jni/android_jni_utility.cc @@ -17,15 +17,15 @@ bool is_cleartext_permitted(absl::string_view hostname) { #if defined(__ANDROID_API__) envoy_data host = Envoy::Data::Utility::copyToBridgeData(hostname); - JNIEnv* env = Envoy::JNI::get_env(); - jstring java_host = Envoy::JNI::native_data_to_string(env, host); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); + jstring java_host = Envoy::JNI::native_data_to_string(jni_helper, host); jclass jcls_AndroidNetworkLibrary = Envoy::JNI::find_class("io.envoyproxy.envoymobile.utilities.AndroidNetworkLibrary"); - jmethodID jmid_isCleartextTrafficPermitted = env->GetStaticMethodID( + jmethodID jmid_isCleartextTrafficPermitted = jni_helper.getEnv()->GetStaticMethodID( jcls_AndroidNetworkLibrary, "isCleartextTrafficPermitted", "(Ljava/lang/String;)Z"); - jboolean result = env->CallStaticBooleanMethod(jcls_AndroidNetworkLibrary, - jmid_isCleartextTrafficPermitted, java_host); - env->DeleteLocalRef(java_host); + jboolean result = jni_helper.getEnv()->CallStaticBooleanMethod( + jcls_AndroidNetworkLibrary, jmid_isCleartextTrafficPermitted, java_host); + jni_helper.getEnv()->DeleteLocalRef(java_host); release_envoy_data(host); return result == JNI_TRUE; #else @@ -36,12 +36,13 @@ bool is_cleartext_permitted(absl::string_view hostname) { void tag_socket(int ifd, int uid, int tag) { #if defined(__ANDROID_API__) - JNIEnv* env = Envoy::JNI::get_env(); + Envoy::JNI::JniHelper jni_helper(Envoy::JNI::get_env()); jclass jcls_AndroidNetworkLibrary = Envoy::JNI::find_class("io.envoyproxy.envoymobile.utilities.AndroidNetworkLibrary"); jmethodID jmid_tagSocket = - env->GetStaticMethodID(jcls_AndroidNetworkLibrary, "tagSocket", "(III)V"); - env->CallStaticVoidMethod(jcls_AndroidNetworkLibrary, jmid_tagSocket, ifd, uid, tag); + jni_helper.getEnv()->GetStaticMethodID(jcls_AndroidNetworkLibrary, "tagSocket", "(III)V"); + jni_helper.getEnv()->CallStaticVoidMethod(jcls_AndroidNetworkLibrary, jmid_tagSocket, ifd, uid, + tag); #else UNREFERENCED_PARAMETER(ifd); UNREFERENCED_PARAMETER(uid); From 90fd1d4f0c685913feda5797fa3a1dfdaf4cec80 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Thu, 2 Nov 2023 21:01:17 +0000 Subject: [PATCH 3/4] Fix typo Signed-off-by: Fredy Wijaya --- mobile/library/common/jni/jni_utility.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/library/common/jni/jni_utility.cc b/mobile/library/common/jni/jni_utility.cc index 4e319644e3f8a..3d581635ae8a2 100644 --- a/mobile/library/common/jni/jni_utility.cc +++ b/mobile/library/common/jni/jni_utility.cc @@ -219,7 +219,7 @@ envoy_headers to_native_headers(JniHelper& jni_helper, jobjectArray headers) { return to_native_map(jni_helper, headers); } -envoy_headers* to_native_headers_ptr(JniHelper&& jni_helper, jobjectArray headers) { +envoy_headers* to_native_headers_ptr(JniHelper& jni_helper, jobjectArray headers) { // Note: This check works for LocalRefs and GlobalRefs, but will not work for WeakGlobalRefs. // Such usage would generally be inappropriate anyways; like C++ weak_ptrs, one should // acquire a new strong reference before attempting to interact with an object held by From 3fe4dd32aac9ee3fcc35a52af663706674085da7 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Thu, 2 Nov 2023 21:08:59 +0000 Subject: [PATCH 4/4] Update macro name to be more consistent with the rest Signed-off-by: Fredy Wijaya --- mobile/library/common/jni/jni_utility.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mobile/library/common/jni/jni_utility.h b/mobile/library/common/jni/jni_utility.h index 3fc2fa100f4d4..764515e9ac425 100644 --- a/mobile/library/common/jni/jni_utility.h +++ b/mobile/library/common/jni/jni_utility.h @@ -146,19 +146,19 @@ JNI_UTILITY_DECLARE_CALL_METHOD(Object, jobject) // satisfy -Xcheck:jni. // See // https://docs.oracle.com/en/java/javase/11/docs/specs/jni/functions.html#calling-static-methods -#define JNI_DECLARE_CALL_STATIC_METHOD(JAVA_TYPE, JNI_TYPE) \ +#define JNI_UTILITY_DECLARE_CALL_STATIC_METHOD(JAVA_TYPE, JNI_TYPE) \ JNI_TYPE callStatic##JAVA_TYPE##Method(JniHelper& jni_helper, jclass clazz, jmethodID method_id, \ ...); void callStaticVoidMethod(JniHelper& jni_helper, jclass clazz, jmethodID method_id, ...); -JNI_DECLARE_CALL_STATIC_METHOD(Byte, jbyte) -JNI_DECLARE_CALL_STATIC_METHOD(Char, jchar) -JNI_DECLARE_CALL_STATIC_METHOD(Short, jshort) -JNI_DECLARE_CALL_STATIC_METHOD(Int, jint) -JNI_DECLARE_CALL_STATIC_METHOD(Long, jlong) -JNI_DECLARE_CALL_STATIC_METHOD(Double, jdouble) -JNI_DECLARE_CALL_STATIC_METHOD(Boolean, jboolean) -JNI_DECLARE_CALL_STATIC_METHOD(Object, jobject) +JNI_UTILITY_DECLARE_CALL_STATIC_METHOD(Byte, jbyte) +JNI_UTILITY_DECLARE_CALL_STATIC_METHOD(Char, jchar) +JNI_UTILITY_DECLARE_CALL_STATIC_METHOD(Short, jshort) +JNI_UTILITY_DECLARE_CALL_STATIC_METHOD(Int, jint) +JNI_UTILITY_DECLARE_CALL_STATIC_METHOD(Long, jlong) +JNI_UTILITY_DECLARE_CALL_STATIC_METHOD(Double, jdouble) +JNI_UTILITY_DECLARE_CALL_STATIC_METHOD(Boolean, jboolean) +JNI_UTILITY_DECLARE_CALL_STATIC_METHOD(Object, jobject) } // namespace JNI } // namespace Envoy