summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qjnienvironment.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qjnienvironment.cpp')
-rw-r--r--src/corelib/kernel/qjnienvironment.cpp246
1 files changed, 156 insertions, 90 deletions
diff --git a/src/corelib/kernel/qjnienvironment.cpp b/src/corelib/kernel/qjnienvironment.cpp
index 9766ff4d3b..1e2826e76b 100644
--- a/src/corelib/kernel/qjnienvironment.cpp
+++ b/src/corelib/kernel/qjnienvironment.cpp
@@ -1,44 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// 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>
@@ -66,8 +29,6 @@ QT_BEGIN_NAMESPACE
It has not been tested for other platforms.
*/
-static const char qJniThreadName[] = "QtThread";
-
class QJniEnvironmentPrivate
{
public:
@@ -83,47 +44,42 @@ public:
}
};
-struct QJniLocalRefDeleterPrivate
-{
- static void cleanup(jobject obj)
- {
- if (!obj)
- return;
-
- QJniEnvironment env;
- env->DeleteLocalRef(obj);
- }
-};
-
-// To simplify this we only define it for jobjects.
-typedef QScopedPointer<_jobject, QJniLocalRefDeleterPrivate> QJniScopedLocalRefPrivate;
-
-
Q_GLOBAL_STATIC(QThreadStorage<QJniEnvironmentPrivateTLS *>, jniEnvTLS)
-
/*!
- \fn QJniEnvironment::QJniEnvironment()
-
Constructs a new JNI Environment object and attaches the current thread to the Java VM.
*/
QJniEnvironment::QJniEnvironment()
: d(new QJniEnvironmentPrivate{})
{
+ d->jniEnv = getJniEnv();
+}
+
+/*!
+ Returns the JNIEnv pointer for the current thread.
+
+ The current thread will be attached to the Java VM.
+*/
+JNIEnv *QJniEnvironment::getJniEnv()
+{
+ JNIEnv *jniEnv = nullptr;
+
JavaVM *vm = QtAndroidPrivate::javaVM();
- const jint ret = vm->GetEnv((void**)&d->jniEnv, JNI_VERSION_1_6);
- if (ret == JNI_OK) // Already attached
- return;
+ const jint ret = vm->GetEnv((void**)&jniEnv, JNI_VERSION_1_6);
if (ret == JNI_EDETACHED) { // We need to (re-)attach
- JavaVMAttachArgs args = { JNI_VERSION_1_6, qJniThreadName, nullptr };
- if (vm->AttachCurrentThread(&d->jniEnv, &args) != JNI_OK)
- return;
-
- if (!jniEnvTLS->hasLocalData()) // If we attached the thread we own it.
- jniEnvTLS->setLocalData(new QJniEnvironmentPrivateTLS);
+ const QByteArray threadName = QThread::currentThread()->objectName().toUtf8();
+ JavaVMAttachArgs args = { JNI_VERSION_1_6,
+ threadName.isEmpty() ? "QtThread" : threadName.constData(),
+ nullptr
+ };
+ if (vm->AttachCurrentThread(&jniEnv, &args) == JNI_OK) {
+ if (!jniEnvTLS->hasLocalData()) // If we attached the thread we own it.
+ jniEnvTLS->setLocalData(new QJniEnvironmentPrivateTLS);
+ }
}
+ return jniEnv;
}
/*!
@@ -148,8 +104,6 @@ bool QJniEnvironment::isValid() const
}
/*!
- \fn JNIEnv *QJniEnvironment::operator->() const
-
Provides access to the JNI Environment's \c JNIEnv pointer.
*/
JNIEnv *QJniEnvironment::operator->() const
@@ -158,8 +112,6 @@ JNIEnv *QJniEnvironment::operator->() const
}
/*!
- \fn JNIEnv &QJniEnvironment::operator*() const
-
Returns the JNI Environment's \c JNIEnv object.
*/
JNIEnv &QJniEnvironment::operator*() const
@@ -168,8 +120,6 @@ JNIEnv &QJniEnvironment::operator*() const
}
/*!
- \fn JNIEnv *QJniEnvironment::jniEnv() const
-
Returns the JNI Environment's \c JNIEnv pointer.
*/
JNIEnv *QJniEnvironment::jniEnv() const
@@ -232,6 +182,16 @@ jmethodID QJniEnvironment::findMethod(jclass clazz, const char *methodName, cons
}
/*!
+ \fn template<typename ...Args> jmethodId QJniEnvironment::findMethod(jclass clazz, const char *methodName)
+ \since 6.4
+
+ Searches for an instance method of a class \a clazz. The method is specified
+ by its \a methodName, the signature is deduced from the template parameters.
+
+ Returns the method ID or \c nullptr if the method is not found.
+*/
+
+/*!
Searches for a static method of a class \a clazz. The method is specified
by its \a methodName and \a signature.
@@ -265,9 +225,28 @@ jmethodID QJniEnvironment::findStaticMethod(jclass clazz, const char *methodName
return nullptr;
}
+/*!
+ \fn template<typename ...Args> jmethodId QJniEnvironment::findStaticMethod(jclass clazz, const char *methodName)
+ \since 6.4
+
+ Searches for an instance method of a class \a clazz. The method is specified
+ by its \a methodName, the signature is deduced from the template parameters.
+
+ Returns the method ID or \c nullptr if the method is not found.
+
+ \code
+ QJniEnvironment env;
+ jclass javaClass = env.findClass("org/qtproject/example/android/CustomClass");
+ jmethodID methodId = env.findStaticMethod<void, jstring>(javaClass, "staticJavaMethod");
+ QJniObject javaMessage = QJniObject::fromString("findStaticMethod example");
+ QJniObject::callStaticMethod<void>(javaClass,
+ methodId,
+ javaMessage.object<jstring>());
+ \endcode
+*/
/*!
- Searches for an member field of a class \a clazz. The field is specified
+ Searches for a member field of a class \a clazz. The field is specified
by its \a fieldName and \a signature.
Returns the field ID or \c nullptr if the field is not found.
@@ -289,6 +268,16 @@ jfieldID QJniEnvironment::findField(jclass clazz, const char *fieldName, const c
}
/*!
+ \fn template<typename T> jfieldID QJniEnvironment::findField(jclass clazz, const char *fieldName)
+ \since 6.4
+
+ Searches for a member field of a class \a clazz. The field is specified
+ by its \a fieldName. The signature of the field is deduced from the template parameter.
+
+ Returns the field ID or \c nullptr if the field is not found.
+*/
+
+/*!
Searches for a static field of a class \a clazz. The field is specified
by its \a fieldName and \a signature.
@@ -311,8 +300,16 @@ jfieldID QJniEnvironment::findStaticField(jclass clazz, const char *fieldName, c
}
/*!
- \fn JavaVM *QJniEnvironment::javaVM()
+ \fn template<typename T> jfieldID QJniEnvironment::findStaticField(jclass clazz, const char *fieldName)
+ \since 6.4
+ Searches for a static field of a class \a clazz. The field is specified
+ by its \a fieldName. The signature of the field is deduced from the template parameter.
+
+ Returns the field ID or \c nullptr if the field is not found.
+*/
+
+/*!
Returns the Java VM interface for the current process. Although it might
be possible to have multiple Java VMs per process, Android allows only one.
@@ -354,6 +351,15 @@ bool QJniEnvironment::registerNativeMethods(const char *className, const JNINati
return registerNativeMethods(clazz, methods, size);
}
+
+/*!
+ \fn bool QJniEnvironment::registerNativeMethods(const char *className, std::initializer_list<JNINativeMethod> methods)
+ \overload
+
+ Registers the native functions methods in \a methods for the Java class \a className.
+ Returns \c true if the registration is successful, otherwise \c false.
+*/
+
#if QT_DEPRECATED_SINCE(6, 2)
/*!
\overload
@@ -409,6 +415,14 @@ bool QJniEnvironment::registerNativeMethods(jclass clazz, const JNINativeMethod
}
/*!
+ \fn bool QJniEnvironment::registerNativeMethods(jclass clazz, std::initializer_list<JNINativeMethod> methods)
+ \overload
+
+ Registers the native functions methods in \a methods for the Java class \a clazz.
+ Returns \c true if the registration is successful, otherwise \c false.
+*/
+
+/*!
\enum QJniEnvironment::OutputMode
\value Silent The exceptions are cleaned silently
@@ -431,20 +445,59 @@ 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, 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)
-
Cleans any pending exceptions for \a env, either silently or reporting
stack backtrace, depending on the \a outputMode. This is useful when you
already have a \c JNIEnv pointer such as in a native function implementation.
@@ -459,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;
}