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