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