Qt 6.x
The Qt SDK
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
32static const char qJniThreadName[] = "QtThread";
33
35{
36public:
37 JNIEnv *jniEnv = nullptr;
38};
39
41{
42public:
44 {
45 QtAndroidPrivate::javaVM()->DetachCurrentThread();
46 }
47};
48
50{
51 static void cleanup(jobject obj)
52 {
53 if (!obj)
54 return;
55
57 env->DeleteLocalRef(obj);
58 }
59};
60
61// To simplify this we only define it for jobjects.
63
64
66
67
68
69
74QJniEnvironment::QJniEnvironment()
76{
77 JavaVM *vm = QtAndroidPrivate::javaVM();
78 const jint ret = vm->GetEnv((void**)&d->jniEnv, JNI_VERSION_1_6);
79 if (ret == JNI_OK) // Already attached
80 return;
81
82 if (ret == JNI_EDETACHED) { // We need to (re-)attach
83 JavaVMAttachArgs args = { JNI_VERSION_1_6, qJniThreadName, nullptr };
84 if (vm->AttachCurrentThread(&d->jniEnv, &args) != JNI_OK)
85 return;
86
87 if (!jniEnvTLS->hasLocalData()) // If we attached the thread we own it.
88 jniEnvTLS->setLocalData(new QJniEnvironmentPrivateTLS);
89 }
90}
91
98QJniEnvironment::~QJniEnvironment()
99{
100 checkAndClearExceptions();
101}
102
108bool QJniEnvironment::isValid() const
109{
110 return d->jniEnv;
111}
112
118JNIEnv *QJniEnvironment::operator->() const
119{
120 return d->jniEnv;
121}
122
128JNIEnv &QJniEnvironment::operator*() const
129{
130 return *d->jniEnv;
131}
132
138JNIEnv *QJniEnvironment::jniEnv() const
139{
140 return d->jniEnv;
141}
142
170jclass QJniEnvironment::findClass(const char *className)
171{
172 return QtAndroidPrivate::findClass(className, d->jniEnv);
173}
174
186jmethodID QJniEnvironment::findMethod(jclass clazz, const char *methodName, const char *signature)
187{
188 if (clazz) {
189 jmethodID id = d->jniEnv->GetMethodID(clazz, methodName, signature);
190 if (!checkAndClearExceptions(d->jniEnv))
191 return id;
192 }
193
194 return nullptr;
195}
196
230jmethodID QJniEnvironment::findStaticMethod(jclass clazz, const char *methodName, const char *signature)
231{
232 if (clazz) {
233 jmethodID id = d->jniEnv->GetStaticMethodID(clazz, methodName, signature);
234 if (!checkAndClearExceptions(d->jniEnv))
235 return id;
236 }
237
238 return nullptr;
239}
240
272jfieldID QJniEnvironment::findField(jclass clazz, const char *fieldName, const char *signature)
273{
274 if (clazz) {
275 jfieldID id = d->jniEnv->GetFieldID(clazz, fieldName, signature);
276 if (!checkAndClearExceptions())
277 return id;
278 }
279
280 return nullptr;
281}
282
304jfieldID QJniEnvironment::findStaticField(jclass clazz, const char *fieldName, const char *signature)
305{
306 if (clazz) {
307 jfieldID id = d->jniEnv->GetStaticFieldID(clazz, fieldName, signature);
308 if (!checkAndClearExceptions())
309 return id;
310 }
311
312 return nullptr;
313}
314
332JavaVM *QJniEnvironment::javaVM()
333{
335}
336
359bool QJniEnvironment::registerNativeMethods(const char *className, const JNINativeMethod methods[],
360 int size)
361{
362 const jclass clazz = findClass(className);
363
364 if (!clazz)
365 return false;
366
367 return registerNativeMethods(clazz, methods, size);
368}
369#if QT_DEPRECATED_SINCE(6, 2)
394bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMethod methods[],
395 int size)
396{
397 return registerNativeMethods(className, const_cast<const JNINativeMethod*>(methods), size);
398}
399#endif
413bool QJniEnvironment::registerNativeMethods(jclass clazz, const JNINativeMethod methods[],
414 int size)
415{
416 if (d->jniEnv->RegisterNatives(clazz, methods, size) < 0) {
417 checkAndClearExceptions();
418 return false;
419 }
420 return true;
421}
422
444bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode outputMode)
445{
446 return checkAndClearExceptions(d->jniEnv, outputMode);
447}
448
449namespace {
450 // Any pending exception need to be cleared before calling this
451 QString exceptionMessage(JNIEnv *env, const jthrowable &exception)
452 {
453 if (!exception)
454 return {};
455
456 auto logError = []() {
457 qWarning() << "QJniEnvironment: a null object returned or an exception occurred while "
458 "fetching a prior exception message";
459 };
460
461 auto checkAndClear = [env]() {
462 if (Q_UNLIKELY(env->ExceptionCheck())) {
463 env->ExceptionClear();
464 return true;
465 }
466 return false;
467 };
468
469 const jclass logClazz = env->FindClass("android/util/Log");
470 if (checkAndClear() || !logClazz) {
471 logError();
472 return {};
473 }
474
475 const jmethodID methodId = env->GetStaticMethodID(logClazz, "getStackTraceString",
476 "(Ljava/lang/Throwable;)Ljava/lang/String;");
477 if (checkAndClear() || !methodId) {
478 logError();
479 return {};
480 }
481
482 jvalue value;
483 value.l = static_cast<jobject>(exception);
484 const jobject messageObj = env->CallStaticObjectMethodA(logClazz, methodId, &value);
485 const jstring jmessage = static_cast<jstring>(messageObj);
486 if (checkAndClear())
487 return {};
488
489 char const *utfMessage = env->GetStringUTFChars(jmessage, 0);
490 const QString message = QString::fromUtf8(utfMessage);
491
492 env->ReleaseStringUTFChars(jmessage, utfMessage);
493
494 return message;
495 }
496} // end namespace
497
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
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
\inmodule QtCore
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,...)
static const char qJniThreadName[]
QScopedPointer< _jobject, QJniLocalRefDeleterPrivate > QJniScopedLocalRefPrivate
#define qWarning
Definition qlogging.h:162
return ret
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLuint id
[7]
GLuint GLsizei const GLchar * message
GLhandleARB obj
[2]
const char className[16]
[1]
Definition qwizard.cpp:100
QJSValueList args
static void cleanup(jobject obj)