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
androidjnimain.cpp
Go to the documentation of this file.
1// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
2// Copyright (C) 2022 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6#include <dlfcn.h>
7#include <pthread.h>
8#include <qplugin.h>
9#include <semaphore.h>
10
14#include "androidjnimain.h"
15#include "androidjnimenu.h"
21#if QT_CONFIG(clipboard)
22#include "qandroidplatformclipboard.h"
23#endif
24#if QT_CONFIG(accessibility)
25#include "androidjniaccessibility.h"
26#endif
29
30#include <android/api-level.h>
31#include <android/asset_manager_jni.h>
32#include <android/bitmap.h>
33
34#include <QtCore/private/qjnihelpers_p.h>
35#include <QtCore/qbasicatomic.h>
36#include <QtCore/qjnienvironment.h>
37#include <QtCore/qjniobject.h>
38#include <QtCore/qprocess.h>
39#include <QtCore/qresource.h>
40#include <QtCore/qscopeguard.h>
41#include <QtCore/qthread.h>
42#include <QtCore/private/qandroiditemmodelproxy_p.h>
43#include <QtCore/private/qandroidmodelindexproxy_p.h>
44#include <QtGui/private/qguiapplication_p.h>
45
46#include <qpa/qwindowsysteminterface.h>
47
48
49using namespace Qt::StringLiterals;
50
51QT_BEGIN_NAMESPACE
52
53static jclass m_applicationClass = nullptr;
54static AAssetManager *m_assetManager = nullptr;
55static jobject m_assets = nullptr;
56static jobject m_resourcesObj = nullptr;
57
58static jclass m_qtActivityClass = nullptr;
59static jclass m_qtServiceClass = nullptr;
60
62static QBasicMutex m_platformMutex;
63
64static jclass m_bitmapClass = nullptr;
68
71
72extern "C" typedef int (*Main)(int, char **); //use the standard main method to start the application
73static Main m_main = nullptr;
75
77
78static double m_density = 1.0;
79
83
85
86static const char m_qtTag[] = "Qt";
87static const char m_classErrorMsg[] = "Can't find class \"%s\"";
88static const char m_methodErrorMsg[] = "Can't find method \"%s%s\"";
89static const char m_staticFieldErrorMsg[] = "Can't find static field \"%s\"";
90
91Q_CONSTINIT static QBasicAtomicInt startQtAndroidPluginCalled = Q_BASIC_ATOMIC_INITIALIZER(0);
92
93#if QT_CONFIG(accessibility)
94Q_DECLARE_JNI_CLASS(QtAccessibilityInterface, "org/qtproject/qt/android/QtAccessibilityInterface");
95#endif
96
97Q_DECLARE_JNI_CLASS(QtThread, "org/qtproject/qt/android/QtThread");
98
99namespace QtAndroid
100{
102 {
103 return &m_platformMutex;
104 }
105
107 {
108 m_androidPlatformIntegration = androidPlatformIntegration;
110
111 // flush the pending state if necessary.
113 if (m_pendingApplicationState == Qt::ApplicationActive)
114 QtAndroidPrivate::handleResume();
115 else if (m_pendingApplicationState == Qt::ApplicationInactive)
116 QtAndroidPrivate::handlePause();
117 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(m_pendingApplicationState));
118 }
119
121 }
122
127
128 QWindow *topLevelWindowAt(const QPoint &globalPos)
129 {
132 : 0;
133 }
134
135 QWindow *windowFromId(int windowId)
136 {
137 if (!qGuiApp)
138 return nullptr;
139
140 for (QWindow *w : qGuiApp->allWindows()) {
141 if (!w->handle())
142 continue;
143 QAndroidPlatformWindow *window = static_cast<QAndroidPlatformWindow *>(w->handle());
144 if (window->nativeViewId() == windowId)
145 return w;
146 }
147 return nullptr;
148 }
149
151 {
152 return m_density;
153 }
154
156 {
157 return m_assetManager;
158 }
159
161 {
162 return m_applicationClass;
163 }
164
166 {
167 // Returns true if the app is a Qt app, i.e. Qt controls the whole app and
168 // the Activity/Service is created by Qt. Returns false if instead Qt is
169 // embedded into a native Android app, where the Activity/Service is created
170 // by the user, outside of Qt, and Qt content is added as a view.
171 JNIEnv *env = QJniEnvironment::getJniEnv();
172 auto activity = QtAndroidPrivate::activity();
173 if (activity.isValid())
174 return env->IsInstanceOf(activity.object(), m_qtActivityClass);
175 auto service = QtAndroidPrivate::service();
176 if (service.isValid())
177 return env->IsInstanceOf(QtAndroidPrivate::service().object(), m_qtServiceClass);
178 // return true as default as Qt application is our default use case.
179 // famous last words: we should not end up here
180 return true;
181 }
182
183#if QT_CONFIG(accessibility)
185 {
187 "notifyLocationChange", accessibilityObjectId);
188 }
189
191 {
193 "notifyObjectHide", accessibilityObjectId, parentObjectId);
194 }
195
197 {
199 "notifyObjectShow", parentObjectId);
200 }
201
203 {
205 "notifyObjectFocus", accessibilityObjectId);
206 }
207
209 {
211 "notifyValueChanged", accessibilityObjectId, value);
212 }
213
215 {
217 "notifyDescriptionOrNameChanged", accessibilityObjectId, value);
218 }
219
221 {
223 "notifyScrolledEvent", accessibilityObjectId);
224 }
225
227 {
229 "notifyAnnouncementEvent", accessibilityObjectId, message);
230 }
231#endif //QT_CONFIG(accessibility)
232
234 {
235 QJniObject::callStaticMethod<void>(m_applicationClass,
236 "notifyNativePluginIntegrationReady",
237 ready);
238 }
239
240 jobject createBitmap(QImage img, JNIEnv *env)
241 {
242 if (!m_bitmapClass)
243 return 0;
244
245 if (img.format() != QImage::Format_RGBA8888 && img.format() != QImage::Format_RGB16)
246 img = std::move(img).convertToFormat(QImage::Format_RGBA8888);
247
248 jobject bitmap = env->CallStaticObjectMethod(m_bitmapClass,
249 m_createBitmapMethodID,
250 img.width(),
251 img.height(),
252 img.format() == QImage::Format_RGBA8888
253 ? m_ARGB_8888_BitmapConfigValue
254 : m_RGB_565_BitmapConfigValue);
255 if (!bitmap)
256 return 0;
257
258 AndroidBitmapInfo info;
259 if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
260 env->DeleteLocalRef(bitmap);
261 return 0;
262 }
263
264 void *pixels;
265 if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) {
266 env->DeleteLocalRef(bitmap);
267 return 0;
268 }
269
270 if (info.stride == uint(img.bytesPerLine())
271 && info.width == uint(img.width())
272 && info.height == uint(img.height())) {
273 memcpy(pixels, img.constBits(), info.stride * info.height);
274 } else {
275 uchar *bmpPtr = static_cast<uchar *>(pixels);
276 const unsigned width = qMin(info.width, (uint)img.width()); //should be the same
277 const unsigned height = qMin(info.height, (uint)img.height()); //should be the same
278 for (unsigned y = 0; y < height; y++, bmpPtr += info.stride)
279 memcpy(bmpPtr, img.constScanLine(y), width);
280 }
281 AndroidBitmap_unlockPixels(env, bitmap);
282 return bitmap;
283 }
284
285 jobject createBitmap(int width, int height, QImage::Format format, JNIEnv *env)
286 {
287 if (format != QImage::Format_RGBA8888
288 && format != QImage::Format_RGB16)
289 return 0;
290
291 return env->CallStaticObjectMethod(m_bitmapClass,
292 m_createBitmapMethodID,
293 width,
294 height,
295 format == QImage::Format_RGB16
296 ? m_RGB_565_BitmapConfigValue
297 : m_ARGB_8888_BitmapConfigValue);
298 }
299
300 jobject createBitmapDrawable(jobject bitmap, JNIEnv *env)
301 {
302 if (!bitmap || !m_bitmapDrawableClass || !m_resourcesObj)
303 return 0;
304
305 return env->NewObject(m_bitmapDrawableClass,
306 m_bitmapDrawableConstructorMethodID,
307 m_resourcesObj,
308 bitmap);
309 }
310
311 const char *classErrorMsgFmt()
312 {
313 return m_classErrorMsg;
314 }
315
316 const char *methodErrorMsgFmt()
317 {
318 return m_methodErrorMsg;
319 }
320
322 {
324 }
325
326 const char *qtTagText()
327 {
328 return m_qtTag;
329 }
330
331 QString deviceName()
332 {
333 QString manufacturer = QJniObject::getStaticObjectField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").toString();
334 QString model = QJniObject::getStaticObjectField("android/os/Build", "MODEL", "Ljava/lang/String;").toString();
335
336 return manufacturer + u' ' + model;
337 }
338
339 void setViewVisibility(jobject view, bool visible)
340 {
341 QJniObject::callStaticMethod<void>(m_applicationClass,
342 "setViewVisibility",
343 "(Landroid/view/View;Z)V",
344 view,
345 visible);
346 }
347
349 {
350 static bool block = qEnvironmentVariableIntValue("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED");
351 return block;
352 }
353
355 {
356 return m_assets;
357 }
358
363
364} // namespace QtAndroid
365
366static bool initJavaReferences(QJniEnvironment &env);
367
368static bool initAndroidQpaPlugin(JNIEnv *jenv, jobject object)
369{
370 Q_UNUSED(jenv)
371 Q_UNUSED(object)
372
373 // Init all the Java refs, if they haven't already been initialized. They get initialized
374 // when the library is loaded, but in case Qt is terminated, they are cleared, and in case
375 // Qt is then started again JNI_OnLoad will not be called again, since the library is already
376 // loaded - in that case we need to init again here, hence the check.
377 // TODO QTBUG-130614 QtCore also inits some Java references in qjnihelpers - we probably
378 // want to reset those, too.
379 QJniEnvironment qEnv;
380 if (!qEnv.isValid()) {
381 qCritical() << "Failed to initialize the JNI Environment";
382 return false;
383 }
384
385 if (!initJavaReferences(qEnv))
386 return false;
387
389
390 // File engine handler instantiation registers the handler
394
396
397 if (sem_init(&m_exitSemaphore, 0, 0) == -1 && sem_init(&m_stopQtSemaphore, 0, 0) == -1) {
398 qCritical() << "Failed to init Qt application cleanup semaphores";
399 return false;
400 }
401
402 return true;
403}
404
405static void startQtNativeApplication(JNIEnv *jenv, jobject object, jstring paramsString)
406{
407 Q_UNUSED(jenv)
408 Q_UNUSED(object)
409
410 {
411 JNIEnv* env = nullptr;
412 JavaVMAttachArgs args;
413 args.version = JNI_VERSION_1_6;
414 args.name = "QtMainThread";
415 args.group = NULL;
416 JavaVM *vm = QJniEnvironment::javaVM();
417 if (vm)
418 vm->AttachCurrentThread(&env, &args);
419 }
420
421 const QStringList argsList = QProcess::splitCommand(QJniObject(paramsString).toString());
422 const int argc = argsList.size();
423 QVarLengthArray<char *> argv(argc + 1);
424 QList<QByteArray> argvData;
425 argvData.reserve(argc);
426 for (int i = 0; i < argc; ++i) {
427 argvData.append(argsList.at(i).toUtf8());
428 argv[i] = argvData.back().data();
429 }
430 argv[argc] = nullptr;
431
432 // Go home
433 QDir::setCurrent(QDir::homePath());
434
435 // look for main()
436 void *mainLibraryHnd = nullptr;
437 if (argc) {
438 // Obtain a handle to the main library (the library that contains the main() function).
439 // This library should already be loaded, and calling dlopen() will just return a reference to it.
440 mainLibraryHnd = dlopen(argv.first(), 0);
441 if (Q_UNLIKELY(!mainLibraryHnd)) {
442 qCritical() << "dlopen failed:" << dlerror();
443 return;
444 }
445 m_main = (Main)dlsym(mainLibraryHnd, "main");
446 } else {
447 qWarning("No main library was specified; searching entire process (this is slow!)");
448 m_main = (Main)dlsym(RTLD_DEFAULT, "main");
449 }
450
451 if (Q_UNLIKELY(!m_main)) {
452 qCritical() << "dlsym failed:" << dlerror() << Qt::endl
453 << "Could not find main method";
454 return;
455 }
456
457 // Register type for invokeMethod() calls.
458 qRegisterMetaType<Qt::ScreenOrientation>("Qt::ScreenOrientation");
459
460 // Register resources if they are available
461 if (QFile{QStringLiteral("assets:/android_rcc_bundle.rcc")}.exists())
462 QResource::registerResource(QStringLiteral("assets:/android_rcc_bundle.rcc"));
463
464 startQtAndroidPluginCalled.fetchAndAddRelease(1);
465
466 const int ret = m_main(argc, argv.data());
467 qInfo() << "main() returned" << ret;
468
469 if (mainLibraryHnd) {
470 int res = dlclose(mainLibraryHnd);
471 if (res < 0)
472 qWarning() << "dlclose failed:" << dlerror();
473 }
474
475 QNativeInterface::QAndroidApplication::runOnAndroidMainThread([]() {
476 QtNative::callStaticMethod("setStarted", false);
477
478 if (QtAndroid::isQtApplication()) {
479 // Now, that the Qt application has exited, tear down the Activity and Service
480 auto activity = QtAndroidPrivate::activity();
481 if (activity.isValid())
482 activity.callMethod("finish");
483 auto service = QtAndroidPrivate::service();
484 if (service.isValid())
485 service.callMethod("stopSelf");
486 } else {
487 // For the embedded case, we only need to terminate Qt
488 QtNative::callStaticMethod("terminateQtNativeApplication");
489 }
490 });
491
492 sem_post(&m_stopQtSemaphore);
493 sem_wait(&m_exitSemaphore);
494 sem_destroy(&m_exitSemaphore);
495
496 // We must call exit() to ensure that all global objects will be destructed
497 if (!qEnvironmentVariableIsSet("QT_ANDROID_NO_EXIT_CALL"))
498 exit(ret);
499}
500
501static void clearJavaReferences(JNIEnv *env)
502{
503 if (m_applicationClass) {
504 env->DeleteGlobalRef(m_applicationClass);
505 m_applicationClass = nullptr;
506 }
507 if (m_resourcesObj) {
508 env->DeleteGlobalRef(m_resourcesObj);
509 m_resourcesObj = nullptr;
510 }
511 if (m_bitmapClass) {
512 env->DeleteGlobalRef(m_bitmapClass);
513 m_bitmapClass = nullptr;
514 }
516 env->DeleteGlobalRef(m_ARGB_8888_BitmapConfigValue);
518 }
520 env->DeleteGlobalRef(m_RGB_565_BitmapConfigValue);
522 }
524 env->DeleteGlobalRef(m_bitmapDrawableClass);
525 m_bitmapDrawableClass = nullptr;
526 }
527 if (m_assets) {
528 env->DeleteGlobalRef(m_assets);
529 m_assets = nullptr;
530 }
531 if (m_qtActivityClass) {
532 env->DeleteGlobalRef(m_qtActivityClass);
533 m_qtActivityClass = nullptr;
534 }
535 if (m_qtServiceClass) {
536 env->DeleteGlobalRef(m_qtServiceClass);
537 m_qtServiceClass = nullptr;
538 }
539}
540
541static void waitForServiceSetup(JNIEnv *env, jclass /*clazz*/)
542{
543 Q_UNUSED(env);
544 // The service must wait until the QCoreApplication starts otherwise onBind will be
545 // called too early
546 if (QtAndroidPrivate::service().isValid() && QtAndroid::isQtApplication())
547 QtAndroidPrivate::waitForServiceSetup();
548}
549
550static void terminateQtNativeApplication(JNIEnv *env, jclass /*clazz*/)
551{
552 // QAndroidEventDispatcherStopper is stopped when the user uses the task manager
553 // to kill the application. Also, in case of a service ensure to call quit().
554 if (QAndroidEventDispatcherStopper::instance()->stopped()
555 || QtAndroidPrivate::service().isValid()) {
556 QAndroidEventDispatcherStopper::instance()->startAll();
557 QCoreApplication::quit();
558 QAndroidEventDispatcherStopper::instance()->goingToStop(false);
559 }
560
561 if (startQtAndroidPluginCalled.loadAcquire())
562 sem_wait(&m_stopQtSemaphore);
563
564 sem_destroy(&m_stopQtSemaphore);
565
566 clearJavaReferences(env);
567
568 m_androidPlatformIntegration = nullptr;
569 delete m_androidAssetsFileEngineHandler;
570 m_androidAssetsFileEngineHandler = nullptr;
571 delete m_androidContentFileEngineHandler;
572 m_androidContentFileEngineHandler = nullptr;
573 delete m_androidApkFileEngineHandler;
574 m_androidApkFileEngineHandler = nullptr;
575 delete m_backendRegister;
576 m_backendRegister = nullptr;
577 sem_post(&m_exitSemaphore);
578
579 // Terminate the QtThread
580 QtNative::callStaticMethod<QtThread>("getQtThread").callMethod("exit");
581}
582
583static void handleLayoutSizeChanged(JNIEnv * /*env*/, jclass /*clazz*/,
584 jint availableWidth, jint availableHeight)
585{
586 QMutexLocker lock(&m_platformMutex);
587 // available geometry always starts from top left
588 const QRect availableGeometry(0, 0, availableWidth, availableHeight);
589 if (m_androidPlatformIntegration)
590 m_androidPlatformIntegration->setAvailableGeometry(availableGeometry);
591 else if (QAndroidPlatformScreen::defaultAvailableGeometry().isNull())
592 QAndroidPlatformScreen::defaultAvailableGeometry() = availableGeometry;
593}
594Q_DECLARE_JNI_NATIVE_METHOD(handleLayoutSizeChanged)
595
596static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state)
597{
598 QMutexLocker lock(&m_platformMutex);
601 return;
602 }
603
604 // We're about to call user code from the Android thread, since we don't know
605 //the side effects we'll unlock first!
606 lock.unlock();
607 if (state == Qt::ApplicationActive)
608 QtAndroidPrivate::handleResume();
609 else if (state == Qt::ApplicationInactive)
610 QtAndroidPrivate::handlePause();
611 lock.relock();
613 return;
614
615 if (state <= Qt::ApplicationInactive) {
616 // NOTE: sometimes we will receive two consecutive suspended notifications,
617 // In the second suspended notification, QWindowSystemInterface::flushWindowSystemEvents()
618 // will deadlock since the dispatcher has been stopped in the first suspended notification.
619 // To avoid the deadlock we simply return if we found the event dispatcher has been stopped.
621 return;
622
623 // Don't send timers and sockets events anymore if we are going to hide all windows
625 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state));
626 if (state == Qt::ApplicationSuspended)
628 } else {
630 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state));
632 }
633}
634
635static void updateLocale(JNIEnv */*env*/, jobject /*thiz*/)
636{
637 QCoreApplication::postEvent(QCoreApplication::instance(), new QEvent(QEvent::LocaleChange));
638 QCoreApplication::postEvent(QCoreApplication::instance(), new QEvent(QEvent::LanguageChange));
639}
640
641static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newRotation, jint nativeOrientation)
642{
643 // Array of orientations rotated in 90 degree increments, counterclockwise
644 // (same direction as Android measures angles)
645 static const Qt::ScreenOrientation orientations[] = {
646 Qt::PortraitOrientation,
647 Qt::LandscapeOrientation,
648 Qt::InvertedPortraitOrientation,
649 Qt::InvertedLandscapeOrientation
650 };
651
652 // The Android API defines the following constants:
653 // ROTATION_0 : 0
654 // ROTATION_90 : 1
655 // ROTATION_180 : 2
656 // ROTATION_270 : 3
657 // ORIENTATION_PORTRAIT : 1
658 // ORIENTATION_LANDSCAPE : 2
659
660 // and newRotation is how much the current orientation is rotated relative to nativeOrientation
661
662 // which means that we can be really clever here :)
663 Qt::ScreenOrientation screenOrientation = orientations[(nativeOrientation - 1 + newRotation) % 4];
664 Qt::ScreenOrientation native = orientations[nativeOrientation - 1];
665
666 QAndroidPlatformIntegration::setScreenOrientation(screenOrientation, native);
667 QMutexLocker lock(&m_platformMutex);
670 // Use invokeMethod to keep the certain order of the "geometry change"
671 // and "orientation change" event handling.
672 if (screen) {
673 QMetaObject::invokeMethod(screen, "setOrientation", Qt::AutoConnection,
674 Q_ARG(Qt::ScreenOrientation, screenOrientation));
675 }
676 }
677}
678Q_DECLARE_JNI_NATIVE_METHOD(handleOrientationChanged)
679
680static void handleRefreshRateChanged(JNIEnv */*env*/, jclass /*cls*/, jfloat refreshRate)
681{
682 if (m_androidPlatformIntegration)
683 m_androidPlatformIntegration->setRefreshRate(refreshRate);
684}
685Q_DECLARE_JNI_NATIVE_METHOD(handleRefreshRateChanged)
686
687static void handleScreenAdded(JNIEnv */*env*/, jclass /*cls*/, jint displayId)
688{
689 if (m_androidPlatformIntegration)
690 m_androidPlatformIntegration->handleScreenAdded(displayId);
691}
692Q_DECLARE_JNI_NATIVE_METHOD(handleScreenAdded)
693
694static void handleScreenChanged(JNIEnv */*env*/, jclass /*cls*/, jint displayId)
695{
696 if (m_androidPlatformIntegration)
697 m_androidPlatformIntegration->handleScreenChanged(displayId);
698}
699Q_DECLARE_JNI_NATIVE_METHOD(handleScreenChanged)
700
701static void handleScreenRemoved(JNIEnv */*env*/, jclass /*cls*/, jint displayId)
702{
703 if (m_androidPlatformIntegration)
704 m_androidPlatformIntegration->handleScreenRemoved(displayId);
705}
706Q_DECLARE_JNI_NATIVE_METHOD(handleScreenRemoved)
707
708static void handleUiDarkModeChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newUiMode)
709{
710 QAndroidPlatformIntegration::updateColorScheme(
711 (newUiMode == 1 ) ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light);
712}
713Q_DECLARE_JNI_NATIVE_METHOD(handleUiDarkModeChanged)
714
715static void handleScreenDensityChanged(JNIEnv */*env*/, jclass /*cls*/, jdouble density)
716{
717 m_density = density;
718}
719Q_DECLARE_JNI_NATIVE_METHOD(handleScreenDensityChanged)
720
721static void onActivityResult(JNIEnv */*env*/, jclass /*cls*/,
722 jint requestCode,
723 jint resultCode,
724 jobject data)
725{
726 QtAndroidPrivate::handleActivityResult(requestCode, resultCode, data);
727}
728
729static void onNewIntent(JNIEnv *env, jclass /*cls*/, jobject data)
730{
731 QtAndroidPrivate::handleNewIntent(env, data);
732}
733
734static jobject onBind(JNIEnv */*env*/, jclass /*cls*/, jobject intent)
735{
736 return QtAndroidPrivate::callOnBindListener(intent);
737}
738
740 { "initAndroidQpaPlugin", "()Z", (void *)initAndroidQpaPlugin },
741 { "startQtNativeApplication", "(Ljava/lang/String;)V", (void *)startQtNativeApplication },
742 { "terminateQtNativeApplication", "()V", (void *)terminateQtNativeApplication },
743 { "waitForServiceSetup", "()V", (void *)waitForServiceSetup },
744 { "updateApplicationState", "(I)V", (void *)updateApplicationState },
745 { "onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult },
746 { "onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent },
747 { "onBind", "(Landroid/content/Intent;)Landroid/os/IBinder;", (void *)onBind },
748 { "updateLocale", "()V", (void *)updateLocale },
749};
750
751#define FIND_AND_CHECK_CLASS(CLASS_NAME) clazz
752 = env->FindClass(CLASS_NAME); if
753 (!clazz) {
754 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_classErrorMsg, CLASS_NAME);
755 return false; \
756}
757
758#define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) VAR
759 = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); if
760 (!VAR) {
761 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE);
762 return false; \
763}
764
765#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) VAR
766 = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); if
767 (!VAR) {
768 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE);
769 return false; \
770}
771
772#define GET_AND_CHECK_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) VAR
773 = env->GetFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); if
774 (!VAR) {
775 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE);
776 return false; \
777}
778
779#define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) VAR
780 = env->GetStaticFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); if
781 (!VAR) {
782 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE);
783 return false; \
784}
785
786Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtDisplayManager")
787
788static bool registerNatives(QJniEnvironment &env)
789{
790 bool success = env.registerNativeMethods(m_applicationClass,
791 methods, sizeof(methods) / sizeof(methods[0]));
792 success &= env.registerNativeMethods(
793 QtJniTypes::Traits<QtJniTypes::QtDisplayManager>::className(),
794 {
795 Q_JNI_NATIVE_METHOD(handleLayoutSizeChanged),
796 Q_JNI_NATIVE_METHOD(handleOrientationChanged),
797 Q_JNI_NATIVE_METHOD(handleRefreshRateChanged),
798 Q_JNI_NATIVE_METHOD(handleScreenAdded),
799 Q_JNI_NATIVE_METHOD(handleScreenChanged),
800 Q_JNI_NATIVE_METHOD(handleScreenRemoved),
801 Q_JNI_NATIVE_METHOD(handleUiDarkModeChanged),
802 Q_JNI_NATIVE_METHOD(handleScreenDensityChanged)
803 });
804
805 success = success
806 && QtAndroidInput::registerNatives(env)
807 && QtAndroidMenu::registerNatives(env)
808#if QT_CONFIG(accessibility)
809 && QtAndroidAccessibility::registerNatives(env)
810#endif
811 && QtAndroidDialogHelpers::registerNatives(env)
812#if QT_CONFIG(clipboard)
813 && QAndroidPlatformClipboard::registerNatives(env)
814#endif
815 && QAndroidPlatformWindow::registerNatives(env)
816 && QtAndroidWindowEmbedding::registerNatives(env)
817 && AndroidBackendRegister::registerNatives()
818 && QAndroidModelIndexProxy::registerNatives(env)
819 && QAndroidItemModelProxy::registerAbstractNatives(env)
820 && QAndroidItemModelProxy::registerProxyNatives(env);
821
822 return success;
823}
824
825static bool initJavaReferences(QJniEnvironment &env)
826{
827 if (m_applicationClass)
828 return true;
829
830 jclass clazz;
831 FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtNative");
832 m_applicationClass = static_cast<jclass>(env->NewGlobalRef(clazz));
833
834 jmethodID methodID;
835 GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;");
836
837 jobject contextObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
838 if (!contextObject) {
839 GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "service", "()Landroid/app/Service;");
840 contextObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
841 }
842
843 if (!contextObject) {
844 __android_log_print(ANDROID_LOG_FATAL,"Qt", "Failed to get Activity or Service object");
845 return false;
846 }
847 const auto releaseContextObject = qScopeGuard([&env, contextObject]{
848 env->DeleteLocalRef(contextObject);
849 });
850
851 FIND_AND_CHECK_CLASS("android/content/ContextWrapper");
852 GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;");
853 m_assets = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID));
854 m_assetManager = AAssetManager_fromJava(env.jniEnv(), m_assets);
855
856 GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;");
857 m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID));
858
859 FIND_AND_CHECK_CLASS("android/graphics/Bitmap");
860 m_bitmapClass = static_cast<jclass>(env->NewGlobalRef(clazz));
861 GET_AND_CHECK_STATIC_METHOD(m_createBitmapMethodID, m_bitmapClass,
862 "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
863 FIND_AND_CHECK_CLASS("android/graphics/Bitmap$Config");
864 jfieldID fieldId;
865 GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");
866 m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
867 GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "RGB_565", "Landroid/graphics/Bitmap$Config;");
868 m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
869
870 FIND_AND_CHECK_CLASS("android/graphics/drawable/BitmapDrawable");
871 m_bitmapDrawableClass = static_cast<jclass>(env->NewGlobalRef(clazz));
872 GET_AND_CHECK_METHOD(m_bitmapDrawableConstructorMethodID,
873 m_bitmapDrawableClass,
874 "<init>", "(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V");
875
876 FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtActivityBase");
877 m_qtActivityClass = static_cast<jclass>(env->NewGlobalRef(clazz));
878 FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtServiceBase");
879 m_qtServiceClass = static_cast<jclass>(env->NewGlobalRef(clazz));
880
881 // The current thread will be the Qt thread, name it accordingly
882 QThread::currentThread()->setObjectName("QtMainLoopThread");
883
884 QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
885
886 return true;
887}
888
889QT_END_NAMESPACE
890
891extern "C" Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM */*vm*/, void */*reserved*/)
892{
893 static bool initialized = false;
894 if (initialized)
895 return JNI_VERSION_1_6;
896 initialized = true;
897
898 QT_USE_NAMESPACE
899
900 QJniEnvironment env;
901 if (!env.isValid()) {
902 __android_log_print(ANDROID_LOG_FATAL, "Qt", "Failed to initialize the JNI Environment");
903 return JNI_ERR;
904 }
905
906 if (!initJavaReferences(env))
907 return JNI_ERR;
908
909 if (!registerNatives(env)) {
910 __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed");
911 return JNI_ERR;
912 }
913
914 __android_log_print(ANDROID_LOG_INFO, "Qt", "Qt platform plugin started");
915 return JNI_VERSION_1_6;
916}
#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE)
static bool initJavaReferences(QJniEnvironment &env)
static const char m_qtTag[]
static sem_t m_exitSemaphore
#define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE)
static jclass m_qtActivityClass
static AndroidAssetsFileEngineHandler * m_androidAssetsFileEngineHandler
static jclass m_bitmapClass
static void terminateQtNativeApplication(JNIEnv *env, jclass)
static jclass m_bitmapDrawableClass
static void waitForServiceSetup(JNIEnv *env, jclass)
static const char m_staticFieldErrorMsg[]
static Main m_main
static void handleLayoutSizeChanged(JNIEnv *, jclass, jint availableWidth, jint availableHeight)
static jobject m_resourcesObj
static jmethodID m_bitmapDrawableConstructorMethodID
static void handleScreenChanged(JNIEnv *, jclass, jint displayId)
static QAndroidPlatformIntegration * m_androidPlatformIntegration
static AndroidContentFileEngineHandler * m_androidContentFileEngineHandler
static bool registerNatives(QJniEnvironment &env)
static JNINativeMethod methods[]
static AndroidBackendRegister * m_backendRegister
static void onNewIntent(JNIEnv *env, jclass, jobject data)
#define FIND_AND_CHECK_CLASS(CLASS_NAME)
#define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE)
static double m_density
static jobject m_assets
static void handleUiDarkModeChanged(JNIEnv *, jobject, jint newUiMode)
static int m_pendingApplicationState
static void handleRefreshRateChanged(JNIEnv *, jclass, jfloat refreshRate)
static QBasicMutex m_platformMutex
static void updateApplicationState(JNIEnv *, jobject, jint state)
static void clearJavaReferences(JNIEnv *env)
int(* Main)(int, char **)
static void handleOrientationChanged(JNIEnv *, jobject, jint newRotation, jint nativeOrientation)
static void startQtNativeApplication(JNIEnv *jenv, jobject object, jstring paramsString)
static const char m_methodErrorMsg[]
static bool initAndroidQpaPlugin(JNIEnv *jenv, jobject object)
static void handleScreenRemoved(JNIEnv *, jclass, jint displayId)
static void updateLocale(JNIEnv *, jobject)
static jobject m_RGB_565_BitmapConfigValue
static const char m_classErrorMsg[]
static jobject m_ARGB_8888_BitmapConfigValue
static sem_t m_stopQtSemaphore
static jclass m_qtServiceClass
static AAssetManager * m_assetManager
static jmethodID m_createBitmapMethodID
static jobject onBind(JNIEnv *, jclass, jobject intent)
static void onActivityResult(JNIEnv *, jclass, jint requestCode, jint resultCode, jobject data)
static void handleScreenDensityChanged(JNIEnv *, jclass, jdouble density)
static QAndroidApkFileEngineHandler * m_androidApkFileEngineHandler
static void handleScreenAdded(JNIEnv *, jclass, jint displayId)
static QAndroidEventDispatcherStopper * instance()
QWindow * topLevelAt(const QPoint &p) const override
Return the given top level window for a given position.
\inmodule QtCore\reentrant
Definition qpoint.h:30
const char * classErrorMsgFmt()
void setViewVisibility(jobject view, bool visible)
void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration)
const char * qtTagText()
jobject assets()
QBasicMutex * platformInterfaceMutex()
jobject createBitmap(int width, int height, QImage::Format format, JNIEnv *env)
jobject createBitmapDrawable(jobject bitmap, JNIEnv *env=nullptr)
QWindow * topLevelWindowAt(const QPoint &globalPos)
QAndroidPlatformIntegration * androidPlatformIntegration()
jobject createBitmap(QImage img, JNIEnv *env=nullptr)
AndroidBackendRegister * backendRegister()
QString deviceName()
bool blockEventLoopsWhenSuspended()
double pixelDensity()
QWindow * windowFromId(int windowId)
jclass applicationClass()
void notifyNativePluginIntegrationReady(bool ready)
AAssetManager * assetManager()
const char * staticFieldErrorMsgFmt()
bool isQtApplication()
const char * methodErrorMsgFmt()
Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")