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