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
qjnihelpers.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 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 "qjnihelpers_p.h"
5
6#include "qjnienvironment.h"
7#include "qjniobject.h"
8#include "qlist.h"
9#include "qmutex.h"
10#include "qsemaphore.h"
11#include "qreadwritelock.h"
12#include <QtCore/private/qcoreapplication_p.h>
13#include <QtCore/private/qlocking_p.h>
14
15#include <android/log.h>
16#include <deque>
17#include <memory>
18
20
21Q_DECLARE_JNI_CLASS(QtInputDelegate, "org/qtproject/qt/android/QtInputDelegate");
22Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent");
23Q_DECLARE_JNI_CLASS(KeyEvent, "android/view/KeyEvent");
24
26 // *Listener virtual function implementations.
27 // Defined out-of-line to pin the vtable/type_info.
33}
34
35static JavaVM *g_javaVM = nullptr;
36static jobject g_jActivity = nullptr;
37static jobject g_jService = nullptr;
38static jobject g_jClassLoader = nullptr;
39
42Q_GLOBAL_STATIC(QSemaphore, g_waitForServiceSetupSemaphore);
44
46
47static jboolean updateNativeActivity(JNIEnv *env, jclass = nullptr)
48{
49
50 jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative");
51 if (QJniEnvironment::checkAndClearExceptions(env))
52 return JNI_FALSE;
53
54 jmethodID activityMethodID =
55 env->GetStaticMethodID(jQtNative, "activity", "()Landroid/app/Activity;");
56 if (QJniEnvironment::checkAndClearExceptions(env))
57 return JNI_FALSE;
58
59 jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
60 if (QJniEnvironment::checkAndClearExceptions(env))
61 return JNI_FALSE;
62
63 QWriteLocker locker(g_updateMutex());
64
65 if (g_jActivity) {
66 env->DeleteGlobalRef(g_jActivity);
67 g_jActivity = nullptr;
68 }
69
70 if (activity) {
71 g_jActivity = env->NewGlobalRef(activity);
72 env->DeleteLocalRef(activity);
73 }
74
75 env->DeleteLocalRef(jQtNative);
76 if (QJniEnvironment::checkAndClearExceptions(env))
77 return JNI_FALSE;
78
79 return JNI_TRUE;
80}
81
82namespace {
83 class ActivityResultListeners
84 {
85 public:
87 QList<QtAndroidPrivate::ActivityResultListener *> listeners;
88 };
89}
90
91Q_GLOBAL_STATIC(ActivityResultListeners, g_activityResultListeners)
92
93void QtAndroidPrivate::registerActivityResultListener(ActivityResultListener *listener)
94{
95 QMutexLocker locker(&g_activityResultListeners()->mutex);
96 g_activityResultListeners()->listeners.append(listener);
97}
98
100{
101 QMutexLocker locker(&g_activityResultListeners()->mutex);
102 g_activityResultListeners()->listeners.removeAll(listener);
103}
104
105void QtAndroidPrivate::handleActivityResult(jint requestCode, jint resultCode, jobject data)
106{
107 QMutexLocker locker(&g_activityResultListeners()->mutex);
108 const QList<QtAndroidPrivate::ActivityResultListener *> &listeners = g_activityResultListeners()->listeners;
109 for (int i=0; i<listeners.size(); ++i) {
110 if (listeners.at(i)->handleActivityResult(requestCode, resultCode, data))
111 break;
112 }
113}
114
115namespace {
116 class NewIntentListeners
117 {
118 public:
120 QList<QtAndroidPrivate::NewIntentListener *> listeners;
121 };
122}
123
124Q_GLOBAL_STATIC(NewIntentListeners, g_newIntentListeners)
125
126void QtAndroidPrivate::registerNewIntentListener(NewIntentListener *listener)
127{
128 QMutexLocker locker(&g_newIntentListeners()->mutex);
129 g_newIntentListeners()->listeners.append(listener);
130}
131
133{
134 QMutexLocker locker(&g_newIntentListeners()->mutex);
135 g_newIntentListeners()->listeners.removeAll(listener);
136}
137
138void QtAndroidPrivate::handleNewIntent(JNIEnv *env, jobject intent)
139{
140 QMutexLocker locker(&g_newIntentListeners()->mutex);
141 const QList<QtAndroidPrivate::NewIntentListener *> &listeners = g_newIntentListeners()->listeners;
142 for (int i=0; i<listeners.size(); ++i) {
143 if (listeners.at(i)->handleNewIntent(env, intent))
144 break;
145 }
146}
147
149namespace {
150struct GenericMotionEventListeners {
152 QList<QtAndroidPrivate::GenericMotionEventListener *> listeners;
153};
154}
155Q_GLOBAL_STATIC(GenericMotionEventListeners, g_genericMotionEventListeners)
156
157static jboolean dispatchGenericMotionEvent(JNIEnv *, jclass, QtJniTypes::MotionEvent event)
158{
159 jboolean ret = JNI_FALSE;
160 QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
161 for (auto *listener : std::as_const(g_genericMotionEventListeners()->listeners))
162 ret |= listener->handleGenericMotionEvent(event.object());
163 return ret;
164}
166
168namespace {
169struct KeyEventListeners {
171 QList<QtAndroidPrivate::KeyEventListener *> listeners;
172};
173}
174Q_GLOBAL_STATIC(KeyEventListeners, g_keyEventListeners)
175
176static jboolean dispatchKeyEvent(JNIEnv *, jclass, QtJniTypes::KeyEvent event)
177{
178 jboolean ret = JNI_FALSE;
179 QMutexLocker locker(&g_keyEventListeners()->mutex);
180 for (auto *listener : std::as_const(g_keyEventListeners()->listeners))
181 ret |= listener->handleKeyEvent(event.object());
182 return ret;
183}
185
187{
188 QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
189 g_genericMotionEventListeners()->listeners.push_back(listener);
190}
191
193{
194 QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
195 g_genericMotionEventListeners()->listeners.removeOne(listener);
196}
197
199{
200 QMutexLocker locker(&g_keyEventListeners()->mutex);
201 g_keyEventListeners()->listeners.push_back(listener);
202}
203
205{
206 QMutexLocker locker(&g_keyEventListeners()->mutex);
207 g_keyEventListeners()->listeners.removeOne(listener);
208}
209
210namespace {
211 class ResumePauseListeners
212 {
213 public:
215 QList<QtAndroidPrivate::ResumePauseListener *> listeners;
216 };
217}
218
219Q_GLOBAL_STATIC(ResumePauseListeners, g_resumePauseListeners)
220
222{
223 QMutexLocker locker(&g_resumePauseListeners()->mutex);
224 g_resumePauseListeners()->listeners.append(listener);
225}
226
228{
229 QMutexLocker locker(&g_resumePauseListeners()->mutex);
230 g_resumePauseListeners()->listeners.removeAll(listener);
231}
232
234{
235 QMutexLocker locker(&g_resumePauseListeners()->mutex);
236 const QList<QtAndroidPrivate::ResumePauseListener *> &listeners = g_resumePauseListeners()->listeners;
237 for (int i=0; i<listeners.size(); ++i)
238 listeners.at(i)->handlePause();
239}
240
242{
243 QMutexLocker locker(&g_resumePauseListeners()->mutex);
244 const QList<QtAndroidPrivate::ResumePauseListener *> &listeners = g_resumePauseListeners()->listeners;
245 for (int i=0; i<listeners.size(); ++i)
246 listeners.at(i)->handleResume();
247}
248
249jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
250{
251 g_javaVM = vm;
252
253 jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative");
254
255 if (QJniEnvironment::checkAndClearExceptions(env))
256 return JNI_ERR;
257
258 jmethodID activityMethodID = env->GetStaticMethodID(jQtNative,
259 "activity",
260 "()Landroid/app/Activity;");
261
262 if (QJniEnvironment::checkAndClearExceptions(env))
263 return JNI_ERR;
264
265 jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
266
267 if (QJniEnvironment::checkAndClearExceptions(env))
268 return JNI_ERR;
269
270 jmethodID serviceMethodID = env->GetStaticMethodID(jQtNative,
271 "service",
272 "()Landroid/app/Service;");
273
274 if (QJniEnvironment::checkAndClearExceptions(env))
275 return JNI_ERR;
276
277 jobject service = env->CallStaticObjectMethod(jQtNative, serviceMethodID);
278
279 if (QJniEnvironment::checkAndClearExceptions(env))
280 return JNI_ERR;
281
282 jmethodID classLoaderMethodID = env->GetStaticMethodID(jQtNative,
283 "classLoader",
284 "()Ljava/lang/ClassLoader;");
285
286 if (QJniEnvironment::checkAndClearExceptions(env))
287 return JNI_ERR;
288
289 jobject classLoader = env->CallStaticObjectMethod(jQtNative, classLoaderMethodID);
290 if (QJniEnvironment::checkAndClearExceptions(env))
291 return JNI_ERR;
292
293 g_jClassLoader = env->NewGlobalRef(classLoader);
294 env->DeleteLocalRef(classLoader);
295 if (activity) {
296 g_jActivity = env->NewGlobalRef(activity);
297 env->DeleteLocalRef(activity);
298 }
299 if (service) {
300 g_jService = env->NewGlobalRef(service);
301 env->DeleteLocalRef(service);
302 }
303
304 static const JNINativeMethod methods[] = {
305 {"updateNativeActivity", "()Z", reinterpret_cast<void *>(updateNativeActivity) },
306 };
307
308 const bool regOk = (env->RegisterNatives(jQtNative, methods, sizeof(methods) / sizeof(methods[0])) == JNI_OK);
309 env->DeleteLocalRef(jQtNative);
310 if (!regOk && QJniEnvironment::checkAndClearExceptions(env))
311 return JNI_ERR;
312
313 QJniEnvironment qJniEnv;
314 using namespace QtJniTypes;
315 if (!QtInputDelegate::registerNativeMethods(
316 { Q_JNI_NATIVE_METHOD(dispatchGenericMotionEvent),
317 Q_JNI_NATIVE_METHOD(dispatchKeyEvent) })) {
318 qCritical() << "Failed to register natives methods for"
319 << Traits<QtInputDelegate>::className();
320 return JNI_ERR;
321 }
322
323 if (!registerPermissionNatives(qJniEnv))
324 return JNI_ERR;
325
326 if (!registerNativeInterfaceNatives(qJniEnv))
327 return JNI_ERR;
328
329 if (!registerExtrasNatives(qJniEnv))
330 return JNI_ERR;
331
332 return JNI_OK;
333}
334
335Q_CORE_EXPORT jobject qt_androidActivity()
336{
337 QReadLocker locker(g_updateMutex());
338 return g_jActivity;
339}
340
341
342QtJniTypes::Activity QtAndroidPrivate::activity()
343{
344 QReadLocker locker(g_updateMutex());
345 return g_jActivity;
346}
347
348Q_CORE_EXPORT jobject qt_androidService()
349{
350 return g_jService;
351}
352
353QtJniTypes::Service QtAndroidPrivate::service()
354{
355 return g_jService;
356}
357
358QtJniTypes::Context QtAndroidPrivate::context()
359{
360 QReadLocker locker(g_updateMutex());
361 if (g_jActivity)
362 return g_jActivity;
363 if (g_jService)
364 return g_jService;
365
366 return nullptr;
367}
368
370{
371 return g_javaVM;
372}
373
375{
376 return g_jClassLoader;
377}
378
380{
381 static jint sdkVersion = 0;
382 if (!sdkVersion)
383 sdkVersion = QJniObject::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT");
384 return sdkVersion;
385}
386
388{
389 g_waitForServiceSetupSemaphore->acquire();
390}
391
397
399{
400 const auto lock = qt_scoped_lock(g_onBindListenerMutex);
401 g_onBindListener = listener;
402 if (!g_serviceSetupLockers.deref())
403 g_waitForServiceSetupSemaphore->release();
404}
405
407{
408 const auto lock = qt_scoped_lock(g_onBindListenerMutex);
410 return g_onBindListener->onBind(intent);
411 return nullptr;
412}
413
415
417{
418 return g_androidDeadlockProtector.testAndSetAcquire(0, 1);
419}
420
425
427
428JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
429{
430 Q_UNUSED(reserved);
431
432 static const char logTag[] = "QtCore";
433 static bool initialized = false;
434 if (initialized)
435 return JNI_VERSION_1_6;
436 initialized = true;
437
438 typedef union {
439 JNIEnv *nenv;
440 void *venv;
441 } _JNIEnv;
442
443 __android_log_print(ANDROID_LOG_INFO, logTag, "Start");
444
445 _JNIEnv uenv;
446 uenv.venv = nullptr;
447
448 if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
449 __android_log_print(ANDROID_LOG_FATAL, logTag, "GetEnv failed");
450 return JNI_ERR;
451 }
452
453 JNIEnv *env = uenv.nenv;
454 const jint ret = QT_PREPEND_NAMESPACE(QtAndroidPrivate::initJNI(vm, env));
455 if (ret != 0) {
456 __android_log_print(ANDROID_LOG_FATAL, logTag, "initJNI failed");
457 return ret;
458 }
459
460 return JNI_VERSION_1_6;
461}
static JNINativeMethod methods[]
\inmodule QtCore
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qsemaphore.h:18
\inmodule QtCore
static const char logTag[]
Combined button and popup list for selecting options.
\preliminary \inmodule QtCorePrivate
Q_CORE_EXPORT void unregisterNewIntentListener(NewIntentListener *listener)
Q_CORE_EXPORT int acuqireServiceSetup(int flags)
Q_CORE_EXPORT jobject callOnBindListener(jobject intent)
Q_CORE_EXPORT jint initJNI(JavaVM *vm, JNIEnv *env)
Q_CORE_EXPORT jint androidSdkVersion()
bool registerNativeInterfaceNatives(QJniEnvironment &env)
Posts the function runnable to the Android thread.
Q_CORE_EXPORT void unregisterResumePauseListener(ResumePauseListener *listener)
Q_CORE_EXPORT void unregisterGenericMotionEventListener(GenericMotionEventListener *listener)
Q_CORE_EXPORT bool acquireAndroidDeadlockProtector()
Q_CORE_EXPORT void unregisterKeyEventListener(KeyEventListener *listener)
Q_CORE_EXPORT QtJniTypes::Context context()
bool registerExtrasNatives(QJniEnvironment &env)
bool registerPermissionNatives(QJniEnvironment &env)
Q_CORE_EXPORT void handleResume()
Q_CORE_EXPORT void registerResumePauseListener(ResumePauseListener *listener)
Q_CORE_EXPORT QtJniTypes::Activity activity()
Q_CORE_EXPORT void releaseAndroidDeadlockProtector()
Q_CORE_EXPORT void handleNewIntent(JNIEnv *env, jobject intent)
Q_CORE_EXPORT void setOnBindListener(OnBindListener *listener)
Q_CORE_EXPORT void registerKeyEventListener(KeyEventListener *listener)
Q_CORE_EXPORT void handlePause()
Q_CORE_EXPORT void unregisterActivityResultListener(ActivityResultListener *listener)
Q_CORE_EXPORT void registerGenericMotionEventListener(GenericMotionEventListener *listener)
Q_CORE_EXPORT QtJniTypes::Service service()
Q_CORE_EXPORT JavaVM * javaVM()
Q_CORE_EXPORT void handleActivityResult(jint requestCode, jint resultCode, jobject data)
Q_CORE_EXPORT void waitForServiceSetup()
jobject classLoader()
#define Q_BASIC_ATOMIC_INITIALIZER(a)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
static jobject g_jService
Q_DECLARE_JNI_NATIVE_METHOD(dispatchGenericMotionEvent)
static Q_CONSTINIT QtAndroidPrivate::OnBindListener * g_onBindListener
static jboolean dispatchGenericMotionEvent(JNIEnv *, jclass, QtJniTypes::MotionEvent event)
Q_CORE_EXPORT jobject qt_androidActivity()
static jobject g_jClassLoader
static jobject g_jActivity
QT_END_NAMESPACE JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
static Q_CONSTINIT QBasicMutex g_onBindListenerMutex
static Q_CONSTINIT QBasicAtomicInt g_androidDeadlockProtector
static JavaVM * g_javaVM
static jboolean updateNativeActivity(JNIEnv *env, jclass=nullptr)
static jboolean dispatchKeyEvent(JNIEnv *, jclass, QtJniTypes::KeyEvent event)
QT_BEGIN_NAMESPACE Q_DECLARE_JNI_CLASS(QtInputDelegate, "org/qtproject/qt/android/QtInputDelegate")
Q_CORE_EXPORT jobject qt_androidService()
static Q_CONSTINIT QBasicAtomicInt g_serviceSetupLockers
#define qCritical
Definition qlogging.h:168
return ret
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLbitfield flags
struct _cl_event * event
#define Q_UNUSED(x)
QMutex mutex
[2]
QReadWriteLock lock
[0]