summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qjnienvironment.cpp
diff options
context:
space:
mode:
authorAssam Boudjelthia <assam.boudjelthia@qt.io>2022-12-20 18:39:30 +0200
committerAssam Boudjelthia <assam.boudjelthia@qt.io>2023-01-20 13:48:20 +0000
commitdbb622a38d6ce4fbd7fba010aea238e5f9552c67 (patch)
tree858c0bce9f746d7177bb7d624145691e5b2ef568 /src/corelib/kernel/qjnienvironment.cpp
parente04af5b9ea60f17787e4cf81cfe1b79a69be3406 (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.cpp77
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;
}