Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qjnienvironment.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qjnienvironment.h"
5#include "qjnihelpers_p.h"
6
7#include <QtCore/QThread>
8#include <QtCore/QThreadStorage>
9
11
33{
34public:
35 JNIEnv *jniEnv = nullptr;
36};
37
39{
40public:
42 {
43 QtAndroidPrivate::javaVM()->DetachCurrentThread();
44 }
45};
46
47Q_GLOBAL_STATIC(QThreadStorage<QJniEnvironmentPrivateTLS *>, jniEnvTLS)
48
49
50
53QJniEnvironment::QJniEnvironment()
55{
56 d->jniEnv = getJniEnv();
57}
58
64JNIEnv *QJniEnvironment::getJniEnv()
65{
66 JNIEnv *jniEnv = nullptr;
67
68 JavaVM *vm = QtAndroidPrivate::javaVM();
69 const jint ret = vm->GetEnv((void**)&jniEnv, JNI_VERSION_1_6);
70
71 if (ret == JNI_EDETACHED) { // We need to (re-)attach
72 const QByteArray threadName = QThread::currentThread()->objectName().toUtf8();
73 JavaVMAttachArgs args = { JNI_VERSION_1_6,
74 threadName.isEmpty() ? "QtThread" : threadName.constData(),
75 nullptr
76 };
77 if (vm->AttachCurrentThread(&jniEnv, &args) == JNI_OK) {
78 if (!jniEnvTLS->hasLocalData()) // If we attached the thread we own it.
79 jniEnvTLS->setLocalData(new QJniEnvironmentPrivateTLS);
80 }
81 }
82 return jniEnv;
83}
84
91QJniEnvironment::~QJniEnvironment()
92{
93 checkAndClearExceptions();
94}
95
101bool QJniEnvironment::isValid() const
102{
103 return d->jniEnv;
104}
105
109JNIEnv *QJniEnvironment::operator->() const
110{
111 return d->jniEnv;
112}
113
117JNIEnv &QJniEnvironment::operator*() const
118{
119 return *d->jniEnv;
120}
121
125JNIEnv *QJniEnvironment::jniEnv() const
126{
127 return d->jniEnv;
128}
129
157jclass QJniEnvironment::findClass(const char *className)
158{
159 return QtAndroidPrivate::findClass(className, d->jniEnv);
160}
161
173jmethodID QJniEnvironment::findMethod(jclass clazz, const char *methodName, const char *signature)
174{
175 if (clazz) {
176 jmethodID id = d->jniEnv->GetMethodID(clazz, methodName, signature);
177 if (!checkAndClearExceptions(d->jniEnv))
178 return id;
179 }
180
181 return nullptr;
182}
183
217jmethodID QJniEnvironment::findStaticMethod(jclass clazz, const char *methodName, const char *signature)
218{
219 if (clazz) {
220 jmethodID id = d->jniEnv->GetStaticMethodID(clazz, methodName, signature);
221 if (!checkAndClearExceptions(d->jniEnv))
222 return id;
223 }
224
225 return nullptr;
226}
227
259jfieldID QJniEnvironment::findField(jclass clazz, const char *fieldName, const char *signature)
260{
261 if (clazz) {
262 jfieldID id = d->jniEnv->GetFieldID(clazz, fieldName, signature);
263 if (!checkAndClearExceptions())
264 return id;
265 }
266
267 return nullptr;
268}
269
291jfieldID QJniEnvironment::findStaticField(jclass clazz, const char *fieldName, const char *signature)
292{
293 if (clazz) {
294 jfieldID id = d->jniEnv->GetStaticFieldID(clazz, fieldName, signature);
295 if (!checkAndClearExceptions())
296 return id;
297 }
298
299 return nullptr;
300}
301
317JavaVM *QJniEnvironment::javaVM()
318{
320}
321
344bool QJniEnvironment::registerNativeMethods(const char *className, const JNINativeMethod methods[],
345 int size)
346{
347 const jclass clazz = findClass(className);
348
349 if (!clazz)
350 return false;
351
352 return registerNativeMethods(clazz, methods, size);
353}
354
363#if QT_DEPRECATED_SINCE(6, 2)
388bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMethod methods[],
389 int size)
390{
391 return registerNativeMethods(className, const_cast<const JNINativeMethod*>(methods), size);
392}
393#endif
407bool QJniEnvironment::registerNativeMethods(jclass clazz, const JNINativeMethod methods[],
408 int size)
409{
410 if (d->jniEnv->RegisterNatives(clazz, methods, size) < 0) {
411 checkAndClearExceptions();
412 return false;
413 }
414 return true;
415}
416
446bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode outputMode)
447{
448 return checkAndClearExceptions(d->jniEnv, outputMode);
449}
450
451namespace {
452 // Any pending exception need to be cleared before calling this
453 QString exceptionMessage(JNIEnv *env, jthrowable exception)
454 {
455 if (!exception)
456 return {};
457
458 auto logError = []() {
459 qWarning() << "QJniEnvironment: a null object returned or an exception occurred while "
460 "fetching a prior exception message";
461 };
462
463 auto checkAndClear = [env]() {
464 if (Q_UNLIKELY(env->ExceptionCheck())) {
465 env->ExceptionClear();
466 return true;
467 }
468 return false;
469 };
470
471 const jclass logClazz = env->FindClass("android/util/Log");
472 if (checkAndClear() || !logClazz) {
473 logError();
474 return {};
475 }
476
477 const jmethodID methodId = env->GetStaticMethodID(logClazz, "getStackTraceString",
478 "(Ljava/lang/Throwable;)Ljava/lang/String;");
479 if (checkAndClear() || !methodId) {
480 logError();
481 return {};
482 }
483
484 jvalue value;
485 value.l = static_cast<jobject>(exception);
486 const jobject messageObj = env->CallStaticObjectMethodA(logClazz, methodId, &value);
487 const jstring jmessage = static_cast<jstring>(messageObj);
488 if (checkAndClear())
489 return {};
490
491 char const *utfMessage = env->GetStringUTFChars(jmessage, 0);
492 const QString message = QString::fromUtf8(utfMessage);
493
494 env->ReleaseStringUTFChars(jmessage, utfMessage);
495
496 return message;
497 }
498} // end namespace
499
512bool QJniEnvironment::checkAndClearExceptions(JNIEnv *env, QJniEnvironment::OutputMode outputMode)
513{
514 if (Q_UNLIKELY(env->ExceptionCheck())) {
515 if (outputMode == OutputMode::Verbose) {
516 if (jthrowable exception = env->ExceptionOccurred()) {
517 env->ExceptionClear();
518 const QString message = exceptionMessage(env, exception);
519 // Print to QWARN since env->ExceptionDescribe() does the same
520 if (!message.isEmpty())
521 qWarning().noquote() << message;
522 env->DeleteLocalRef(exception);
523 } else {
524 // if the exception object is null for some reason just
525 env->ExceptionDescribe();
526 env->ExceptionClear();
527 }
528 } else {
529 env->ExceptionClear();
530 }
531
532 return true;
533 }
534
535 return false;
536}
537
static JNINativeMethod methods[]
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
static QThread * currentThread()
Definition qthread.cpp:1039
Combined button and popup list for selecting options.
Q_CORE_EXPORT jclass findClass(const char *className, JNIEnv *env)
Q_CORE_EXPORT JavaVM * javaVM()
#define Q_UNLIKELY(x)
static QString methodName(const QDBusIntrospection::Method &method)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition qlogging.h:166
return ret
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint id
[7]
GLuint GLsizei const GLchar * message
const char className[16]
[1]
Definition qwizard.cpp:100
QJSValueList args