1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
4 ** Copyright (C) 2016 The Qt Company Ltd.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the plugins of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include <dlfcn.h>
42 #include <pthread.h>
43 #include <semaphore.h>
44 #include <qplugin.h>
45 #include <qdebug.h>
46 
47 #include "androidjnimain.h"
48 #include "androidjniaccessibility.h"
49 #include "androidjniinput.h"
50 #include "androidjniclipboard.h"
51 #include "androidjnimenu.h"
52 #include "androidcontentfileengine.h"
53 #include "androiddeadlockprotector.h"
54 #include "qandroidplatformdialoghelpers.h"
55 #include "qandroidplatformintegration.h"
56 #include "qandroidassetsfileenginehandler.h"
57 
58 #include <android/bitmap.h>
59 #include <android/asset_manager_jni.h>
60 #include "qandroideventdispatcher.h"
61 #include <android/api-level.h>
62 
63 #include <QtCore/qresource.h>
64 #include <QtCore/qthread.h>
65 #include <QtCore/private/qjnihelpers_p.h>
66 #include <QtCore/private/qjni_p.h>
67 #include <QtGui/private/qguiapplication_p.h>
68 #include <QtGui/private/qhighdpiscaling_p.h>
69 
70 #include <qpa/qwindowsysteminterface.h>
71 
72 QT_BEGIN_NAMESPACE
73 
74 static JavaVM *m_javaVM = nullptr;
75 static jclass m_applicationClass  = nullptr;
76 static jobject m_classLoaderObject = nullptr;
77 static jmethodID m_loadClassMethodID = nullptr;
78 static AAssetManager *m_assetManager = nullptr;
79 static jobject m_assets = nullptr;
80 static jobject m_resourcesObj = nullptr;
81 static jobject m_activityObject = nullptr;
82 static jmethodID m_createSurfaceMethodID = nullptr;
83 static jobject m_serviceObject = nullptr;
84 static jmethodID m_setSurfaceGeometryMethodID = nullptr;
85 static jmethodID m_destroySurfaceMethodID = nullptr;
86 
87 static int m_pendingApplicationState = -1;
88 static QBasicMutex m_platformMutex;
89 
90 static jclass m_bitmapClass  = nullptr;
91 static jmethodID m_createBitmapMethodID = nullptr;
92 static jobject m_ARGB_8888_BitmapConfigValue = nullptr;
93 static jobject m_RGB_565_BitmapConfigValue = nullptr;
94 
95 static bool m_statusBarShowing = true;
96 
97 static jclass m_bitmapDrawableClass = nullptr;
98 static jmethodID m_bitmapDrawableConstructorMethodID = nullptr;
99 
100 extern "C" typedef int (*Main)(int, char **); //use the standard main method to start the application
101 static Main m_main = nullptr;
102 static void *m_mainLibraryHnd = nullptr;
103 static QList<QByteArray> m_applicationParams;
104 static sem_t m_exitSemaphore, m_terminateSemaphore;
105 
106 QHash<int, AndroidSurfaceClient *> m_surfaces;
107 
108 static QBasicMutex m_surfacesMutex;
109 static int m_surfaceId = 1;
110 
111 
112 static QAndroidPlatformIntegration *m_androidPlatformIntegration = nullptr;
113 
114 static int m_desktopWidthPixels  = 0;
115 static int m_desktopHeightPixels = 0;
116 static double m_scaledDensity = 0;
117 static double m_density = 1.0;
118 
119 static AndroidAssetsFileEngineHandler *m_androidAssetsFileEngineHandler = nullptr;
120 static AndroidContentFileEngineHandler *m_androidContentFileEngineHandler = nullptr;
121 
122 
123 
124 static const char m_qtTag[] = "Qt";
125 static const char m_classErrorMsg[] = "Can't find class \"%s\"";
126 static const char m_methodErrorMsg[] = "Can't find method \"%s%s\"";
127 
128 namespace QtAndroid
129 {
platformInterfaceMutex()130     QBasicMutex *platformInterfaceMutex()
131     {
132         return &m_platformMutex;
133     }
134 
setAndroidPlatformIntegration(QAndroidPlatformIntegration * androidPlatformIntegration)135     void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration)
136     {
137         m_androidPlatformIntegration = androidPlatformIntegration;
138 
139         // flush the pending state if necessary.
140         if (m_androidPlatformIntegration && (m_pendingApplicationState != -1))
141             QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(m_pendingApplicationState));
142 
143         m_pendingApplicationState = -1;
144     }
145 
androidPlatformIntegration()146     QAndroidPlatformIntegration *androidPlatformIntegration()
147     {
148         return m_androidPlatformIntegration;
149     }
150 
topLevelWindowAt(const QPoint & globalPos)151     QWindow *topLevelWindowAt(const QPoint &globalPos)
152     {
153         return m_androidPlatformIntegration
154                ? m_androidPlatformIntegration->screen()->topLevelAt(globalPos)
155                : 0;
156     }
157 
desktopWidthPixels()158     int desktopWidthPixels()
159     {
160         return m_desktopWidthPixels;
161     }
162 
desktopHeightPixels()163     int desktopHeightPixels()
164     {
165         return m_desktopHeightPixels;
166     }
167 
scaledDensity()168     double scaledDensity()
169     {
170         return m_scaledDensity;
171     }
172 
pixelDensity()173     double pixelDensity()
174     {
175         return m_density;
176     }
177 
javaVM()178     JavaVM *javaVM()
179     {
180         return m_javaVM;
181     }
182 
assetManager()183     AAssetManager *assetManager()
184     {
185         return m_assetManager;
186     }
187 
applicationClass()188     jclass applicationClass()
189     {
190         return m_applicationClass;
191     }
192 
activity()193     jobject activity()
194     {
195         return m_activityObject;
196     }
197 
service()198     jobject service()
199     {
200         return m_serviceObject;
201     }
202 
showStatusBar()203     void showStatusBar()
204     {
205         if (m_statusBarShowing)
206             return;
207 
208         QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "setFullScreen", "(Z)V", false);
209         m_statusBarShowing = true;
210     }
211 
hideStatusBar()212     void hideStatusBar()
213     {
214         if (!m_statusBarShowing)
215             return;
216 
217         QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "setFullScreen", "(Z)V", true);
218         m_statusBarShowing = false;
219     }
220 
notifyAccessibilityLocationChange()221     void notifyAccessibilityLocationChange()
222     {
223         QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyAccessibilityLocationChange");
224     }
225 
notifyObjectHide(uint accessibilityObjectId)226     void notifyObjectHide(uint accessibilityObjectId)
227     {
228         QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyObjectHide","(I)V", accessibilityObjectId);
229     }
230 
notifyObjectFocus(uint accessibilityObjectId)231     void notifyObjectFocus(uint accessibilityObjectId)
232     {
233         QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyObjectFocus","(I)V", accessibilityObjectId);
234     }
235 
createBitmap(QImage img,JNIEnv * env)236     jobject createBitmap(QImage img, JNIEnv *env)
237     {
238         if (!m_bitmapClass)
239             return 0;
240 
241         if (img.format() != QImage::Format_RGBA8888 && img.format() != QImage::Format_RGB16)
242             img = img.convertToFormat(QImage::Format_RGBA8888);
243 
244         jobject bitmap = env->CallStaticObjectMethod(m_bitmapClass,
245                                                      m_createBitmapMethodID,
246                                                      img.width(),
247                                                      img.height(),
248                                                      img.format() == QImage::Format_RGBA8888
249                                                         ? m_ARGB_8888_BitmapConfigValue
250                                                         : m_RGB_565_BitmapConfigValue);
251         if (!bitmap)
252             return 0;
253 
254         AndroidBitmapInfo info;
255         if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
256             env->DeleteLocalRef(bitmap);
257             return 0;
258         }
259 
260         void *pixels;
261         if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) {
262             env->DeleteLocalRef(bitmap);
263             return 0;
264         }
265 
266         if (info.stride == uint(img.bytesPerLine())
267                 && info.width == uint(img.width())
268                 && info.height == uint(img.height())) {
269             memcpy(pixels, img.constBits(), info.stride * info.height);
270         } else {
271             uchar *bmpPtr = static_cast<uchar *>(pixels);
272             const unsigned width = qMin(info.width, (uint)img.width()); //should be the same
273             const unsigned height = qMin(info.height, (uint)img.height()); //should be the same
274             for (unsigned y = 0; y < height; y++, bmpPtr += info.stride)
275                 memcpy(bmpPtr, img.constScanLine(y), width);
276         }
277         AndroidBitmap_unlockPixels(env, bitmap);
278         return bitmap;
279     }
280 
createBitmap(int width,int height,QImage::Format format,JNIEnv * env)281     jobject createBitmap(int width, int height, QImage::Format format, JNIEnv *env)
282     {
283         if (format != QImage::Format_RGBA8888
284                 && format != QImage::Format_RGB16)
285             return 0;
286 
287         return env->CallStaticObjectMethod(m_bitmapClass,
288                                                      m_createBitmapMethodID,
289                                                      width,
290                                                      height,
291                                                      format == QImage::Format_RGB16
292                                                         ? m_RGB_565_BitmapConfigValue
293                                                         : m_ARGB_8888_BitmapConfigValue);
294     }
295 
createBitmapDrawable(jobject bitmap,JNIEnv * env)296     jobject createBitmapDrawable(jobject bitmap, JNIEnv *env)
297     {
298         if (!bitmap || !m_bitmapDrawableClass || !m_resourcesObj)
299             return 0;
300 
301         return env->NewObject(m_bitmapDrawableClass,
302                               m_bitmapDrawableConstructorMethodID,
303                               m_resourcesObj,
304                               bitmap);
305     }
306 
classErrorMsgFmt()307     const char *classErrorMsgFmt()
308     {
309         return m_classErrorMsg;
310     }
311 
methodErrorMsgFmt()312     const char *methodErrorMsgFmt()
313     {
314         return m_methodErrorMsg;
315     }
316 
qtTagText()317     const char *qtTagText()
318     {
319         return m_qtTag;
320     }
321 
deviceName()322     QString deviceName()
323     {
324         QString manufacturer = QJNIObjectPrivate::getStaticObjectField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").toString();
325         QString model = QJNIObjectPrivate::getStaticObjectField("android/os/Build", "MODEL", "Ljava/lang/String;").toString();
326 
327         return manufacturer + QLatin1Char(' ') + model;
328     }
329 
createSurface(AndroidSurfaceClient * client,const QRect & geometry,bool onTop,int imageDepth)330     int createSurface(AndroidSurfaceClient *client, const QRect &geometry, bool onTop, int imageDepth)
331     {
332         QJNIEnvironmentPrivate env;
333         if (!env)
334             return -1;
335 
336         m_surfacesMutex.lock();
337         int surfaceId = m_surfaceId++;
338         m_surfaces[surfaceId] = client;
339         m_surfacesMutex.unlock();
340 
341         jint x = 0, y = 0, w = -1, h = -1;
342         if (!geometry.isNull()) {
343             x = geometry.x();
344             y = geometry.y();
345             w = std::max(geometry.width(), 1);
346             h = std::max(geometry.height(), 1);
347         }
348         env->CallStaticVoidMethod(m_applicationClass,
349                                      m_createSurfaceMethodID,
350                                      surfaceId,
351                                      jboolean(onTop),
352                                      x, y, w, h,
353                                      imageDepth);
354         return surfaceId;
355     }
356 
insertNativeView(jobject view,const QRect & geometry)357     int insertNativeView(jobject view, const QRect &geometry)
358     {
359         m_surfacesMutex.lock();
360         const int surfaceId = m_surfaceId++;
361         m_surfaces[surfaceId] = nullptr; // dummy
362         m_surfacesMutex.unlock();
363 
364         jint x = 0, y = 0, w = -1, h = -1;
365         if (!geometry.isNull())
366             geometry.getRect(&x, &y, &w, &h);
367 
368         QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass,
369                                                   "insertNativeView",
370                                                   "(ILandroid/view/View;IIII)V",
371                                                   surfaceId,
372                                                   view,
373                                                   x,
374                                                   y,
375                                                   qMax(w, 1),
376                                                   qMax(h, 1));
377 
378         return surfaceId;
379     }
380 
setViewVisibility(jobject view,bool visible)381     void setViewVisibility(jobject view, bool visible)
382     {
383         QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass,
384                                                   "setViewVisibility",
385                                                   "(Landroid/view/View;Z)V",
386                                                   view,
387                                                   visible);
388     }
389 
setSurfaceGeometry(int surfaceId,const QRect & geometry)390     void setSurfaceGeometry(int surfaceId, const QRect &geometry)
391     {
392         if (surfaceId == -1)
393             return;
394 
395         QJNIEnvironmentPrivate env;
396         if (!env)
397             return;
398         jint x = 0, y = 0, w = -1, h = -1;
399         if (!geometry.isNull()) {
400             x = geometry.x();
401             y = geometry.y();
402             w = geometry.width();
403             h = geometry.height();
404         }
405         env->CallStaticVoidMethod(m_applicationClass,
406                                      m_setSurfaceGeometryMethodID,
407                                      surfaceId,
408                                      x, y, w, h);
409     }
410 
411 
destroySurface(int surfaceId)412     void destroySurface(int surfaceId)
413     {
414         if (surfaceId == -1)
415             return;
416 
417         {
418             QMutexLocker lock(&m_surfacesMutex);
419             const auto &it = m_surfaces.find(surfaceId);
420             if (it != m_surfaces.end())
421                 m_surfaces.erase(it);
422         }
423 
424         QJNIEnvironmentPrivate env;
425         if (env)
426             env->CallStaticVoidMethod(m_applicationClass,
427                                      m_destroySurfaceMethodID,
428                                      surfaceId);
429     }
430 
bringChildToFront(int surfaceId)431     void bringChildToFront(int surfaceId)
432     {
433         if (surfaceId == -1)
434             return;
435 
436         QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass,
437                                                   "bringChildToFront",
438                                                   "(I)V",
439                                                   surfaceId);
440     }
441 
bringChildToBack(int surfaceId)442     void bringChildToBack(int surfaceId)
443     {
444         if (surfaceId == -1)
445             return;
446 
447         QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass,
448                                                   "bringChildToBack",
449                                                   "(I)V",
450                                                   surfaceId);
451     }
452 
blockEventLoopsWhenSuspended()453     bool blockEventLoopsWhenSuspended()
454     {
455         static bool block = qEnvironmentVariableIntValue("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED");
456         return block;
457     }
458 
assets()459     jobject assets()
460     {
461         return m_assets;
462     }
463 
464 } // namespace QtAndroid
465 
startQtAndroidPlugin(JNIEnv * env,jobject,jstring paramsString,jstring environmentString)466 static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring paramsString, jstring environmentString)
467 {
468     m_androidPlatformIntegration = nullptr;
469     m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler();
470     m_androidContentFileEngineHandler = new AndroidContentFileEngineHandler();
471     m_mainLibraryHnd = nullptr;
472     { // Set env. vars
473         const char *nativeString = env->GetStringUTFChars(environmentString, 0);
474         const QList<QByteArray> envVars = QByteArray(nativeString).split('\t');
475         env->ReleaseStringUTFChars(environmentString, nativeString);
476         for (const QByteArray &envVar : envVars) {
477             int pos = envVar.indexOf('=');
478             if (pos != -1 && ::setenv(envVar.left(pos), envVar.mid(pos + 1), 1) != 0)
479                 qWarning() << "Can't set environment" << envVar;
480         }
481     }
482 
483     const char *nativeString = env->GetStringUTFChars(paramsString, 0);
484     QByteArray string = nativeString;
485     env->ReleaseStringUTFChars(paramsString, nativeString);
486 
487     m_applicationParams=string.split('\t');
488 
489     // Go home
490     QDir::setCurrent(QDir::homePath());
491 
492     //look for main()
493     if (m_applicationParams.length()) {
494         // Obtain a handle to the main library (the library that contains the main() function).
495         // This library should already be loaded, and calling dlopen() will just return a reference to it.
496         m_mainLibraryHnd = dlopen(m_applicationParams.constFirst().data(), 0);
497         if (Q_UNLIKELY(!m_mainLibraryHnd)) {
498             qCritical() << "dlopen failed:" << dlerror();
499             return false;
500         }
501         m_main = (Main)dlsym(m_mainLibraryHnd, "main");
502     } else {
503         qWarning("No main library was specified; searching entire process (this is slow!)");
504         m_main = (Main)dlsym(RTLD_DEFAULT, "main");
505     }
506 
507     if (Q_UNLIKELY(!m_main)) {
508         qCritical() << "dlsym failed:" << dlerror() << Qt::endl
509                     << "Could not find main method";
510         return false;
511     }
512 
513     if (sem_init(&m_exitSemaphore, 0, 0) == -1)
514         return false;
515 
516     if (sem_init(&m_terminateSemaphore, 0, 0) == -1)
517         return false;
518 
519     return true;
520 }
521 
waitForServiceSetup(JNIEnv * env,jclass)522 static void waitForServiceSetup(JNIEnv *env, jclass /*clazz*/)
523 {
524     Q_UNUSED(env);
525     // The service must wait until the QCoreApplication starts otherwise onBind will be
526     // called too early
527     if (m_serviceObject)
528         QtAndroidPrivate::waitForServiceSetup();
529 }
530 
startQtApplication(JNIEnv *,jclass)531 static jboolean startQtApplication(JNIEnv */*env*/, jclass /*clazz*/)
532 {
533     {
534         JNIEnv* env = nullptr;
535         JavaVMAttachArgs args;
536         args.version = JNI_VERSION_1_6;
537         args.name = "QtMainThread";
538         args.group = NULL;
539         JavaVM *vm = QtAndroidPrivate::javaVM();
540         if (vm != 0)
541             vm->AttachCurrentThread(&env, &args);
542     }
543 
544     // Register resources if they are available
545     if (QFile{QStringLiteral("assets:/android_rcc_bundle.rcc")}.exists())
546         QResource::registerResource(QStringLiteral("assets:/android_rcc_bundle.rcc"));
547 
548     QVarLengthArray<const char *> params(m_applicationParams.size());
549     for (int i = 0; i < m_applicationParams.size(); i++)
550         params[i] = static_cast<const char *>(m_applicationParams[i].constData());
551 
552     int ret = m_main(m_applicationParams.length(), const_cast<char **>(params.data()));
553 
554     if (m_mainLibraryHnd) {
555         int res = dlclose(m_mainLibraryHnd);
556         if (res < 0)
557             qWarning() << "dlclose failed:" << dlerror();
558     }
559 
560     if (m_applicationClass) {
561         qWarning("exit app 0");
562         QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "quitApp", "()V");
563     }
564 
565     sem_post(&m_terminateSemaphore);
566     sem_wait(&m_exitSemaphore);
567     sem_destroy(&m_exitSemaphore);
568 
569     // We must call exit() to ensure that all global objects will be destructed
570     exit(ret);
571 }
572 
quitQtCoreApplication(JNIEnv * env,jclass)573 static void quitQtCoreApplication(JNIEnv *env, jclass /*clazz*/)
574 {
575     Q_UNUSED(env);
576     QCoreApplication::quit();
577 }
578 
quitQtAndroidPlugin(JNIEnv * env,jclass)579 static void quitQtAndroidPlugin(JNIEnv *env, jclass /*clazz*/)
580 {
581     Q_UNUSED(env);
582     m_androidPlatformIntegration = nullptr;
583     delete m_androidAssetsFileEngineHandler;
584     m_androidAssetsFileEngineHandler = nullptr;
585     delete m_androidContentFileEngineHandler;
586     m_androidContentFileEngineHandler = nullptr;
587 }
588 
terminateQt(JNIEnv * env,jclass)589 static void terminateQt(JNIEnv *env, jclass /*clazz*/)
590 {
591     // QAndroidEventDispatcherStopper is stopped when the user uses the task manager to kill the application
592     if (QAndroidEventDispatcherStopper::instance()->stopped()) {
593         QAndroidEventDispatcherStopper::instance()->startAll();
594         QCoreApplication::quit();
595         QAndroidEventDispatcherStopper::instance()->goingToStop(false);
596     }
597 
598     sem_wait(&m_terminateSemaphore);
599     sem_destroy(&m_terminateSemaphore);
600 
601     env->DeleteGlobalRef(m_applicationClass);
602     env->DeleteGlobalRef(m_classLoaderObject);
603     if (m_resourcesObj)
604         env->DeleteGlobalRef(m_resourcesObj);
605     if (m_activityObject)
606         env->DeleteGlobalRef(m_activityObject);
607     if (m_serviceObject)
608         env->DeleteGlobalRef(m_serviceObject);
609     if (m_bitmapClass)
610         env->DeleteGlobalRef(m_bitmapClass);
611     if (m_ARGB_8888_BitmapConfigValue)
612         env->DeleteGlobalRef(m_ARGB_8888_BitmapConfigValue);
613     if (m_RGB_565_BitmapConfigValue)
614         env->DeleteGlobalRef(m_RGB_565_BitmapConfigValue);
615     if (m_bitmapDrawableClass)
616         env->DeleteGlobalRef(m_bitmapDrawableClass);
617     if (m_assets)
618         env->DeleteGlobalRef(m_assets);
619     m_androidPlatformIntegration = nullptr;
620     delete m_androidAssetsFileEngineHandler;
621     m_androidAssetsFileEngineHandler = nullptr;
622     sem_post(&m_exitSemaphore);
623 }
624 
setSurface(JNIEnv * env,jobject,jint id,jobject jSurface,jint w,jint h)625 static void setSurface(JNIEnv *env, jobject /*thiz*/, jint id, jobject jSurface, jint w, jint h)
626 {
627     QMutexLocker lock(&m_surfacesMutex);
628     const auto &it = m_surfaces.find(id);
629     if (it == m_surfaces.end())
630         return;
631 
632     auto surfaceClient = it.value();
633     if (surfaceClient)
634         surfaceClient->surfaceChanged(env, jSurface, w, h);
635 }
636 
setDisplayMetrics(JNIEnv *,jclass,jint widthPixels,jint heightPixels,jint desktopWidthPixels,jint desktopHeightPixels,jdouble xdpi,jdouble ydpi,jdouble scaledDensity,jdouble density)637 static void setDisplayMetrics(JNIEnv */*env*/, jclass /*clazz*/,
638                             jint widthPixels, jint heightPixels,
639                             jint desktopWidthPixels, jint desktopHeightPixels,
640                             jdouble xdpi, jdouble ydpi,
641                             jdouble scaledDensity, jdouble density)
642 {
643     // Android does not give us the correct screen size for immersive mode, but
644     // the surface does have the right size
645 
646     widthPixels = qMax(widthPixels, desktopWidthPixels);
647     heightPixels = qMax(heightPixels, desktopHeightPixels);
648 
649     m_desktopWidthPixels = desktopWidthPixels;
650     m_desktopHeightPixels = desktopHeightPixels;
651     m_scaledDensity = scaledDensity;
652     m_density = density;
653 
654     QMutexLocker lock(&m_platformMutex);
655     if (!m_androidPlatformIntegration) {
656         QAndroidPlatformIntegration::setDefaultDisplayMetrics(desktopWidthPixels,
657                                                               desktopHeightPixels,
658                                                               qRound(double(widthPixels)  / xdpi * 25.4),
659                                                               qRound(double(heightPixels) / ydpi * 25.4),
660                                                               widthPixels,
661                                                               heightPixels);
662     } else {
663         m_androidPlatformIntegration->setDisplayMetrics(qRound(double(widthPixels)  / xdpi * 25.4),
664                                                         qRound(double(heightPixels) / ydpi * 25.4));
665         m_androidPlatformIntegration->setScreenSize(widthPixels, heightPixels);
666         m_androidPlatformIntegration->setDesktopSize(desktopWidthPixels, desktopHeightPixels);
667     }
668 }
669 
updateWindow(JNIEnv *,jobject)670 static void updateWindow(JNIEnv */*env*/, jobject /*thiz*/)
671 {
672     if (!m_androidPlatformIntegration)
673         return;
674 
675     if (QGuiApplication::instance() != nullptr) {
676         const auto tlw = QGuiApplication::topLevelWindows();
677         for (QWindow *w : tlw) {
678 
679             // Skip non-platform windows, e.g., offscreen windows.
680             if (!w->handle())
681                 continue;
682 
683             QRect availableGeometry = w->screen()->availableGeometry();
684             if (w->geometry().width() > 0 && w->geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0)
685                 QWindowSystemInterface::handleExposeEvent(w, QRegion(QRect(QPoint(), w->geometry().size())));
686         }
687     }
688 
689     QAndroidPlatformScreen *screen = static_cast<QAndroidPlatformScreen *>(m_androidPlatformIntegration->screen());
690     if (screen->rasterSurfaces())
691         QMetaObject::invokeMethod(screen, "setDirty", Qt::QueuedConnection, Q_ARG(QRect,screen->geometry()));
692 }
693 
updateApplicationState(JNIEnv *,jobject,jint state)694 static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state)
695 {
696     QMutexLocker lock(&m_platformMutex);
697     if (!m_main || !m_androidPlatformIntegration) {
698         m_pendingApplicationState = state;
699         return;
700     }
701 
702     // We're about to call user code from the Android thread, since we don't know
703     //the side effects we'll unlock first!
704     lock.unlock();
705     if (state == Qt::ApplicationActive)
706         QtAndroidPrivate::handleResume();
707     else if (state == Qt::ApplicationInactive)
708         QtAndroidPrivate::handlePause();
709     lock.relock();
710     if (!m_androidPlatformIntegration)
711         return;
712 
713     if (state <= Qt::ApplicationInactive) {
714         // NOTE: sometimes we will receive two consecutive suspended notifications,
715         // In the second suspended notification, QWindowSystemInterface::flushWindowSystemEvents()
716         // will deadlock since the dispatcher has been stopped in the first suspended notification.
717         // To avoid the deadlock we simply return if we found the event dispatcher has been stopped.
718         if (QAndroidEventDispatcherStopper::instance()->stopped())
719             return;
720 
721         // Don't send timers and sockets events anymore if we are going to hide all windows
722         QAndroidEventDispatcherStopper::instance()->goingToStop(true);
723         QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state));
724         if (state == Qt::ApplicationSuspended)
725             QAndroidEventDispatcherStopper::instance()->stopAll();
726     } else {
727         QAndroidEventDispatcherStopper::instance()->startAll();
728         QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state));
729         QAndroidEventDispatcherStopper::instance()->goingToStop(false);
730     }
731 }
732 
handleOrientationChanged(JNIEnv *,jobject,jint newRotation,jint nativeOrientation)733 static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newRotation, jint nativeOrientation)
734 {
735     // Array of orientations rotated in 90 degree increments, counterclockwise
736     // (same direction as Android measures angles)
737     static const Qt::ScreenOrientation orientations[] = {
738         Qt::PortraitOrientation,
739         Qt::LandscapeOrientation,
740         Qt::InvertedPortraitOrientation,
741         Qt::InvertedLandscapeOrientation
742     };
743 
744     // The Android API defines the following constants:
745     // ROTATION_0 :   0
746     // ROTATION_90 :  1
747     // ROTATION_180 : 2
748     // ROTATION_270 : 3
749     // ORIENTATION_PORTRAIT :  1
750     // ORIENTATION_LANDSCAPE : 2
751 
752     // and newRotation is how much the current orientation is rotated relative to nativeOrientation
753 
754     // which means that we can be really clever here :)
755     Qt::ScreenOrientation screenOrientation = orientations[(nativeOrientation - 1 + newRotation) % 4];
756     Qt::ScreenOrientation native = orientations[nativeOrientation - 1];
757 
758     QAndroidPlatformIntegration::setScreenOrientation(screenOrientation, native);
759     QMutexLocker lock(&m_platformMutex);
760     if (m_androidPlatformIntegration) {
761         QPlatformScreen *screen = m_androidPlatformIntegration->screen();
762         QWindowSystemInterface::handleScreenOrientationChange(screen->screen(),
763                                                               screenOrientation);
764     }
765 }
766 
onActivityResult(JNIEnv *,jclass,jint requestCode,jint resultCode,jobject data)767 static void onActivityResult(JNIEnv */*env*/, jclass /*cls*/,
768                              jint requestCode,
769                              jint resultCode,
770                              jobject data)
771 {
772     QtAndroidPrivate::handleActivityResult(requestCode, resultCode, data);
773 }
774 
onNewIntent(JNIEnv * env,jclass,jobject data)775 static void onNewIntent(JNIEnv *env, jclass /*cls*/, jobject data)
776 {
777     QtAndroidPrivate::handleNewIntent(env, data);
778 }
779 
onBind(JNIEnv *,jclass,jobject intent)780 static jobject onBind(JNIEnv */*env*/, jclass /*cls*/, jobject intent)
781 {
782     return QtAndroidPrivate::callOnBindListener(intent);
783 }
784 
785 static JNINativeMethod methods[] = {
786     {"startQtAndroidPlugin", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)startQtAndroidPlugin},
787     {"startQtApplication", "()V", (void *)startQtApplication},
788     {"quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin},
789     {"quitQtCoreApplication", "()V", (void *)quitQtCoreApplication},
790     {"terminateQt", "()V", (void *)terminateQt},
791     {"waitForServiceSetup", "()V", (void *)waitForServiceSetup},
792     {"setDisplayMetrics", "(IIIIDDDD)V", (void *)setDisplayMetrics},
793     {"setSurface", "(ILjava/lang/Object;II)V", (void *)setSurface},
794     {"updateWindow", "()V", (void *)updateWindow},
795     {"updateApplicationState", "(I)V", (void *)updateApplicationState},
796     {"handleOrientationChanged", "(II)V", (void *)handleOrientationChanged},
797     {"onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult},
798     {"onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent},
799     {"onBind", "(Landroid/content/Intent;)Landroid/os/IBinder;", (void *)onBind}
800 };
801 
802 #define FIND_AND_CHECK_CLASS(CLASS_NAME) \
803 clazz = env->FindClass(CLASS_NAME); \
804 if (!clazz) { \
805     __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_classErrorMsg, CLASS_NAME); \
806     return JNI_FALSE; \
807 }
808 
809 #define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \
810 VAR = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \
811 if (!VAR) { \
812     __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \
813     return JNI_FALSE; \
814 }
815 
816 #define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \
817 VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \
818 if (!VAR) { \
819     __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \
820     return JNI_FALSE; \
821 }
822 
823 #define GET_AND_CHECK_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \
824 VAR = env->GetFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \
825 if (!VAR) { \
826     __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \
827     return JNI_FALSE; \
828 }
829 
830 #define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \
831 VAR = env->GetStaticFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \
832 if (!VAR) { \
833     __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \
834     return JNI_FALSE; \
835 }
836 
registerNatives(JNIEnv * env)837 static int registerNatives(JNIEnv *env)
838 {
839     jclass clazz;
840     FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/QtNative");
841     m_applicationClass = static_cast<jclass>(env->NewGlobalRef(clazz));
842 
843     if (env->RegisterNatives(m_applicationClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
844         __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed");
845         return JNI_FALSE;
846     }
847 
848     GET_AND_CHECK_STATIC_METHOD(m_createSurfaceMethodID, m_applicationClass, "createSurface", "(IZIIIII)V");
849     GET_AND_CHECK_STATIC_METHOD(m_setSurfaceGeometryMethodID, m_applicationClass, "setSurfaceGeometry", "(IIIII)V");
850     GET_AND_CHECK_STATIC_METHOD(m_destroySurfaceMethodID, m_applicationClass, "destroySurface", "(I)V");
851 
852     jmethodID methodID;
853     GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;");
854     jobject activityObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
855     GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "service", "()Landroid/app/Service;");
856     jobject serviceObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
857     GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "classLoader", "()Ljava/lang/ClassLoader;");
858     m_classLoaderObject = env->NewGlobalRef(env->CallStaticObjectMethod(m_applicationClass, methodID));
859     clazz = env->GetObjectClass(m_classLoaderObject);
860     GET_AND_CHECK_METHOD(m_loadClassMethodID, clazz, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
861     if (serviceObject)
862         m_serviceObject = env->NewGlobalRef(serviceObject);
863 
864     if (activityObject)
865         m_activityObject = env->NewGlobalRef(activityObject);
866 
867     jobject object = activityObject ? activityObject : serviceObject;
868     if (object) {
869         FIND_AND_CHECK_CLASS("android/content/ContextWrapper");
870         GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;");
871         m_assets = env->NewGlobalRef(env->CallObjectMethod(object, methodID));
872         m_assetManager = AAssetManager_fromJava(env, m_assets);
873 
874         GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;");
875         m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(object, methodID));
876 
877         FIND_AND_CHECK_CLASS("android/graphics/Bitmap");
878         m_bitmapClass = static_cast<jclass>(env->NewGlobalRef(clazz));
879         GET_AND_CHECK_STATIC_METHOD(m_createBitmapMethodID, m_bitmapClass
880                                     , "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
881         FIND_AND_CHECK_CLASS("android/graphics/Bitmap$Config");
882         jfieldID fieldId;
883         GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");
884         m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
885         GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "RGB_565", "Landroid/graphics/Bitmap$Config;");
886         m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
887 
888         FIND_AND_CHECK_CLASS("android/graphics/drawable/BitmapDrawable");
889         m_bitmapDrawableClass = static_cast<jclass>(env->NewGlobalRef(clazz));
890         GET_AND_CHECK_METHOD(m_bitmapDrawableConstructorMethodID,
891                              m_bitmapDrawableClass,
892                              "<init>",
893                              "(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V");
894     }
895 
896     return JNI_TRUE;
897 }
898 
899 QT_END_NAMESPACE
900 
JNI_OnLoad(JavaVM * vm,void *)901 Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
902 {
903     static bool initialized = false;
904     if (initialized)
905         return JNI_VERSION_1_6;
906     initialized = true;
907 
908     QT_USE_NAMESPACE
909     typedef union {
910         JNIEnv *nativeEnvironment;
911         void *venv;
912     } UnionJNIEnvToVoid;
913 
914     UnionJNIEnvToVoid uenv;
915     uenv.venv = nullptr;
916     m_javaVM = nullptr;
917 
918     if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
919         __android_log_print(ANDROID_LOG_FATAL, "Qt", "GetEnv failed");
920         return -1;
921     }
922 
923     JNIEnv *env = uenv.nativeEnvironment;
924     if (!registerNatives(env)
925             || !QtAndroidInput::registerNatives(env)
926             || !QtAndroidMenu::registerNatives(env)
927             || !QtAndroidAccessibility::registerNatives(env)
928             || !QtAndroidDialogHelpers::registerNatives(env)) {
929         __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed");
930         return -1;
931     }
932     QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
933 
934     m_javaVM = vm;
935     // attach qt main thread data to this thread
936     QObject threadSetter;
937     if (threadSetter.thread())
938         threadSetter.thread()->setObjectName("QtMainLoopThread");
939     __android_log_print(ANDROID_LOG_INFO, "Qt", "qt started");
940     return JNI_VERSION_1_6;
941 }
942