diff --git a/android/jni/com/mapswithme/core/jni_helper.cpp b/android/jni/com/mapswithme/core/jni_helper.cpp index b7cd8354eb..75bb12e407 100644 --- a/android/jni/com/mapswithme/core/jni_helper.cpp +++ b/android/jni/com/mapswithme/core/jni_helper.cpp @@ -178,6 +178,32 @@ shared_ptr make_global_ref(jobject obj) return shared_ptr(ref, global_ref_deleter()); } +string ToNativeString(JNIEnv * env, const jthrowable & e) +{ + jni::TScopedLocalClassRef logClassRef(env, env->FindClass("android/util/Log")); + ASSERT(logClassRef.get(), ()); + static jmethodID const getStacktraceMethod = + jni::GetStaticMethodID(env, logClassRef.get(), "getStackTraceString", + "(Ljava/lang/Throwable;)Ljava/lang/String;"); + ASSERT(getStacktraceMethod, ()); + TScopedLocalRef resultRef(env, env->CallStaticObjectMethod(logClassRef.get(), getStacktraceMethod, e)); + return ToNativeString(env, (jstring) resultRef.get()); +} + + +bool HandleJavaException(JNIEnv * env) +{ + if (env->ExceptionCheck()) + { + const jthrowable e = env->ExceptionOccurred(); + env->ExceptionDescribe(); + env->ExceptionClear(); + LOG(LERROR, (ToNativeString(env, e))); + return true; + } + return false; +} + string DescribeException() { JNIEnv * env = GetEnv(); @@ -189,16 +215,9 @@ string DescribeException() // have to clear the exception before JNI will work again. env->ExceptionClear(); - jclass eclass = env->GetObjectClass(e); - - jmethodID mid = env->GetMethodID(eclass, "toString", "()Ljava/lang/String;"); - - jstring jErrorMsg = (jstring) env->CallObjectMethod(e, mid); - - return ToNativeString(env, jErrorMsg); + return ToNativeString(env, e); } - - return ""; + return {}; } jobject GetNewParcelablePointD(JNIEnv * env, m2::PointD const & point) diff --git a/android/jni/com/mapswithme/core/jni_helper.hpp b/android/jni/com/mapswithme/core/jni_helper.hpp index c2cd781734..86f97fc7c4 100644 --- a/android/jni/com/mapswithme/core/jni_helper.hpp +++ b/android/jni/com/mapswithme/core/jni_helper.hpp @@ -46,6 +46,7 @@ jclass GetStringClass(JNIEnv * env); char const * GetStringClassName(); string DescribeException(); +bool HandleJavaException(JNIEnv * env); shared_ptr make_global_ref(jobject obj); using TScopedLocalRef = ScopedLocalRef; diff --git a/android/jni/com/mapswithme/platform/SocketImpl.cpp b/android/jni/com/mapswithme/platform/SocketImpl.cpp index cda456dc1a..632449a126 100644 --- a/android/jni/com/mapswithme/platform/SocketImpl.cpp +++ b/android/jni/com/mapswithme/platform/SocketImpl.cpp @@ -28,8 +28,11 @@ public: JNIEnv * env = jni::GetEnv(); static jmethodID const openMethod = jni::GetMethodID(env, m_self, "open", "(Ljava/lang/String;I)Z"); - return env->CallBooleanMethod(m_self, openMethod, jni::ToJavaString(env, host), - static_cast(port)); + jni::TScopedLocalRef hostRef(env, jni::ToJavaString(env, host)); + jboolean result = env->CallBooleanMethod(m_self, openMethod, hostRef.get(), static_cast(port)); + if (jni::HandleJavaException(env)) + return false; + return result; } void Close() @@ -37,6 +40,7 @@ public: JNIEnv * env = jni::GetEnv(); static jmethodID const closeMethod = jni::GetMethodID(env, m_self, "close", "()V"); env->CallVoidMethod(m_self, closeMethod); + jni::HandleJavaException(env); } bool Read(uint8_t * data, uint32_t count) @@ -45,19 +49,26 @@ public: jbyteArray array = env->NewByteArray(count); static jmethodID const readMethod = jni::GetMethodID(env, m_self, "read", "([BI)Z"); jboolean result = env->CallBooleanMethod(m_self, readMethod, array, static_cast(count)); + if (jni::HandleJavaException(env)) + return false; //this call copies java byte array to native buffer env->GetByteArrayRegion(array, 0, count, reinterpret_cast(data)); + if (jni::HandleJavaException(env)) + return false; return result; } bool Write(uint8_t const * data, uint32_t count) { JNIEnv * env = jni::GetEnv(); - jbyteArray array = env->NewByteArray(count); + jni::TScopedLocalByteArrayRef arrayRef(env, env->NewByteArray(count)); //this call copies native buffer to java byte array - env->SetByteArrayRegion(array, 0, count, reinterpret_cast(data)); + env->SetByteArrayRegion(arrayRef.get(), 0, count, reinterpret_cast(data)); static jmethodID const writeMethod = jni::GetMethodID(env, m_self, "write", "([BI)Z"); - return env->CallBooleanMethod(m_self, writeMethod, array, static_cast(count)); + jboolean result = env->CallBooleanMethod(m_self, writeMethod, arrayRef.get(), static_cast(count)); + if (jni::HandleJavaException(env)) + return false; + return result; } void SetTimeout(uint32_t milliseconds) @@ -65,6 +76,7 @@ public: JNIEnv * env = jni::GetEnv(); static jmethodID const setTimeoutMethod = jni::GetMethodID(env, m_self, "setTimeout", "(I)V"); env->CallVoidMethod(m_self, setTimeoutMethod, static_cast(milliseconds)); + jni::HandleJavaException(env); }; private: diff --git a/android/jni/com/mapswithme/util/HttpClient.cpp b/android/jni/com/mapswithme/util/HttpClient.cpp index ae631514e6..49e71dbff3 100644 --- a/android/jni/com/mapswithme/util/HttpClient.cpp +++ b/android/jni/com/mapswithme/util/HttpClient.cpp @@ -38,14 +38,6 @@ SOFTWARE. DECLARE_EXCEPTION(JniException, RootException); -#define CLEAR_AND_RETURN_FALSE_ON_EXCEPTION \ - if (env->ExceptionCheck()) \ - { \ - env->ExceptionDescribe(); \ - env->ExceptionClear(); \ - return false; \ - } - namespace { void RethrowOnJniException(ScopedEnv & env) @@ -199,14 +191,16 @@ bool HttpClient::RunHttpRequest() // Create and fill request params. jni::ScopedLocalRef const jniUrl(env.get(), jni::ToJavaString(env.get(), m_urlRequested)); - CLEAR_AND_RETURN_FALSE_ON_EXCEPTION + if (jni::HandleJavaException(env.get())) + return false; static jmethodID const httpParamsConstructor = jni::GetConstructorID(env.get(), g_httpParamsClazz, "(Ljava/lang/String;)V"); jni::ScopedLocalRef const httpParamsObject( env.get(), env->NewObject(g_httpParamsClazz, httpParamsConstructor, jniUrl.get())); - CLEAR_AND_RETURN_FALSE_ON_EXCEPTION + if (jni::HandleJavaException(env.get())) + return false; // Cache it on the first call. static jfieldID const dataField = env->GetFieldID(g_httpParamsClazz, "data", "[B"); @@ -214,14 +208,17 @@ bool HttpClient::RunHttpRequest() { jni::ScopedLocalRef const jniPostData(env.get(), env->NewByteArray(m_bodyData.size())); - CLEAR_AND_RETURN_FALSE_ON_EXCEPTION + if (jni::HandleJavaException(env.get())) + return false; env->SetByteArrayRegion(jniPostData.get(), 0, m_bodyData.size(), reinterpret_cast(m_bodyData.data())); - CLEAR_AND_RETURN_FALSE_ON_EXCEPTION + if (jni::HandleJavaException(env.get())) + return false; env->SetObjectField(httpParamsObject.get(), dataField, jniPostData.get()); - CLEAR_AND_RETURN_FALSE_ON_EXCEPTION + if (jni::HandleJavaException(env.get())) + return false; } ASSERT(!m_httpMethod.empty(), ("Http method type can not be empty.")); @@ -250,7 +247,8 @@ bool HttpClient::RunHttpRequest() // call DeleteLocalRef(response). jobject const response = env->CallStaticObjectMethod(g_httpClientClazz, httpClientClassRun, httpParamsObject.get()); - CLEAR_AND_RETURN_FALSE_ON_EXCEPTION + if (jni::HandleJavaException(env.get())) + return false; try { @@ -266,7 +264,8 @@ bool HttpClient::RunHttpRequest() // dataField is already cached above. jni::ScopedLocalRef const jniData( env.get(), static_cast(env->GetObjectField(response, dataField))); - CLEAR_AND_RETURN_FALSE_ON_EXCEPTION + if (jni::HandleJavaException(env.get())) + return false; if (jniData) { jbyte * buffer = env->GetByteArrayElements(jniData.get(), nullptr);