Qt 6.x
The Qt SDK
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
22 // *Listener virtual function implementations.
23 // Defined out-of-line to pin the vtable/type_info.
31}
32
33static JavaVM *g_javaVM = nullptr;
34static jobject g_jActivity = nullptr;
35static jobject g_jService = nullptr;
36static jobject g_jClassLoader = nullptr;
37
40Q_GLOBAL_STATIC(QSemaphore, g_waitForServiceSetupSemaphore);
42
44
45namespace {
46 struct GenericMotionEventListeners {
49 };
50}
51Q_GLOBAL_STATIC(GenericMotionEventListeners, g_genericMotionEventListeners)
52
53static jboolean dispatchGenericMotionEvent(JNIEnv *, jclass, jobject event)
54{
55 jboolean ret = JNI_FALSE;
56 QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
57 for (auto *listener : std::as_const(g_genericMotionEventListeners()->listeners))
58 ret |= listener->handleGenericMotionEvent(event);
59 return ret;
60}
61
62namespace {
63 struct KeyEventListeners {
66 };
67}
68Q_GLOBAL_STATIC(KeyEventListeners, g_keyEventListeners)
69
70static jboolean dispatchKeyEvent(JNIEnv *, jclass, jobject event)
71{
72 jboolean ret = JNI_FALSE;
73 QMutexLocker locker(&g_keyEventListeners()->mutex);
74 for (auto *listener : std::as_const(g_keyEventListeners()->listeners))
75 ret |= listener->handleKeyEvent(event);
76 return ret;
77}
78
79static jboolean updateNativeActivity(JNIEnv *env, jclass = nullptr)
80{
81
82 jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative");
83 if (QJniEnvironment::checkAndClearExceptions(env))
84 return JNI_FALSE;
85
86 jmethodID activityMethodID =
87 env->GetStaticMethodID(jQtNative, "activity", "()Landroid/app/Activity;");
88 if (QJniEnvironment::checkAndClearExceptions(env))
89 return JNI_FALSE;
90
91 jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
92 if (QJniEnvironment::checkAndClearExceptions(env))
93 return JNI_FALSE;
94
95 QWriteLocker locker(g_updateMutex());
96
97 if (g_jActivity) {
98 env->DeleteGlobalRef(g_jActivity);
99 g_jActivity = nullptr;
100 }
101
102 if (activity) {
103 g_jActivity = env->NewGlobalRef(activity);
104 env->DeleteLocalRef(activity);
105 }
106
107 env->DeleteLocalRef(jQtNative);
108 if (QJniEnvironment::checkAndClearExceptions(env))
109 return JNI_FALSE;
110
111 return JNI_TRUE;
112}
113
114namespace {
115 class ActivityResultListeners
116 {
117 public:
120 };
121}
122
123Q_GLOBAL_STATIC(ActivityResultListeners, g_activityResultListeners)
124
125void QtAndroidPrivate::registerActivityResultListener(ActivityResultListener *listener)
126{
127 QMutexLocker locker(&g_activityResultListeners()->mutex);
128 g_activityResultListeners()->listeners.append(listener);
129}
130
132{
133 QMutexLocker locker(&g_activityResultListeners()->mutex);
134 g_activityResultListeners()->listeners.removeAll(listener);
135}
136
137void QtAndroidPrivate::handleActivityResult(jint requestCode, jint resultCode, jobject data)
138{
139 QMutexLocker locker(&g_activityResultListeners()->mutex);
140 const QList<QtAndroidPrivate::ActivityResultListener *> &listeners = g_activityResultListeners()->listeners;
141 for (int i=0; i<listeners.size(); ++i) {
142 if (listeners.at(i)->handleActivityResult(requestCode, resultCode, data))
143 break;
144 }
145}
146
147namespace {
148 class NewIntentListeners
149 {
150 public:
153 };
154}
155
156Q_GLOBAL_STATIC(NewIntentListeners, g_newIntentListeners)
157
158void QtAndroidPrivate::registerNewIntentListener(NewIntentListener *listener)
159{
160 QMutexLocker locker(&g_newIntentListeners()->mutex);
161 g_newIntentListeners()->listeners.append(listener);
162}
163
165{
166 QMutexLocker locker(&g_newIntentListeners()->mutex);
167 g_newIntentListeners()->listeners.removeAll(listener);
168}
169
170void QtAndroidPrivate::handleNewIntent(JNIEnv *env, jobject intent)
171{
172 QMutexLocker locker(&g_newIntentListeners()->mutex);
173 const QList<QtAndroidPrivate::NewIntentListener *> &listeners = g_newIntentListeners()->listeners;
174 for (int i=0; i<listeners.size(); ++i) {
175 if (listeners.at(i)->handleNewIntent(env, intent))
176 break;
177 }
178}
179
180namespace {
181 class ResumePauseListeners
182 {
183 public:
186 };
187}
188
189Q_GLOBAL_STATIC(ResumePauseListeners, g_resumePauseListeners)
190
191void QtAndroidPrivate::registerResumePauseListener(ResumePauseListener *listener)
192{
193 QMutexLocker locker(&g_resumePauseListeners()->mutex);
194 g_resumePauseListeners()->listeners.append(listener);
195}
196
198{
199 QMutexLocker locker(&g_resumePauseListeners()->mutex);
200 g_resumePauseListeners()->listeners.removeAll(listener);
201}
202
204{
205 QMutexLocker locker(&g_resumePauseListeners()->mutex);
206 const QList<QtAndroidPrivate::ResumePauseListener *> &listeners = g_resumePauseListeners()->listeners;
207 for (int i=0; i<listeners.size(); ++i)
208 listeners.at(i)->handlePause();
209}
210
212{
213 QMutexLocker locker(&g_resumePauseListeners()->mutex);
214 const QList<QtAndroidPrivate::ResumePauseListener *> &listeners = g_resumePauseListeners()->listeners;
215 for (int i=0; i<listeners.size(); ++i)
216 listeners.at(i)->handleResume();
217}
218
219jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
220{
221 g_javaVM = vm;
222
223 jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative");
224
225 if (QJniEnvironment::checkAndClearExceptions(env))
226 return JNI_ERR;
227
228 jmethodID activityMethodID = env->GetStaticMethodID(jQtNative,
229 "activity",
230 "()Landroid/app/Activity;");
231
232 if (QJniEnvironment::checkAndClearExceptions(env))
233 return JNI_ERR;
234
235 jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
236
237 if (QJniEnvironment::checkAndClearExceptions(env))
238 return JNI_ERR;
239
240 jmethodID serviceMethodID = env->GetStaticMethodID(jQtNative,
241 "service",
242 "()Landroid/app/Service;");
243
244 if (QJniEnvironment::checkAndClearExceptions(env))
245 return JNI_ERR;
246
247 jobject service = env->CallStaticObjectMethod(jQtNative, serviceMethodID);
248
249 if (QJniEnvironment::checkAndClearExceptions(env))
250 return JNI_ERR;
251
252 jmethodID classLoaderMethodID = env->GetStaticMethodID(jQtNative,
253 "classLoader",
254 "()Ljava/lang/ClassLoader;");
255
256 if (QJniEnvironment::checkAndClearExceptions(env))
257 return JNI_ERR;
258
259 jobject classLoader = env->CallStaticObjectMethod(jQtNative, classLoaderMethodID);
260 if (QJniEnvironment::checkAndClearExceptions(env))
261 return JNI_ERR;
262
263 g_jClassLoader = env->NewGlobalRef(classLoader);
264 env->DeleteLocalRef(classLoader);
265 if (activity) {
266 g_jActivity = env->NewGlobalRef(activity);
267 env->DeleteLocalRef(activity);
268 }
269 if (service) {
270 g_jService = env->NewGlobalRef(service);
271 env->DeleteLocalRef(service);
272 }
273
274 static const JNINativeMethod methods[] = {
275 {"dispatchGenericMotionEvent", "(Landroid/view/MotionEvent;)Z", reinterpret_cast<void *>(dispatchGenericMotionEvent)},
276 {"dispatchKeyEvent", "(Landroid/view/KeyEvent;)Z", reinterpret_cast<void *>(dispatchKeyEvent)},
277 {"updateNativeActivity", "()Z", reinterpret_cast<void *>(updateNativeActivity) },
278 };
279
280 const bool regOk = (env->RegisterNatives(jQtNative, methods, sizeof(methods) / sizeof(methods[0])) == JNI_OK);
281 env->DeleteLocalRef(jQtNative);
282 if (!regOk && QJniEnvironment::checkAndClearExceptions(env))
283 return JNI_ERR;
284
286 return JNI_ERR;
287
289 return JNI_ERR;
290
291 return JNI_OK;
292}
293
294QtJniTypes::Activity QtAndroidPrivate::activity()
295{
296 QReadLocker locker(g_updateMutex());
297 return g_jActivity;
298}
299
300QtJniTypes::Service QtAndroidPrivate::service()
301{
302 return g_jService;
303}
304
305QtJniTypes::Context QtAndroidPrivate::context()
306{
307 QReadLocker locker(g_updateMutex());
308 if (g_jActivity)
309 return g_jActivity;
310 if (g_jService)
311 return g_jService;
312
313 return nullptr;
314}
315
317{
318 return g_javaVM;
319}
320
322{
323 return g_jClassLoader;
324}
325
327{
328 static jint sdkVersion = 0;
329 if (!sdkVersion)
330 sdkVersion = QJniObject::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT");
331 return sdkVersion;
332}
333
335{
336 QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
337 g_genericMotionEventListeners()->listeners.push_back(listener);
338}
339
341{
342 QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
343 g_genericMotionEventListeners()->listeners.removeOne(listener);
344}
345
347{
348 QMutexLocker locker(&g_keyEventListeners()->mutex);
349 g_keyEventListeners()->listeners.push_back(listener);
350}
351
353{
354 QMutexLocker locker(&g_keyEventListeners()->mutex);
355 g_keyEventListeners()->listeners.removeOne(listener);
356}
357
359{
360 g_waitForServiceSetupSemaphore->acquire();
361}
362
364{
366 return flags;
367}
368
370{
371 const auto lock = qt_scoped_lock(g_onBindListenerMutex);
372 g_onBindListener = listener;
374 g_waitForServiceSetupSemaphore->release();
375}
376
378{
379 const auto lock = qt_scoped_lock(g_onBindListenerMutex);
381 return g_onBindListener->onBind(intent);
382 return nullptr;
383}
384
386
388{
390}
391
393{
395}
396
398
399JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
400{
401 Q_UNUSED(reserved);
402
403 static const char logTag[] = "QtCore";
404 static bool initialized = false;
405 if (initialized)
406 return JNI_VERSION_1_6;
407 initialized = true;
408
409 typedef union {
410 JNIEnv *nenv;
411 void *venv;
412 } _JNIEnv;
413
414 __android_log_print(ANDROID_LOG_INFO, logTag, "Start");
415
416 _JNIEnv uenv;
417 uenv.venv = nullptr;
418
419 if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
420 __android_log_print(ANDROID_LOG_FATAL, logTag, "GetEnv failed");
421 return JNI_ERR;
422 }
423
424 JNIEnv *env = uenv.nenv;
426 if (ret != 0) {
427 __android_log_print(ANDROID_LOG_FATAL, logTag, "initJNI failed");
428 return ret;
429 }
430
431 return JNI_VERSION_1_6;
432}
static JNINativeMethod methods[]
bool ref() noexcept
bool deref() noexcept
bool testAndSetAcquire(T expectedValue, T newValue) noexcept
void storeRelease(T newValue) noexcept
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qmutex.h:285
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qsemaphore.h:16
\inmodule QtCore
virtual jobject onBind(jobject intent)=0
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()
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 registerNativeInterfaceNatives()
Posts the function runnable to the Android thread.
Q_CORE_EXPORT void handleResume()
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 jboolean dispatchGenericMotionEvent(JNIEnv *, jclass, jobject event)
static jobject g_jService
static Q_CONSTINIT QtAndroidPrivate::OnBindListener * g_onBindListener
static jobject g_jClassLoader
static jobject g_jActivity
QT_END_NAMESPACE JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
static jboolean dispatchKeyEvent(JNIEnv *, jclass, jobject event)
static Q_CONSTINIT QBasicMutex g_onBindListenerMutex
static Q_CONSTINIT QBasicAtomicInt g_androidDeadlockProtector
static JavaVM * g_javaVM
static jboolean updateNativeActivity(JNIEnv *env, jclass=nullptr)
static Q_CONSTINIT QBasicAtomicInt g_serviceSetupLockers
return ret
GLbitfield flags
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
struct _cl_event * event
#define Q_UNUSED(x)
QT_END_NAMESPACE typedef QT_PREPEND_NAMESPACE(quintptr) WId
QMutex mutex
[2]
QReadWriteLock lock
[0]