1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qjnihelpers_p.h"
41 #include "qjni_p.h"
42 #include "qmutex.h"
43 #include "qlist.h"
44 #include "qsemaphore.h"
45 #include "qsharedpointer.h"
46 #include "qvector.h"
47 #include "qthread.h"
48 #include "qcoreapplication.h"
49 #include <QtCore/qrunnable.h>
50 
51 #include <deque>
52 #include <memory>
53 
54 QT_BEGIN_NAMESPACE
55 
56 namespace QtAndroidPrivate {
57     // *Listener virtual function implementations.
58     // Defined out-of-line to pin the vtable/type_info.
~ActivityResultListener()59     ActivityResultListener::~ActivityResultListener() {}
~NewIntentListener()60     NewIntentListener::~NewIntentListener() {}
~ResumePauseListener()61     ResumePauseListener::~ResumePauseListener() {}
handlePause()62     void ResumePauseListener::handlePause() {}
handleResume()63     void ResumePauseListener::handleResume() {}
~GenericMotionEventListener()64     GenericMotionEventListener::~GenericMotionEventListener() {}
~KeyEventListener()65     KeyEventListener::~KeyEventListener() {}
66 }
67 
68 static JavaVM *g_javaVM = nullptr;
69 static jobject g_jActivity = nullptr;
70 static jobject g_jService = nullptr;
71 static jobject g_jClassLoader = nullptr;
72 static jint g_androidSdkVersion = 0;
73 static jclass g_jNativeClass = nullptr;
74 static jmethodID g_runPendingCppRunnablesMethodID = nullptr;
75 static jmethodID g_hideSplashScreenMethodID = nullptr;
76 Q_GLOBAL_STATIC(std::deque<QtAndroidPrivate::Runnable>, g_pendingRunnables);
77 static QBasicMutex g_pendingRunnablesMutex;
78 
79 Q_GLOBAL_STATIC_WITH_ARGS(QtAndroidPrivate::OnBindListener*, g_onBindListener, (nullptr));
80 Q_GLOBAL_STATIC(QMutex, g_onBindListenerMutex);
81 Q_GLOBAL_STATIC(QSemaphore, g_waitForServiceSetupSemaphore);
82 Q_GLOBAL_STATIC(QAtomicInt, g_serviceSetupLockers);
83 
84 class PermissionsResultClass : public QObject
85 {
86     Q_OBJECT
87 public:
PermissionsResultClass(const QtAndroidPrivate::PermissionsResultFunc & func)88     PermissionsResultClass(const QtAndroidPrivate::PermissionsResultFunc &func) : m_func(func) {}
sendResult(const QtAndroidPrivate::PermissionsHash & result)89     Q_INVOKABLE void sendResult(const QtAndroidPrivate::PermissionsHash &result) { m_func(result); delete this;}
90 
91 private:
92     QtAndroidPrivate::PermissionsResultFunc m_func;
93 };
94 
95 typedef QHash<int, PermissionsResultClass*> PendingPermissionRequestsHash;
96 Q_GLOBAL_STATIC(PendingPermissionRequestsHash, g_pendingPermissionRequests);
97 static QBasicMutex g_pendingPermissionRequestsMutex;
nextRequestCode()98 static int nextRequestCode()
99 {
100     static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0);
101     return counter.fetchAndAddRelaxed(1);
102 }
103 
104 // function called from Java from Android UI thread
runPendingCppRunnables(JNIEnv *,jobject)105 static void runPendingCppRunnables(JNIEnv */*env*/, jobject /*obj*/)
106 {
107     for (;;) { // run all posted runnables
108         QMutexLocker locker(&g_pendingRunnablesMutex);
109         if (g_pendingRunnables->empty()) {
110             break;
111         }
112         QtAndroidPrivate::Runnable runnable(std::move(g_pendingRunnables->front()));
113         g_pendingRunnables->pop_front();
114         locker.unlock();
115         runnable(); // run it outside the sync block!
116     }
117 }
118 
119 namespace {
120     struct GenericMotionEventListeners {
121         QMutex mutex;
122         QVector<QtAndroidPrivate::GenericMotionEventListener *> listeners;
123     };
124 
125     enum {
126         PERMISSION_GRANTED = 0
127     };
128 }
Q_GLOBAL_STATIC(GenericMotionEventListeners,g_genericMotionEventListeners)129 Q_GLOBAL_STATIC(GenericMotionEventListeners, g_genericMotionEventListeners)
130 
131 static void sendRequestPermissionsResult(JNIEnv *env, jobject /*obj*/, jint requestCode,
132                                          jobjectArray permissions, jintArray grantResults)
133 {
134     QMutexLocker locker(&g_pendingPermissionRequestsMutex);
135     auto it = g_pendingPermissionRequests->find(requestCode);
136     if (it == g_pendingPermissionRequests->end()) {
137         // show an error or something ?
138         return;
139     }
140     auto request = *it;
141     g_pendingPermissionRequests->erase(it);
142     locker.unlock();
143 
144     Qt::ConnectionType connection = QThread::currentThread() == request->thread() ? Qt::DirectConnection : Qt::QueuedConnection;
145     QtAndroidPrivate::PermissionsHash hash;
146     const int size = env->GetArrayLength(permissions);
147     std::unique_ptr<jint[]> results(new jint[size]);
148     env->GetIntArrayRegion(grantResults, 0, size, results.get());
149     for (int i = 0 ; i < size; ++i) {
150         const auto &permission = QJNIObjectPrivate(env->GetObjectArrayElement(permissions, i)).toString();
151         auto value = results[i] == PERMISSION_GRANTED ?
152                             QtAndroidPrivate::PermissionsResult::Granted :
153                             QtAndroidPrivate::PermissionsResult::Denied;
154         hash[permission] = value;
155     }
156     QMetaObject::invokeMethod(request, "sendResult", connection, Q_ARG(QtAndroidPrivate::PermissionsHash, hash));
157 }
158 
dispatchGenericMotionEvent(JNIEnv *,jclass,jobject event)159 static jboolean dispatchGenericMotionEvent(JNIEnv *, jclass, jobject event)
160 {
161     jboolean ret = JNI_FALSE;
162     QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
163     for (auto *listener : qAsConst(g_genericMotionEventListeners()->listeners))
164         ret |= listener->handleGenericMotionEvent(event);
165     return ret;
166 }
167 
168 namespace {
169     struct KeyEventListeners {
170         QMutex mutex;
171         QVector<QtAndroidPrivate::KeyEventListener *> listeners;
172     };
173 }
Q_GLOBAL_STATIC(KeyEventListeners,g_keyEventListeners)174 Q_GLOBAL_STATIC(KeyEventListeners, g_keyEventListeners)
175 
176 static jboolean dispatchKeyEvent(JNIEnv *, jclass, jobject event)
177 {
178     jboolean ret = JNI_FALSE;
179     QMutexLocker locker(&g_keyEventListeners()->mutex);
180     for (auto *listener : qAsConst(g_keyEventListeners()->listeners))
181         ret |= listener->handleKeyEvent(event);
182     return ret;
183 }
184 
185 namespace {
186     class ActivityResultListeners
187     {
188     public:
189         QMutex mutex;
190         QList<QtAndroidPrivate::ActivityResultListener *> listeners;
191     };
192 }
193 
Q_GLOBAL_STATIC(ActivityResultListeners,g_activityResultListeners)194 Q_GLOBAL_STATIC(ActivityResultListeners, g_activityResultListeners)
195 
196 void QtAndroidPrivate::registerActivityResultListener(ActivityResultListener *listener)
197 {
198     QMutexLocker locker(&g_activityResultListeners()->mutex);
199     g_activityResultListeners()->listeners.append(listener);
200 }
201 
unregisterActivityResultListener(ActivityResultListener * listener)202 void QtAndroidPrivate::unregisterActivityResultListener(ActivityResultListener *listener)
203 {
204     QMutexLocker locker(&g_activityResultListeners()->mutex);
205     g_activityResultListeners()->listeners.removeAll(listener);
206 }
207 
handleActivityResult(jint requestCode,jint resultCode,jobject data)208 void QtAndroidPrivate::handleActivityResult(jint requestCode, jint resultCode, jobject data)
209 {
210     QMutexLocker locker(&g_activityResultListeners()->mutex);
211     const QList<QtAndroidPrivate::ActivityResultListener *> &listeners = g_activityResultListeners()->listeners;
212     for (int i=0; i<listeners.size(); ++i) {
213         if (listeners.at(i)->handleActivityResult(requestCode, resultCode, data))
214             break;
215     }
216 }
217 
218 namespace {
219     class NewIntentListeners
220     {
221     public:
222         QMutex mutex;
223         QList<QtAndroidPrivate::NewIntentListener *> listeners;
224     };
225 }
226 
Q_GLOBAL_STATIC(NewIntentListeners,g_newIntentListeners)227 Q_GLOBAL_STATIC(NewIntentListeners, g_newIntentListeners)
228 
229 void QtAndroidPrivate::registerNewIntentListener(NewIntentListener *listener)
230 {
231     QMutexLocker locker(&g_newIntentListeners()->mutex);
232     g_newIntentListeners()->listeners.append(listener);
233 }
234 
unregisterNewIntentListener(NewIntentListener * listener)235 void QtAndroidPrivate::unregisterNewIntentListener(NewIntentListener *listener)
236 {
237     QMutexLocker locker(&g_newIntentListeners()->mutex);
238     g_newIntentListeners()->listeners.removeAll(listener);
239 }
240 
handleNewIntent(JNIEnv * env,jobject intent)241 void QtAndroidPrivate::handleNewIntent(JNIEnv *env, jobject intent)
242 {
243     QMutexLocker locker(&g_newIntentListeners()->mutex);
244     const QList<QtAndroidPrivate::NewIntentListener *> &listeners = g_newIntentListeners()->listeners;
245     for (int i=0; i<listeners.size(); ++i) {
246         if (listeners.at(i)->handleNewIntent(env, intent))
247             break;
248     }
249 }
250 
251 namespace {
252     class ResumePauseListeners
253     {
254     public:
255         QMutex mutex;
256         QList<QtAndroidPrivate::ResumePauseListener *> listeners;
257     };
258 }
259 
Q_GLOBAL_STATIC(ResumePauseListeners,g_resumePauseListeners)260 Q_GLOBAL_STATIC(ResumePauseListeners, g_resumePauseListeners)
261 
262 void QtAndroidPrivate::registerResumePauseListener(ResumePauseListener *listener)
263 {
264     QMutexLocker locker(&g_resumePauseListeners()->mutex);
265     g_resumePauseListeners()->listeners.append(listener);
266 }
267 
unregisterResumePauseListener(ResumePauseListener * listener)268 void QtAndroidPrivate::unregisterResumePauseListener(ResumePauseListener *listener)
269 {
270     QMutexLocker locker(&g_resumePauseListeners()->mutex);
271     g_resumePauseListeners()->listeners.removeAll(listener);
272 }
273 
handlePause()274 void QtAndroidPrivate::handlePause()
275 {
276     QMutexLocker locker(&g_resumePauseListeners()->mutex);
277     const QList<QtAndroidPrivate::ResumePauseListener *> &listeners = g_resumePauseListeners()->listeners;
278     for (int i=0; i<listeners.size(); ++i)
279         listeners.at(i)->handlePause();
280 }
281 
handleResume()282 void QtAndroidPrivate::handleResume()
283 {
284     QMutexLocker locker(&g_resumePauseListeners()->mutex);
285     const QList<QtAndroidPrivate::ResumePauseListener *> &listeners = g_resumePauseListeners()->listeners;
286     for (int i=0; i<listeners.size(); ++i)
287         listeners.at(i)->handleResume();
288 }
289 
exceptionCheck(JNIEnv * env)290 static inline bool exceptionCheck(JNIEnv *env)
291 {
292     if (env->ExceptionCheck()) {
293 #ifdef QT_DEBUG
294         env->ExceptionDescribe();
295 #endif // QT_DEBUG
296         env->ExceptionClear();
297         return true;
298     }
299 
300     return false;
301 }
302 
setAndroidSdkVersion(JNIEnv * env)303 static void setAndroidSdkVersion(JNIEnv *env)
304 {
305     jclass androidVersionClass = env->FindClass("android/os/Build$VERSION");
306     if (exceptionCheck(env))
307         return;
308 
309     jfieldID androidSDKFieldID = env->GetStaticFieldID(androidVersionClass, "SDK_INT", "I");
310     if (exceptionCheck(env))
311         return;
312 
313     g_androidSdkVersion = env->GetStaticIntField(androidVersionClass, androidSDKFieldID);
314 }
315 
setNativeActivity(JNIEnv * env,jclass,jobject activity)316 static void setNativeActivity(JNIEnv *env, jclass, jobject activity)
317 {
318     if (g_jActivity != 0)
319         env->DeleteGlobalRef(g_jActivity);
320 
321     if (activity != 0) {
322         g_jActivity = env->NewGlobalRef(activity);
323         env->DeleteLocalRef(activity);
324     } else {
325         g_jActivity = 0;
326     }
327 }
328 
setNativeService(JNIEnv * env,jclass,jobject service)329 static void setNativeService(JNIEnv *env, jclass, jobject service)
330 {
331     if (g_jService != 0)
332         env->DeleteGlobalRef(g_jService);
333 
334     if (service != 0) {
335         g_jService = env->NewGlobalRef(service);
336         env->DeleteLocalRef(service);
337     } else {
338         g_jService = 0;
339     }
340 }
341 
initJNI(JavaVM * vm,JNIEnv * env)342 jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
343 {
344     jclass jQtNative = env->FindClass("org/qtproject/qt5/android/QtNative");
345 
346     if (exceptionCheck(env))
347         return JNI_ERR;
348 
349     jmethodID activityMethodID = env->GetStaticMethodID(jQtNative,
350                                                         "activity",
351                                                         "()Landroid/app/Activity;");
352 
353     if (exceptionCheck(env))
354         return JNI_ERR;
355 
356     jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
357 
358     if (exceptionCheck(env))
359         return JNI_ERR;
360 
361     jmethodID serviceMethodID = env->GetStaticMethodID(jQtNative,
362                                                        "service",
363                                                        "()Landroid/app/Service;");
364 
365     if (exceptionCheck(env))
366         return JNI_ERR;
367 
368     jobject service = env->CallStaticObjectMethod(jQtNative, serviceMethodID);
369 
370     if (exceptionCheck(env))
371         return JNI_ERR;
372 
373     jmethodID classLoaderMethodID = env->GetStaticMethodID(jQtNative,
374                                                            "classLoader",
375                                                            "()Ljava/lang/ClassLoader;");
376 
377     if (exceptionCheck(env))
378         return JNI_ERR;
379 
380     jobject classLoader = env->CallStaticObjectMethod(jQtNative, classLoaderMethodID);
381     if (exceptionCheck(env))
382         return JNI_ERR;
383 
384     setAndroidSdkVersion(env);
385 
386     g_jClassLoader = env->NewGlobalRef(classLoader);
387     env->DeleteLocalRef(classLoader);
388     if (activity) {
389         g_jActivity = env->NewGlobalRef(activity);
390         env->DeleteLocalRef(activity);
391     }
392     if (service) {
393         g_jService = env->NewGlobalRef(service);
394         env->DeleteLocalRef(service);
395     }
396     g_javaVM = vm;
397 
398     static const JNINativeMethod methods[] = {
399         {"runPendingCppRunnables", "()V",  reinterpret_cast<void *>(runPendingCppRunnables)},
400         {"dispatchGenericMotionEvent", "(Landroid/view/MotionEvent;)Z", reinterpret_cast<void *>(dispatchGenericMotionEvent)},
401         {"dispatchKeyEvent", "(Landroid/view/KeyEvent;)Z", reinterpret_cast<void *>(dispatchKeyEvent)},
402         {"setNativeActivity", "(Landroid/app/Activity;)V", reinterpret_cast<void *>(setNativeActivity)},
403         {"setNativeService", "(Landroid/app/Service;)V", reinterpret_cast<void *>(setNativeService)},
404         {"sendRequestPermissionsResult", "(I[Ljava/lang/String;[I)V", reinterpret_cast<void *>(sendRequestPermissionsResult)},
405     };
406 
407     const bool regOk = (env->RegisterNatives(jQtNative, methods, sizeof(methods) / sizeof(methods[0])) == JNI_OK);
408 
409     if (!regOk && exceptionCheck(env))
410         return JNI_ERR;
411 
412     g_runPendingCppRunnablesMethodID = env->GetStaticMethodID(jQtNative,
413                                                        "runPendingCppRunnablesOnAndroidThread",
414                                                        "()V");
415     g_hideSplashScreenMethodID = env->GetStaticMethodID(jQtNative, "hideSplashScreen", "(I)V");
416     g_jNativeClass = static_cast<jclass>(env->NewGlobalRef(jQtNative));
417     env->DeleteLocalRef(jQtNative);
418 
419     qRegisterMetaType<QtAndroidPrivate::PermissionsHash>();
420     return JNI_OK;
421 }
422 
423 
activity()424 jobject QtAndroidPrivate::activity()
425 {
426     return g_jActivity;
427 }
428 
service()429 jobject QtAndroidPrivate::service()
430 {
431     return g_jService;
432 }
433 
context()434 jobject QtAndroidPrivate::context()
435 {
436     if (g_jActivity)
437         return g_jActivity;
438     if (g_jService)
439         return g_jService;
440 
441     return 0;
442 }
443 
javaVM()444 JavaVM *QtAndroidPrivate::javaVM()
445 {
446     return g_javaVM;
447 }
448 
classLoader()449 jobject QtAndroidPrivate::classLoader()
450 {
451     return g_jClassLoader;
452 }
453 
androidSdkVersion()454 jint QtAndroidPrivate::androidSdkVersion()
455 {
456     return g_androidSdkVersion;
457 }
458 
runOnUiThread(QRunnable * runnable,JNIEnv * env)459 void QtAndroidPrivate::runOnUiThread(QRunnable *runnable, JNIEnv *env)
460 {
461     runOnAndroidThread([runnable]() {
462         runnable->run();
463         if (runnable->autoDelete())
464             delete runnable;
465     }, env);
466 }
467 
runOnAndroidThread(const QtAndroidPrivate::Runnable & runnable,JNIEnv * env)468 void QtAndroidPrivate::runOnAndroidThread(const QtAndroidPrivate::Runnable &runnable, JNIEnv *env)
469 {
470     QMutexLocker locker(&g_pendingRunnablesMutex);
471     const bool triggerRun = g_pendingRunnables->empty();
472     g_pendingRunnables->push_back(runnable);
473     locker.unlock();
474     if (triggerRun)
475         env->CallStaticVoidMethod(g_jNativeClass, g_runPendingCppRunnablesMethodID);
476 }
477 
waitForSemaphore(int timeoutMs,QSharedPointer<QSemaphore> sem)478 static bool waitForSemaphore(int timeoutMs, QSharedPointer<QSemaphore> sem)
479 {
480     while (timeoutMs > 0) {
481         if (sem->tryAcquire(1, 10))
482             return true;
483         timeoutMs -= 10;
484         QCoreApplication::processEvents();
485     }
486     return false;
487 }
488 
runOnAndroidThreadSync(const QtAndroidPrivate::Runnable & runnable,JNIEnv * env,int timeoutMs)489 void QtAndroidPrivate::runOnAndroidThreadSync(const QtAndroidPrivate::Runnable &runnable, JNIEnv *env, int timeoutMs)
490 {
491     QSharedPointer<QSemaphore> sem(new QSemaphore);
492     runOnAndroidThread([&runnable, sem]{
493         runnable();
494         sem->release();
495     }, env);
496     waitForSemaphore(timeoutMs, sem);
497 }
498 
requestPermissions(JNIEnv * env,const QStringList & permissions,const QtAndroidPrivate::PermissionsResultFunc & callbackFunc,bool directCall)499 void QtAndroidPrivate::requestPermissions(JNIEnv *env, const QStringList &permissions, const QtAndroidPrivate::PermissionsResultFunc &callbackFunc, bool directCall)
500 {
501     if (androidSdkVersion() < 23 || !activity()) {
502         QHash<QString, QtAndroidPrivate::PermissionsResult> res;
503         for (const auto &perm : permissions)
504             res[perm] = checkPermission(perm);
505         callbackFunc(res);
506         return;
507     }
508     // Check API 23+ permissions
509     const int requestCode = nextRequestCode();
510     if (!directCall) {
511         QMutexLocker locker(&g_pendingPermissionRequestsMutex);
512         (*g_pendingPermissionRequests)[requestCode] = new PermissionsResultClass(callbackFunc);
513     }
514 
515     runOnAndroidThread([permissions, callbackFunc, requestCode, directCall] {
516         if (directCall) {
517             QMutexLocker locker(&g_pendingPermissionRequestsMutex);
518             (*g_pendingPermissionRequests)[requestCode] = new PermissionsResultClass(callbackFunc);
519         }
520 
521         QJNIEnvironmentPrivate env;
522         auto array = env->NewObjectArray(permissions.size(), env->FindClass("java/lang/String"), nullptr);
523         int index = 0;
524         for (const auto &perm : permissions)
525             env->SetObjectArrayElement(array, index++, QJNIObjectPrivate::fromString(perm).object());
526         QJNIObjectPrivate(activity()).callMethod<void>("requestPermissions", "([Ljava/lang/String;I)V", array, requestCode);
527         env->DeleteLocalRef(array);
528     }, env);
529 }
530 
requestPermissionsSync(JNIEnv * env,const QStringList & permissions,int timeoutMs)531 QtAndroidPrivate::PermissionsHash QtAndroidPrivate::requestPermissionsSync(JNIEnv *env, const QStringList &permissions, int timeoutMs)
532 {
533     QSharedPointer<QHash<QString, QtAndroidPrivate::PermissionsResult>> res(new QHash<QString, QtAndroidPrivate::PermissionsResult>());
534     QSharedPointer<QSemaphore> sem(new QSemaphore);
535     requestPermissions(env, permissions, [sem, res](const QHash<QString, PermissionsResult> &result){
536         *res = result;
537         sem->release();
538     }, true);
539     if (waitForSemaphore(timeoutMs, sem))
540         return std::move(*res);
541     else // mustn't touch *res
542         return QHash<QString, QtAndroidPrivate::PermissionsResult>();
543 }
544 
checkPermission(const QString & permission)545 QtAndroidPrivate::PermissionsResult QtAndroidPrivate::checkPermission(const QString &permission)
546 {
547     const auto res = QJNIObjectPrivate::callStaticMethod<jint>("org/qtproject/qt5/android/QtNative",
548                                                                "checkSelfPermission",
549                                                                "(Ljava/lang/String;)I",
550                                                                QJNIObjectPrivate::fromString(permission).object());
551     return res == PERMISSION_GRANTED ? PermissionsResult::Granted : PermissionsResult::Denied;
552 }
553 
shouldShowRequestPermissionRationale(const QString & permission)554 bool QtAndroidPrivate::shouldShowRequestPermissionRationale(const QString &permission)
555 {
556     if (androidSdkVersion() < 23 || !activity())
557         return false;
558 
559     return QJNIObjectPrivate(activity()).callMethod<jboolean>("shouldShowRequestPermissionRationale", "(Ljava/lang/String;)Z",
560                                                               QJNIObjectPrivate::fromString(permission).object());
561 }
562 
registerGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener * listener)563 void QtAndroidPrivate::registerGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener)
564 {
565     QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
566     g_genericMotionEventListeners()->listeners.push_back(listener);
567 }
568 
unregisterGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener * listener)569 void QtAndroidPrivate::unregisterGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener)
570 {
571     QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
572     g_genericMotionEventListeners()->listeners.removeOne(listener);
573 }
574 
registerKeyEventListener(QtAndroidPrivate::KeyEventListener * listener)575 void QtAndroidPrivate::registerKeyEventListener(QtAndroidPrivate::KeyEventListener *listener)
576 {
577     QMutexLocker locker(&g_keyEventListeners()->mutex);
578     g_keyEventListeners()->listeners.push_back(listener);
579 }
580 
unregisterKeyEventListener(QtAndroidPrivate::KeyEventListener * listener)581 void QtAndroidPrivate::unregisterKeyEventListener(QtAndroidPrivate::KeyEventListener *listener)
582 {
583     QMutexLocker locker(&g_keyEventListeners()->mutex);
584     g_keyEventListeners()->listeners.removeOne(listener);
585 }
586 
hideSplashScreen(JNIEnv * env,int duration)587 void QtAndroidPrivate::hideSplashScreen(JNIEnv *env, int duration)
588 {
589     env->CallStaticVoidMethod(g_jNativeClass, g_hideSplashScreenMethodID, duration);
590 }
591 
waitForServiceSetup()592 void QtAndroidPrivate::waitForServiceSetup()
593 {
594     g_waitForServiceSetupSemaphore->acquire();
595 }
596 
acuqireServiceSetup(int flags)597 int QtAndroidPrivate::acuqireServiceSetup(int flags)
598 {
599     g_serviceSetupLockers->ref();
600     return flags;
601 }
602 
setOnBindListener(QtAndroidPrivate::OnBindListener * listener)603 void QtAndroidPrivate::setOnBindListener(QtAndroidPrivate::OnBindListener *listener)
604 {
605     QMutexLocker lock(g_onBindListenerMutex);
606     *g_onBindListener = listener;
607     if (!g_serviceSetupLockers->deref())
608         g_waitForServiceSetupSemaphore->release();
609 }
610 
callOnBindListener(jobject intent)611 jobject QtAndroidPrivate::callOnBindListener(jobject intent)
612 {
613     QMutexLocker lock(g_onBindListenerMutex);
614     if (*g_onBindListener)
615         return (*g_onBindListener)->onBind(intent);
616     return nullptr;
617 }
618 
619 QT_END_NAMESPACE
620 
621 #include "qjnihelpers.moc"
622