Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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
5
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#if QT_CONFIG(regularexpression)
16#include <QtCore/qregularexpression.h>
17#endif
18
19#include <android/log.h>
20#include <deque>
21#include <memory>
22
24
25Q_DECLARE_JNI_CLASS(QtLoader, "org/qtproject/qt/android/QtLoader")
26Q_DECLARE_JNI_CLASS(QtInputDelegate, "org/qtproject/qt/android/QtInputDelegate");
27Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent");
28Q_DECLARE_JNI_CLASS(KeyEvent, "android/view/KeyEvent");
29
30using namespace Qt::StringLiterals;
31
32namespace QtAndroidPrivate {
33 // *Listener virtual function implementations.
34 // Defined out-of-line to pin the vtable/type_info.
40}
41
42static JavaVM *g_javaVM = nullptr;
43static jobject g_jActivity = nullptr;
44static jobject g_jService = nullptr;
45static jobject g_jClassLoader = nullptr;
46
47Q_CONSTINIT static QtAndroidPrivate::OnBindListener *g_onBindListener;
48Q_CONSTINIT static QBasicMutex g_onBindListenerMutex;
49Q_GLOBAL_STATIC(QSemaphore, g_waitForServiceSetupSemaphore);
50Q_CONSTINIT static QBasicAtomicInt g_serviceSetupLockers = Q_BASIC_ATOMIC_INITIALIZER(0);
51
53
54static jboolean updateNativeActivity(JNIEnv *env, jclass = nullptr)
55{
56
57 jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative");
58 if (QJniEnvironment::checkAndClearExceptions(env))
59 return JNI_FALSE;
60
61 jmethodID activityMethodID =
62 env->GetStaticMethodID(jQtNative, "activity", "()Landroid/app/Activity;");
63 if (QJniEnvironment::checkAndClearExceptions(env))
64 return JNI_FALSE;
65
66 jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
67 if (QJniEnvironment::checkAndClearExceptions(env))
68 return JNI_FALSE;
69
70 QWriteLocker locker(g_updateMutex());
71
72 if (g_jActivity) {
73 env->DeleteGlobalRef(g_jActivity);
74 g_jActivity = nullptr;
75 }
76
77 if (activity) {
78 g_jActivity = env->NewGlobalRef(activity);
79 env->DeleteLocalRef(activity);
80 }
81
82 env->DeleteLocalRef(jQtNative);
83 if (QJniEnvironment::checkAndClearExceptions(env))
84 return JNI_FALSE;
85
86 return JNI_TRUE;
87}
88
89namespace {
90 class ActivityResultListeners
91 {
92 public:
93 QMutex mutex;
94 QList<QtAndroidPrivate::ActivityResultListener *> listeners;
95 };
96}
97
98Q_GLOBAL_STATIC(ActivityResultListeners, g_activityResultListeners)
99
100void QtAndroidPrivate::registerActivityResultListener(ActivityResultListener *listener)
101{
102 QMutexLocker locker(&g_activityResultListeners()->mutex);
103 g_activityResultListeners()->listeners.append(listener);
104}
105
106void QtAndroidPrivate::unregisterActivityResultListener(ActivityResultListener *listener)
107{
108 QMutexLocker locker(&g_activityResultListeners()->mutex);
109 g_activityResultListeners()->listeners.removeAll(listener);
110}
111
112void QtAndroidPrivate::handleActivityResult(jint requestCode, jint resultCode, jobject data)
113{
114 QMutexLocker locker(&g_activityResultListeners()->mutex);
115 const QList<QtAndroidPrivate::ActivityResultListener *> &listeners = g_activityResultListeners()->listeners;
116 for (int i=0; i<listeners.size(); ++i) {
117 if (listeners.at(i)->handleActivityResult(requestCode, resultCode, data))
118 break;
119 }
120}
121
122namespace {
123 class NewIntentListeners
124 {
125 public:
126 QMutex mutex;
127 QList<QtAndroidPrivate::NewIntentListener *> listeners;
128 };
129}
130
131Q_GLOBAL_STATIC(NewIntentListeners, g_newIntentListeners)
132
133void QtAndroidPrivate::registerNewIntentListener(NewIntentListener *listener)
134{
135 QMutexLocker locker(&g_newIntentListeners()->mutex);
136 g_newIntentListeners()->listeners.append(listener);
137}
138
139void QtAndroidPrivate::unregisterNewIntentListener(NewIntentListener *listener)
140{
141 QMutexLocker locker(&g_newIntentListeners()->mutex);
142 g_newIntentListeners()->listeners.removeAll(listener);
143}
144
145void QtAndroidPrivate::handleNewIntent(JNIEnv *env, jobject intent)
146{
147 QMutexLocker locker(&g_newIntentListeners()->mutex);
148 const QList<QtAndroidPrivate::NewIntentListener *> &listeners = g_newIntentListeners()->listeners;
149 for (int i=0; i<listeners.size(); ++i) {
150 if (listeners.at(i)->handleNewIntent(env, intent))
151 break;
152 }
153}
154
155QtAndroidPrivate::GenericMotionEventListener::~GenericMotionEventListener() {}
156namespace {
157struct GenericMotionEventListeners {
158 QMutex mutex;
159 QList<QtAndroidPrivate::GenericMotionEventListener *> listeners;
160};
161}
162Q_GLOBAL_STATIC(GenericMotionEventListeners, g_genericMotionEventListeners)
163
164static jboolean dispatchGenericMotionEvent(JNIEnv *, jclass, QtJniTypes::MotionEvent event)
165{
166 jboolean ret = JNI_FALSE;
167 QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
168 for (auto *listener : std::as_const(g_genericMotionEventListeners()->listeners))
169 ret |= listener->handleGenericMotionEvent(event.object());
170 return ret;
171}
173
174QtAndroidPrivate::KeyEventListener::~KeyEventListener() {}
175namespace {
176struct KeyEventListeners {
177 QMutex mutex;
178 QList<QtAndroidPrivate::KeyEventListener *> listeners;
179};
180}
181Q_GLOBAL_STATIC(KeyEventListeners, g_keyEventListeners)
182
183static jboolean dispatchKeyEvent(JNIEnv *, jclass, QtJniTypes::KeyEvent event)
184{
185 jboolean ret = JNI_FALSE;
186 QMutexLocker locker(&g_keyEventListeners()->mutex);
187 for (auto *listener : std::as_const(g_keyEventListeners()->listeners))
188 ret |= listener->handleKeyEvent(event.object());
189 return ret;
190}
192
193void QtAndroidPrivate::registerGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener)
194{
195 QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
196 g_genericMotionEventListeners()->listeners.push_back(listener);
197}
198
199void QtAndroidPrivate::unregisterGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener)
200{
201 QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
202 g_genericMotionEventListeners()->listeners.removeOne(listener);
203}
204
205void QtAndroidPrivate::registerKeyEventListener(QtAndroidPrivate::KeyEventListener *listener)
206{
207 QMutexLocker locker(&g_keyEventListeners()->mutex);
208 g_keyEventListeners()->listeners.push_back(listener);
209}
210
211void QtAndroidPrivate::unregisterKeyEventListener(QtAndroidPrivate::KeyEventListener *listener)
212{
213 QMutexLocker locker(&g_keyEventListeners()->mutex);
214 g_keyEventListeners()->listeners.removeOne(listener);
215}
216
217namespace {
218 class ResumePauseListeners
219 {
220 public:
221 QMutex mutex;
222 QList<QtAndroidPrivate::ResumePauseListener *> listeners;
223 };
224}
225
226Q_GLOBAL_STATIC(ResumePauseListeners, g_resumePauseListeners)
227
228void QtAndroidPrivate::registerResumePauseListener(ResumePauseListener *listener)
229{
230 QMutexLocker locker(&g_resumePauseListeners()->mutex);
231 g_resumePauseListeners()->listeners.append(listener);
232}
233
234void QtAndroidPrivate::unregisterResumePauseListener(ResumePauseListener *listener)
235{
236 QMutexLocker locker(&g_resumePauseListeners()->mutex);
237 g_resumePauseListeners()->listeners.removeAll(listener);
238}
239
241{
242 QMutexLocker locker(&g_resumePauseListeners()->mutex);
243 const QList<QtAndroidPrivate::ResumePauseListener *> &listeners = g_resumePauseListeners()->listeners;
244 for (int i=0; i<listeners.size(); ++i)
245 listeners.at(i)->handlePause();
246}
247
249{
250 QMutexLocker locker(&g_resumePauseListeners()->mutex);
251 const QList<QtAndroidPrivate::ResumePauseListener *> &listeners = g_resumePauseListeners()->listeners;
252 for (int i=0; i<listeners.size(); ++i)
253 listeners.at(i)->handleResume();
254}
255
257{
258 const static bool isUncompressed = QtJniTypes::QtLoader::callStaticMethod<bool>(
259 "isUncompressedNativeLibs");
260 return isUncompressed;
261}
262
263QString QtAndroidPrivate::resolveApkPath(const QString &fileName)
264{
265#if QT_CONFIG(regularexpression)
266 const static QRegularExpression inApkRegex("(.+\\.apk)!\\/.+"_L1);
267 auto match = inApkRegex.matchView(fileName);
268 if (match.hasMatch())
269 return match.captured(1);
270#else
271 if (int index = fileName.lastIndexOf(u".apk!/"); index > 0)
272 return fileName.mid(0, index + 4);
273#endif
274
275 return {};
276}
277
278jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
279{
280 g_javaVM = vm;
281
282 jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative");
283
284 if (QJniEnvironment::checkAndClearExceptions(env))
285 return JNI_ERR;
286
287 jmethodID activityMethodID = env->GetStaticMethodID(jQtNative,
288 "activity",
289 "()Landroid/app/Activity;");
290
291 if (QJniEnvironment::checkAndClearExceptions(env))
292 return JNI_ERR;
293
294 jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
295
296 if (QJniEnvironment::checkAndClearExceptions(env))
297 return JNI_ERR;
298
299 jmethodID serviceMethodID = env->GetStaticMethodID(jQtNative,
300 "service",
301 "()Landroid/app/Service;");
302
303 if (QJniEnvironment::checkAndClearExceptions(env))
304 return JNI_ERR;
305
306 jobject service = env->CallStaticObjectMethod(jQtNative, serviceMethodID);
307
308 if (QJniEnvironment::checkAndClearExceptions(env))
309 return JNI_ERR;
310
311 jmethodID classLoaderMethodID = env->GetStaticMethodID(jQtNative,
312 "classLoader",
313 "()Ljava/lang/ClassLoader;");
314
315 if (QJniEnvironment::checkAndClearExceptions(env))
316 return JNI_ERR;
317
318 jobject classLoader = env->CallStaticObjectMethod(jQtNative, classLoaderMethodID);
319 if (QJniEnvironment::checkAndClearExceptions(env))
320 return JNI_ERR;
321
322 g_jClassLoader = env->NewGlobalRef(classLoader);
323 env->DeleteLocalRef(classLoader);
324 if (activity) {
325 g_jActivity = env->NewGlobalRef(activity);
326 env->DeleteLocalRef(activity);
327 }
328 if (service) {
329 g_jService = env->NewGlobalRef(service);
330 env->DeleteLocalRef(service);
331 }
332
333 static const JNINativeMethod methods[] = {
334 {"updateNativeActivity", "()Z", reinterpret_cast<void *>(updateNativeActivity) },
335 };
336
337 const bool regOk = (env->RegisterNatives(jQtNative, methods, sizeof(methods) / sizeof(methods[0])) == JNI_OK);
338 env->DeleteLocalRef(jQtNative);
339 if (!regOk && QJniEnvironment::checkAndClearExceptions(env))
340 return JNI_ERR;
341
342 QJniEnvironment qJniEnv;
343 using namespace QtJniTypes;
344 if (!QtInputDelegate::registerNativeMethods(
345 { Q_JNI_NATIVE_METHOD(dispatchGenericMotionEvent),
346 Q_JNI_NATIVE_METHOD(dispatchKeyEvent) })) {
347 qCritical() << "Failed to register natives methods for"
348 << Traits<QtInputDelegate>::className();
349 return JNI_ERR;
350 }
351
352#if QT_CONFIG(permissions)
353 if (!registerPermissionNatives(qJniEnv))
354 return JNI_ERR;
355#endif
356
357 if (!registerNativeInterfaceNatives(qJniEnv))
358 return JNI_ERR;
359
360 if (!registerExtrasNatives(qJniEnv))
361 return JNI_ERR;
362
363 return JNI_OK;
364}
365
366Q_CORE_EXPORT jobject qt_androidActivity()
367{
368 QReadLocker locker(g_updateMutex());
369 return g_jActivity;
370}
371
372
373QtJniTypes::Activity QtAndroidPrivate::activity()
374{
375 QReadLocker locker(g_updateMutex());
376 return g_jActivity;
377}
378
379Q_CORE_EXPORT jobject qt_androidService()
380{
381 return g_jService;
382}
383
384QtJniTypes::Service QtAndroidPrivate::service()
385{
386 return g_jService;
387}
388
389QtJniTypes::Context QtAndroidPrivate::context()
390{
391 QReadLocker locker(g_updateMutex());
392 if (g_jActivity)
393 return g_jActivity;
394 if (g_jService)
395 return g_jService;
396
397 return nullptr;
398}
399
400JavaVM *QtAndroidPrivate::javaVM()
401{
402 return g_javaVM;
403}
404
406{
407 return g_jClassLoader;
408}
409
410jint QtAndroidPrivate::androidSdkVersion()
411{
412 static jint sdkVersion = 0;
413 if (!sdkVersion)
414 sdkVersion = QJniObject::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT");
415 return sdkVersion;
416}
417
419{
420 g_waitForServiceSetupSemaphore->acquire();
421}
422
424{
425 g_serviceSetupLockers.ref();
426 return flags;
427}
428
429void QtAndroidPrivate::setOnBindListener(QtAndroidPrivate::OnBindListener *listener)
430{
431 const auto lock = qt_scoped_lock(g_onBindListenerMutex);
432 g_onBindListener = listener;
433 if (!g_serviceSetupLockers.deref())
434 g_waitForServiceSetupSemaphore->release();
435}
436
437jobject QtAndroidPrivate::callOnBindListener(jobject intent)
438{
439 const auto lock = qt_scoped_lock(g_onBindListenerMutex);
440 if (g_onBindListener)
441 return g_onBindListener->onBind(intent);
442 return nullptr;
443}
444
445Q_CONSTINIT static QBasicAtomicInt g_androidDeadlockProtector = Q_BASIC_ATOMIC_INITIALIZER(0);
446
448{
449 return g_androidDeadlockProtector.testAndSetAcquire(0, 1);
450}
451
453{
454 g_androidDeadlockProtector.storeRelease(0);
455}
456
457QtAndroidPrivate::AndroidDeadlockProtector::AndroidDeadlockProtector(const QString &lockedBy)
458 : m_lockedBy(lockedBy)
459{ }
460
461QtAndroidPrivate::AndroidDeadlockProtector::~AndroidDeadlockProtector() {
462 if (m_acquired) {
463 QtAndroidPrivate::releaseAndroidDeadlockProtector();
464 s_lockers.removeOne(m_lockedBy);
465 }
466}
467
468bool QtAndroidPrivate::AndroidDeadlockProtector::acquire() {
469 m_acquired = QtAndroidPrivate::acquireAndroidDeadlockProtector();
470 if (m_acquired) {
471 s_lockers.append(m_lockedBy);
472 } else {
473 qWarning("Failed to acquire deadlock protector for '%s' while already locked by '%s'.",
474 qPrintable(m_lockedBy), qPrintable(s_lockers.join(u',')));
475 }
476 return m_acquired;
477}
478
479QT_END_NAMESPACE
480
481JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
482{
483 Q_UNUSED(reserved);
484
485 static const char logTag[] = "QtCore";
486 static bool initialized = false;
487 if (initialized)
488 return JNI_VERSION_1_6;
489 initialized = true;
490
491 typedef union {
492 JNIEnv *nenv;
493 void *venv;
494 } _JNIEnv;
495
496 __android_log_print(ANDROID_LOG_INFO, logTag, "Start");
497
498 _JNIEnv uenv;
499 uenv.venv = nullptr;
500
501 if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
502 __android_log_print(ANDROID_LOG_FATAL, logTag, "GetEnv failed");
503 return JNI_ERR;
504 }
505
506 JNIEnv *env = uenv.nenv;
507 const jint ret = QT_PREPEND_NAMESPACE(QtAndroidPrivate::initJNI(vm, env));
508 if (ret != 0) {
509 __android_log_print(ANDROID_LOG_FATAL, logTag, "initJNI failed");
510 return ret;
511 }
512
513 return JNI_VERSION_1_6;
514}
\inmodule QtCore
Definition qmutex.h:346
\preliminary \inmodule QtCorePrivate
Q_CORE_EXPORT void unregisterNewIntentListener(NewIntentListener *listener)
Q_CORE_EXPORT int acuqireServiceSetup(int flags)
Q_CORE_EXPORT void registerNewIntentListener(NewIntentListener *listener)
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 void handleResume()
Q_CORE_EXPORT void registerResumePauseListener(ResumePauseListener *listener)
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 bool isUncompressedNativeLibs()
Q_CORE_EXPORT void unregisterActivityResultListener(ActivityResultListener *listener)
Q_CORE_EXPORT void registerGenericMotionEventListener(GenericMotionEventListener *listener)
Q_CORE_EXPORT void handleActivityResult(jint requestCode, jint resultCode, jobject data)
Q_CORE_EXPORT void waitForServiceSetup()
jobject classLoader()
Q_CORE_EXPORT void registerActivityResultListener(ActivityResultListener *listener)
Q_GLOBAL_STATIC(DefaultRoleNames, qDefaultRoleNames, { { Qt::DisplayRole, "display" }, { Qt::DecorationRole, "decoration" }, { Qt::EditRole, "edit" }, { Qt::ToolTipRole, "toolTip" }, { Qt::StatusTipRole, "statusTip" }, { Qt::WhatsThisRole, "whatsThis" }, }) const QHash< int
Q_DECLARE_JNI_NATIVE_METHOD(dispatchKeyEvent)
Q_DECLARE_JNI_NATIVE_METHOD(dispatchGenericMotionEvent)
static jobject g_jClassLoader
static jboolean dispatchGenericMotionEvent(JNIEnv *, jclass, QtJniTypes::MotionEvent event)
static JavaVM * g_javaVM
Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex)
static jobject g_jActivity
Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")
static jboolean updateNativeActivity(JNIEnv *env, jclass=nullptr)
static jboolean dispatchKeyEvent(JNIEnv *, jclass, QtJniTypes::KeyEvent event)
static jobject g_jService