diff options
author | Assam Boudjelthia <assam.boudjelthia@qt.io> | 2022-12-20 18:39:30 +0200 |
---|---|---|
committer | Assam Boudjelthia <assam.boudjelthia@qt.io> | 2023-01-20 13:48:20 +0000 |
commit | dbb622a38d6ce4fbd7fba010aea238e5f9552c67 (patch) | |
tree | 858c0bce9f746d7177bb7d624145691e5b2ef568 /src/corelib/kernel/qjnienvironment.cpp | |
parent | e04af5b9ea60f17787e4cf81cfe1b79a69be3406 (diff) |
Android: print jni exceptions from Qt instead of ExceptionDescribe()
This makes the exceptions prints tagged with the app's name/tag,
and also can allow QTest::ignoreMessage() to handle exceptions as now
it cannot filter messages out because they're printed by the Android
system.
Pick-to: 6.5
Change-Id: I9f5132b9ec5b5cd8fb35707eaaf68aa517f417ec
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Diffstat (limited to 'src/corelib/kernel/qjnienvironment.cpp')
-rw-r--r-- | src/corelib/kernel/qjnienvironment.cpp | 77 |
1 files changed, 65 insertions, 12 deletions
diff --git a/src/corelib/kernel/qjnienvironment.cpp b/src/corelib/kernel/qjnienvironment.cpp index 88ad68178e..7fda44a21e 100644 --- a/src/corelib/kernel/qjnienvironment.cpp +++ b/src/corelib/kernel/qjnienvironment.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qjnienvironment.h" -#include "qjniobject.h" #include "qjnihelpers_p.h" #include <QtCore/QThread> @@ -444,16 +443,57 @@ bool QJniEnvironment::registerNativeMethods(jclass clazz, const JNINativeMethod */ bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode outputMode) { - if (Q_UNLIKELY(d->jniEnv->ExceptionCheck())) { - if (outputMode != OutputMode::Silent) - d->jniEnv->ExceptionDescribe(); - d->jniEnv->ExceptionClear(); + return checkAndClearExceptions(d->jniEnv, outputMode); +} - return true; +namespace { + // Any pending exception need to be cleared before calling this + QString exceptionMessage(JNIEnv *env, const jthrowable &exception) + { + if (!exception) + return {}; + + auto logError = []() { + qWarning() << "QJniEnvironment: a null object returned or an exception occurred while " + "fetching a prior exception message"; + }; + + auto checkAndClear = [env]() { + if (Q_UNLIKELY(env->ExceptionCheck())) { + env->ExceptionClear(); + return true; + } + return false; + }; + + const jclass logClazz = env->FindClass("android/util/Log"); + if (checkAndClear() || !logClazz) { + logError(); + return {}; + } + + const jmethodID methodId = env->GetStaticMethodID(logClazz, "getStackTraceString", + "(Ljava/lang/Throwable;)Ljava/lang/String;"); + if (checkAndClear() || !methodId) { + logError(); + return {}; + } + + jvalue value; + value.l = static_cast<jobject>(exception); + const jobject messageObj = env->CallStaticObjectMethodA(logClazz, methodId, &value); + const jstring jmessage = static_cast<jstring>(messageObj); + if (checkAndClear()) + return {}; + + char const *utfMessage = env->GetStringUTFChars(jmessage, 0); + const QString message = QString::fromUtf8(utfMessage); + + env->ReleaseStringUTFChars(jmessage, utfMessage); + + return message; } - - return false; -} +} // end namespace /*! \fn QJniEnvironment::checkAndClearExceptions(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose) @@ -472,9 +512,22 @@ bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode output bool QJniEnvironment::checkAndClearExceptions(JNIEnv *env, QJniEnvironment::OutputMode outputMode) { if (Q_UNLIKELY(env->ExceptionCheck())) { - if (outputMode != OutputMode::Silent) - env->ExceptionDescribe(); - env->ExceptionClear(); + if (outputMode == OutputMode::Verbose) { + if (jthrowable exception = env->ExceptionOccurred()) { + env->ExceptionClear(); + const QString message = exceptionMessage(env, exception); + // Print to QWARN since env->ExceptionDescribe() does the same + if (!message.isEmpty()) + qWarning().noquote() << message; + env->DeleteLocalRef(exception); + } else { + // if the exception object is null for some reason just + env->ExceptionDescribe(); + env->ExceptionClear(); + } + } else { + env->ExceptionClear(); + } return true; } |