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
22 // *Listener virtual function implementations.
23 // Defined out-of-line to pin the vtable/type_info.
29}
30
31static JavaVM *g_javaVM = nullptr;
32static jobject g_jActivity = nullptr;
33static jobject g_jService = nullptr;
34static jobject g_jClassLoader = nullptr;
35
38Q_GLOBAL_STATIC(QSemaphore, g_waitForServiceSetupSemaphore);
40
42
43static jboolean updateNativeActivity(JNIEnv *env, jclass = nullptr)
44{
45
46 jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative");
47 if (QJniEnvironment::checkAndClearExceptions(env))
48 return JNI_FALSE;
49
50 jmethodID activityMethodID =
51 env->GetStaticMethodID(jQtNative, "activity", "()Landroid/app/Activity;");
52 if (QJniEnvironment::checkAndClearExceptions(env))
53 return JNI_FALSE;
54
55 jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
56 if (QJniEnvironment::checkAndClearExceptions(env))
57 return JNI_FALSE;
58
59 QWriteLocker locker(g_updateMutex());
60
61 if (g_jActivity) {
62 env->DeleteGlobalRef(g_jActivity);
63 g_jActivity = nullptr;
64 }
65
66 if (activity) {
67 g_jActivity = env->NewGlobalRef(activity);
68 env->DeleteLocalRef(activity);
69 }
70
71 env->DeleteLocalRef(jQtNative);
72 if (QJniEnvironment::checkAndClearExceptions(env))
73 return JNI_FALSE;
74
75 return JNI_TRUE;
76}
77
78namespace {
79 class ActivityResultListeners
80 {
81 public:
83 QList<QtAndroidPrivate::ActivityResultListener *> listeners;
84 };
85}
86
87Q_GLOBAL_STATIC(ActivityResultListeners, g_activityResultListeners)
88
89void QtAndroidPrivate::registerActivityResultListener(ActivityResultListener *listener)
90{
91 QMutexLocker locker(&g_activityResultListeners()->mutex);
92 g_activityResultListeners()->listeners.append(listener);
93}
94
96{
97 QMutexLocker locker(&g_activityResultListeners()->mutex);
98 g_activityResultListeners()->listeners.removeAll(listener);
99}
100
101void QtAndroidPrivate::handleActivityResult(jint requestCode, jint resultCode, jobject data)
102{
103 QMutexLocker locker(&g_activityResultListeners()->mutex);
104 const QList<QtAndroidPrivate::ActivityResultListener *> &listeners = g_activityResultListeners()->listeners;
105 for (int i=0; i<listeners.size(); ++i) {
106 if (listeners.at(i)->handleActivityResult(requestCode, resultCode, data))
107 break;
108 }
109}
110
111namespace {
112 class NewIntentListeners
113 {
114 public:
116 QList<QtAndroidPrivate::NewIntentListener *> listeners;
117 };
118}
119
120Q_GLOBAL_STATIC(NewIntentListeners, g_newIntentListeners)
121
122void QtAndroidPrivate::registerNewIntentListener(NewIntentListener *listener)
123{
124 QMutexLocker locker(&g_newIntentListeners()->mutex);
125 g_newIntentListeners()->listeners.append(listener);
126}
127
129{
130 QMutexLocker locker(&g_newIntentListeners()->mutex);
131 g_newIntentListeners()->listeners.removeAll(listener);
132}
133
134void QtAndroidPrivate::handleNewIntent(JNIEnv *env, jobject intent)
135{
136 QMutexLocker locker(&g_newIntentListeners()->mutex);
137 const QList<QtAndroidPrivate::NewIntentListener *> &listeners = g_newIntentListeners()->listeners;
138 for (int i=0; i<listeners.size(); ++i) {
139 if (listeners.at(i)->handleNewIntent(env, intent))
140 break;
141 }
142}
143
144namespace {
145 class ResumePauseListeners
146 {
147 public:
149 QList<QtAndroidPrivate::ResumePauseListener *> listeners;
150 };
151}
152
153Q_GLOBAL_STATIC(ResumePauseListeners, g_resumePauseListeners)
154
155void QtAndroidPrivate::registerResumePauseListener(ResumePauseListener *listener)
156{
157 QMutexLocker locker(&g_resumePauseListeners()->mutex);
158 g_resumePauseListeners()->listeners.append(listener);
159}
160
162{
163 QMutexLocker locker(&g_resumePauseListeners()->mutex);
164 g_resumePauseListeners()->listeners.removeAll(listener);
165}
166
168{
169 QMutexLocker locker(&g_resumePauseListeners()->mutex);
170 const QList<QtAndroidPrivate::ResumePauseListener *> &listeners = g_resumePauseListeners()->listeners;
171 for (int i=0; i<listeners.size(); ++i)
172 listeners.at(i)->handlePause();
173}
174
176{
177 QMutexLocker locker(&g_resumePauseListeners()->mutex);
178 const QList<QtAndroidPrivate::ResumePauseListener *> &listeners = g_resumePauseListeners()->listeners;
179 for (int i=0; i<listeners.size(); ++i)
180 listeners.at(i)->handleResume();
181}
182
183jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
184{
185 g_javaVM = vm;
186
187 jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative");
188
189 if (QJniEnvironment::checkAndClearExceptions(env))
190 return JNI_ERR;
191
192 jmethodID activityMethodID = env->GetStaticMethodID(jQtNative,
193 "activity",
194 "()Landroid/app/Activity;");
195
196 if (QJniEnvironment::checkAndClearExceptions(env))
197 return JNI_ERR;
198
199 jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
200
201 if (QJniEnvironment::checkAndClearExceptions(env))
202 return JNI_ERR;
203
204 jmethodID serviceMethodID = env->GetStaticMethodID(jQtNative,
205 "service",
206 "()Landroid/app/Service;");
207
208 if (QJniEnvironment::checkAndClearExceptions(env))
209 return JNI_ERR;
210
211 jobject service = env->CallStaticObjectMethod(jQtNative, serviceMethodID);
212
213 if (QJniEnvironment::checkAndClearExceptions(env))
214 return JNI_ERR;
215
216 jmethodID classLoaderMethodID = env->GetStaticMethodID(jQtNative,
217 "classLoader",
218 "()Ljava/lang/ClassLoader;");
219
220 if (QJniEnvironment::checkAndClearExceptions(env))
221 return JNI_ERR;
222
223 jobject classLoader = env->CallStaticObjectMethod(jQtNative, classLoaderMethodID);
224 if (QJniEnvironment::checkAndClearExceptions(env))
225 return JNI_ERR;
226
227 g_jClassLoader = env->NewGlobalRef(classLoader);
228 env->DeleteLocalRef(classLoader);
229 if (activity) {
230 g_jActivity = env->NewGlobalRef(activity);
231 env->DeleteLocalRef(activity);
232 }
233 if (service) {
234 g_jService = env->NewGlobalRef(service);
235 env->DeleteLocalRef(service);
236 }
237
238 static const JNINativeMethod methods[] = {
239 {"updateNativeActivity", "()Z", reinterpret_cast<void *>(updateNativeActivity) },
240 };
241
242 const bool regOk = (env->RegisterNatives(jQtNative, methods, sizeof(methods) / sizeof(methods[0])) == JNI_OK);
243 env->DeleteLocalRef(jQtNative);
244 if (!regOk && QJniEnvironment::checkAndClearExceptions(env))
245 return JNI_ERR;
246
247 QJniEnvironment qJniEnv;
248 if (!registerPermissionNatives(qJniEnv))
249 return JNI_ERR;
250
251 if (!registerNativeInterfaceNatives(qJniEnv))
252 return JNI_ERR;
253
254 if (!registerExtrasNatives(qJniEnv))
255 return JNI_ERR;
256
257 return JNI_OK;
258}
259
260Q_CORE_EXPORT jobject qt_androidActivity()
261{
262 QReadLocker locker(g_updateMutex());
263 return g_jActivity;
264}
265
266
267QtJniTypes::Activity QtAndroidPrivate::activity()
268{
269 QReadLocker locker(g_updateMutex());
270 return g_jActivity;
271}
272
273Q_CORE_EXPORT jobject qt_androidService()
274{
275 return g_jService;
276}
277
278QtJniTypes::Service QtAndroidPrivate::service()
279{
280 return g_jService;
281}
282
283QtJniTypes::Context QtAndroidPrivate::context()
284{
285 QReadLocker locker(g_updateMutex());
286 if (g_jActivity)
287 return g_jActivity;
288 if (g_jService)
289 return g_jService;
290
291 return nullptr;
292}
293
295{
296 return g_javaVM;
297}
298
300{
301 return g_jClassLoader;
302}
303
305{
306 static jint sdkVersion = 0;
307 if (!sdkVersion)
308 sdkVersion = QJniObject::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT");
309 return sdkVersion;
310}
311
313{
314 g_waitForServiceSetupSemaphore->acquire();
315}
316
322
324{
325 const auto lock = qt_scoped_lock(g_onBindListenerMutex);
326 g_onBindListener = listener;
327 if (!g_serviceSetupLockers.deref())
328 g_waitForServiceSetupSemaphore->release();
329}
330
332{
333 const auto lock = qt_scoped_lock(g_onBindListenerMutex);
335 return g_onBindListener->onBind(intent);
336 return nullptr;
337}
338
340
342{
343 return g_androidDeadlockProtector.testAndSetAcquire(0, 1);
344}
345
350
352
353JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
354{
355 Q_UNUSED(reserved);
356
357 static const char logTag[] = "QtCore";
358 static bool initialized = false;
359 if (initialized)
360 return JNI_VERSION_1_6;
361 initialized = true;
362
363 typedef union {
364 JNIEnv *nenv;
365 void *venv;
366 } _JNIEnv;
367
368 __android_log_print(ANDROID_LOG_INFO, logTag, "Start");
369
370 _JNIEnv uenv;
371 uenv.venv = nullptr;
372
373 if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
374 __android_log_print(ANDROID_LOG_FATAL, logTag, "GetEnv failed");
375 return JNI_ERR;
376 }
377
378 JNIEnv *env = uenv.nenv;
379 const jint ret = QT_PREPEND_NAMESPACE(QtAndroidPrivate::initJNI(vm, env));
380 if (ret != 0) {
381 __android_log_print(ANDROID_LOG_FATAL, logTag, "initJNI failed");
382 return ret;
383 }
384
385 return JNI_VERSION_1_6;
386}
QList< QtAndroidInput::GenericMotionEventListener * > listeners
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 bool acquireAndroidDeadlockProtector()
Q_CORE_EXPORT QtJniTypes::Context context()
bool registerExtrasNatives(QJniEnvironment &env)
bool registerPermissionNatives(QJniEnvironment &env)
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 handlePause()
Q_CORE_EXPORT void unregisterActivityResultListener(ActivityResultListener *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
static Q_CONSTINIT QtAndroidPrivate::OnBindListener * g_onBindListener
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)
Q_CORE_EXPORT jobject qt_androidService()
static Q_CONSTINIT QBasicAtomicInt g_serviceSetupLockers
return ret
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLbitfield flags
#define Q_UNUSED(x)
QMutex mutex
[2]
QReadWriteLock lock
[0]