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