summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qjnihelpers.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qjnihelpers.cpp')
-rw-r--r--src/corelib/kernel/qjnihelpers.cpp390
1 files changed, 87 insertions, 303 deletions
diff --git a/src/corelib/kernel/qjnihelpers.cpp b/src/corelib/kernel/qjnihelpers.cpp
index f847bc7380..d900b74d37 100644
--- a/src/corelib/kernel/qjnihelpers.cpp
+++ b/src/corelib/kernel/qjnihelpers.cpp
@@ -1,54 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qcoreapplication.h"
-#include "qjnienvironment.h"
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#include "qjnihelpers_p.h"
+
+#include "qjnienvironment.h"
#include "qjniobject.h"
#include "qlist.h"
#include "qmutex.h"
#include "qsemaphore.h"
-#include "qsharedpointer.h"
-#include "qthread.h"
-
+#include "qreadwritelock.h"
#include <QtCore/private/qcoreapplication_p.h>
-#include <QtCore/qrunnable.h>
+#include <QtCore/private/qlocking_p.h>
#include <android/log.h>
#include <deque>
@@ -64,124 +26,53 @@ namespace QtAndroidPrivate {
ResumePauseListener::~ResumePauseListener() {}
void ResumePauseListener::handlePause() {}
void ResumePauseListener::handleResume() {}
- GenericMotionEventListener::~GenericMotionEventListener() {}
- KeyEventListener::~KeyEventListener() {}
}
static JavaVM *g_javaVM = nullptr;
static jobject g_jActivity = nullptr;
static jobject g_jService = nullptr;
static jobject g_jClassLoader = nullptr;
-static jint g_androidSdkVersion = 0;
-static jclass g_jNativeClass = nullptr;
-static jmethodID g_runPendingCppRunnablesMethodID = nullptr;
-Q_GLOBAL_STATIC(std::deque<QtAndroidPrivate::Runnable>, g_pendingRunnables);
-static QBasicMutex g_pendingRunnablesMutex;
-
-Q_GLOBAL_STATIC_WITH_ARGS(QtAndroidPrivate::OnBindListener*, g_onBindListener, (nullptr));
-Q_GLOBAL_STATIC(QMutex, g_onBindListenerMutex);
+
+Q_CONSTINIT static QtAndroidPrivate::OnBindListener *g_onBindListener;
+Q_CONSTINIT static QBasicMutex g_onBindListenerMutex;
Q_GLOBAL_STATIC(QSemaphore, g_waitForServiceSetupSemaphore);
-Q_GLOBAL_STATIC(QAtomicInt, g_serviceSetupLockers);
+Q_CONSTINIT static QBasicAtomicInt g_serviceSetupLockers = Q_BASIC_ATOMIC_INITIALIZER(0);
-class PermissionsResultClass : public QObject
-{
- Q_OBJECT
-public:
- PermissionsResultClass(const QtAndroidPrivate::PermissionsResultFunc &func) : m_func(func) {}
- Q_INVOKABLE void sendResult(const QtAndroidPrivate::PermissionsHash &result) { m_func(result); delete this;}
-
-private:
- QtAndroidPrivate::PermissionsResultFunc m_func;
-};
-
-typedef QHash<int, PermissionsResultClass*> PendingPermissionRequestsHash;
-Q_GLOBAL_STATIC(PendingPermissionRequestsHash, g_pendingPermissionRequests);
-static QBasicMutex g_pendingPermissionRequestsMutex;
-static int nextRequestCode()
-{
- static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0);
- return counter.fetchAndAddRelaxed(1);
-}
+Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex);
-// function called from Java from Android UI thread
-static void runPendingCppRunnables(JNIEnv */*env*/, jobject /*obj*/)
+static jboolean updateNativeActivity(JNIEnv *env, jclass = nullptr)
{
- for (;;) { // run all posted runnables
- QMutexLocker locker(&g_pendingRunnablesMutex);
- if (g_pendingRunnables->empty()) {
- break;
- }
- QtAndroidPrivate::Runnable runnable(std::move(g_pendingRunnables->front()));
- g_pendingRunnables->pop_front();
- locker.unlock();
- runnable(); // run it outside the sync block!
- }
-}
-namespace {
- struct GenericMotionEventListeners {
- QMutex mutex;
- QList<QtAndroidPrivate::GenericMotionEventListener *> listeners;
- };
+ jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative");
+ if (QJniEnvironment::checkAndClearExceptions(env))
+ return JNI_FALSE;
- enum {
- PERMISSION_GRANTED = 0
- };
-}
-Q_GLOBAL_STATIC(GenericMotionEventListeners, g_genericMotionEventListeners)
+ jmethodID activityMethodID =
+ env->GetStaticMethodID(jQtNative, "activity", "()Landroid/app/Activity;");
+ if (QJniEnvironment::checkAndClearExceptions(env))
+ return JNI_FALSE;
-static void sendRequestPermissionsResult(JNIEnv *env, jobject /*obj*/, jint requestCode,
- jobjectArray permissions, jintArray grantResults)
-{
- QMutexLocker locker(&g_pendingPermissionRequestsMutex);
- auto it = g_pendingPermissionRequests->find(requestCode);
- if (it == g_pendingPermissionRequests->end()) {
- // show an error or something ?
- return;
- }
- auto request = *it;
- g_pendingPermissionRequests->erase(it);
- locker.unlock();
-
- Qt::ConnectionType connection = QThread::currentThread() == request->thread() ? Qt::DirectConnection : Qt::QueuedConnection;
- QtAndroidPrivate::PermissionsHash hash;
- const int size = env->GetArrayLength(permissions);
- std::unique_ptr<jint[]> results(new jint[size]);
- env->GetIntArrayRegion(grantResults, 0, size, results.get());
- for (int i = 0 ; i < size; ++i) {
- const auto &permission = QJniObject(env->GetObjectArrayElement(permissions, i)).toString();
- auto value = results[i] == PERMISSION_GRANTED ?
- QtAndroidPrivate::PermissionsResult::Granted :
- QtAndroidPrivate::PermissionsResult::Denied;
- hash[permission] = value;
+ jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
+ if (QJniEnvironment::checkAndClearExceptions(env))
+ return JNI_FALSE;
+
+ QWriteLocker locker(g_updateMutex());
+
+ if (g_jActivity) {
+ env->DeleteGlobalRef(g_jActivity);
+ g_jActivity = nullptr;
}
- QMetaObject::invokeMethod(request, "sendResult", connection, Q_ARG(QtAndroidPrivate::PermissionsHash, hash));
-}
-static jboolean dispatchGenericMotionEvent(JNIEnv *, jclass, jobject event)
-{
- jboolean ret = JNI_FALSE;
- QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
- for (auto *listener : qAsConst(g_genericMotionEventListeners()->listeners))
- ret |= listener->handleGenericMotionEvent(event);
- return ret;
-}
+ if (activity) {
+ g_jActivity = env->NewGlobalRef(activity);
+ env->DeleteLocalRef(activity);
+ }
-namespace {
- struct KeyEventListeners {
- QMutex mutex;
- QList<QtAndroidPrivate::KeyEventListener *> listeners;
- };
-}
-Q_GLOBAL_STATIC(KeyEventListeners, g_keyEventListeners)
+ env->DeleteLocalRef(jQtNative);
+ if (QJniEnvironment::checkAndClearExceptions(env))
+ return JNI_FALSE;
-static jboolean dispatchKeyEvent(JNIEnv *, jclass, jobject event)
-{
- jboolean ret = JNI_FALSE;
- QMutexLocker locker(&g_keyEventListeners()->mutex);
- for (auto *listener : qAsConst(g_keyEventListeners()->listeners))
- ret |= listener->handleKeyEvent(event);
- return ret;
+ return JNI_TRUE;
}
namespace {
@@ -333,8 +224,6 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
- g_androidSdkVersion = QJniObject::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT");
-
g_jClassLoader = env->NewGlobalRef(classLoader);
env->DeleteLocalRef(classLoader);
if (activity) {
@@ -347,49 +236,59 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
}
static const JNINativeMethod methods[] = {
- {"runPendingCppRunnables", "()V", reinterpret_cast<void *>(runPendingCppRunnables)},
- {"dispatchGenericMotionEvent", "(Landroid/view/MotionEvent;)Z", reinterpret_cast<void *>(dispatchGenericMotionEvent)},
- {"dispatchKeyEvent", "(Landroid/view/KeyEvent;)Z", reinterpret_cast<void *>(dispatchKeyEvent)},
- {"sendRequestPermissionsResult", "(I[Ljava/lang/String;[I)V", reinterpret_cast<void *>(sendRequestPermissionsResult)},
+ {"updateNativeActivity", "()Z", reinterpret_cast<void *>(updateNativeActivity) },
};
const bool regOk = (env->RegisterNatives(jQtNative, methods, sizeof(methods) / sizeof(methods[0])) == JNI_OK);
-
+ env->DeleteLocalRef(jQtNative);
if (!regOk && QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
- if (!registerPermissionNatives())
+ QJniEnvironment qJniEnv;
+ if (!registerPermissionNatives(qJniEnv))
return JNI_ERR;
- g_runPendingCppRunnablesMethodID = env->GetStaticMethodID(jQtNative,
- "runPendingCppRunnablesOnAndroidThread",
- "()V");
- g_jNativeClass = static_cast<jclass>(env->NewGlobalRef(jQtNative));
- env->DeleteLocalRef(jQtNative);
+ if (!registerNativeInterfaceNatives(qJniEnv))
+ return JNI_ERR;
+
+ if (!registerExtrasNatives(qJniEnv))
+ return JNI_ERR;
- qRegisterMetaType<QtAndroidPrivate::PermissionsHash>();
return JNI_OK;
}
+Q_CORE_EXPORT jobject qt_androidActivity()
+{
+ QReadLocker locker(g_updateMutex());
+ return g_jActivity;
+}
+
-jobject QtAndroidPrivate::activity()
+QtJniTypes::Activity QtAndroidPrivate::activity()
{
+ QReadLocker locker(g_updateMutex());
return g_jActivity;
}
-jobject QtAndroidPrivate::service()
+Q_CORE_EXPORT jobject qt_androidService()
{
return g_jService;
}
-jobject QtAndroidPrivate::context()
+QtJniTypes::Service QtAndroidPrivate::service()
{
+ return g_jService;
+}
+
+QtJniTypes::Context QtAndroidPrivate::context()
+{
+ QReadLocker locker(g_updateMutex());
if (g_jActivity)
return g_jActivity;
if (g_jService)
return g_jService;
- return 0;
+ return nullptr;
}
JavaVM *QtAndroidPrivate::javaVM()
@@ -404,135 +303,10 @@ jobject QtAndroidPrivate::classLoader()
jint QtAndroidPrivate::androidSdkVersion()
{
- return g_androidSdkVersion;
-}
-
-void QtAndroidPrivate::runOnAndroidThread(const QtAndroidPrivate::Runnable &runnable, JNIEnv *env)
-{
- QMutexLocker locker(&g_pendingRunnablesMutex);
- const bool triggerRun = g_pendingRunnables->empty();
- g_pendingRunnables->push_back(runnable);
- locker.unlock();
- if (triggerRun)
- env->CallStaticVoidMethod(g_jNativeClass, g_runPendingCppRunnablesMethodID);
-}
-
-static bool waitForSemaphore(int timeoutMs, QSharedPointer<QSemaphore> sem)
-{
- while (timeoutMs > 0) {
- if (sem->tryAcquire(1, 10))
- return true;
- timeoutMs -= 10;
- QCoreApplication::processEvents();
- }
- return false;
-}
-
-void QtAndroidPrivate::runOnAndroidThreadSync(const QtAndroidPrivate::Runnable &runnable, JNIEnv *env, int timeoutMs)
-{
- QSharedPointer<QSemaphore> sem(new QSemaphore);
- runOnAndroidThread([&runnable, sem]{
- runnable();
- sem->release();
- }, env);
- waitForSemaphore(timeoutMs, sem);
-}
-
-void QtAndroidPrivate::requestPermissions(JNIEnv *env,
- const QStringList &permissions,
- const QtAndroidPrivate::PermissionsResultFunc &callbackFunc,
- bool directCall)
-{
- if (androidSdkVersion() < 23 || !activity()) {
- QHash<QString, QtAndroidPrivate::PermissionsResult> res;
- for (const auto &perm : permissions)
- res[perm] = checkPermission(perm);
- callbackFunc(res);
- return;
- }
- // Check API 23+ permissions
- const int requestCode = nextRequestCode();
- if (!directCall) {
- QMutexLocker locker(&g_pendingPermissionRequestsMutex);
- (*g_pendingPermissionRequests)[requestCode] = new PermissionsResultClass(callbackFunc);
- }
-
- runOnAndroidThread([permissions, callbackFunc, requestCode, directCall] {
- if (directCall) {
- QMutexLocker locker(&g_pendingPermissionRequestsMutex);
- (*g_pendingPermissionRequests)[requestCode] = new PermissionsResultClass(callbackFunc);
- }
-
- QJniEnvironment env;
- jclass clazz = env->FindClass("java/lang/String");
-
- if (env.checkAndClearExceptions())
- return;
-
- auto array = env->NewObjectArray(permissions.size(), clazz, nullptr);
- int index = 0;
- for (const auto &perm : permissions)
- env->SetObjectArrayElement(array, index++, QJniObject::fromString(perm).object());
- QJniObject(activity()).callMethod<void>("requestPermissions", "([Ljava/lang/String;I)V", array, requestCode);
- env->DeleteLocalRef(array);
- }, env);
-}
-
-QtAndroidPrivate::PermissionsHash QtAndroidPrivate::requestPermissionsSync(JNIEnv *env, const QStringList &permissions, int timeoutMs)
-{
- QSharedPointer<QHash<QString, QtAndroidPrivate::PermissionsResult>> res(new QHash<QString, QtAndroidPrivate::PermissionsResult>());
- QSharedPointer<QSemaphore> sem(new QSemaphore);
- requestPermissions(env, permissions, [sem, res](const QHash<QString, PermissionsResult> &result){
- *res = result;
- sem->release();
- }, true);
- if (waitForSemaphore(timeoutMs, sem))
- return std::move(*res);
- else // mustn't touch *res
- return QHash<QString, QtAndroidPrivate::PermissionsResult>();
-}
-
-QtAndroidPrivate::PermissionsResult QtAndroidPrivate::checkPermission(const QString &permission)
-{
- const auto res = QJniObject::callStaticMethod<jint>("org/qtproject/qt/android/QtNative",
- "checkSelfPermission",
- "(Ljava/lang/String;)I",
- QJniObject::fromString(permission).object());
- return res == PERMISSION_GRANTED ? PermissionsResult::Granted : PermissionsResult::Denied;
-}
-
-bool QtAndroidPrivate::shouldShowRequestPermissionRationale(const QString &permission)
-{
- if (androidSdkVersion() < 23 || !activity())
- return false;
-
- return QJniObject(activity()).callMethod<jboolean>("shouldShowRequestPermissionRationale",
- "(Ljava/lang/String;)Z",
- QJniObject::fromString(permission).object());
-}
-
-void QtAndroidPrivate::registerGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener)
-{
- QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
- g_genericMotionEventListeners()->listeners.push_back(listener);
-}
-
-void QtAndroidPrivate::unregisterGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener)
-{
- QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
- g_genericMotionEventListeners()->listeners.removeOne(listener);
-}
-
-void QtAndroidPrivate::registerKeyEventListener(QtAndroidPrivate::KeyEventListener *listener)
-{
- QMutexLocker locker(&g_keyEventListeners()->mutex);
- g_keyEventListeners()->listeners.push_back(listener);
-}
-
-void QtAndroidPrivate::unregisterKeyEventListener(QtAndroidPrivate::KeyEventListener *listener)
-{
- QMutexLocker locker(&g_keyEventListeners()->mutex);
- g_keyEventListeners()->listeners.removeOne(listener);
+ static jint sdkVersion = 0;
+ if (!sdkVersion)
+ sdkVersion = QJniObject::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT");
+ return sdkVersion;
}
void QtAndroidPrivate::waitForServiceSetup()
@@ -542,29 +316,41 @@ void QtAndroidPrivate::waitForServiceSetup()
int QtAndroidPrivate::acuqireServiceSetup(int flags)
{
- g_serviceSetupLockers->ref();
+ g_serviceSetupLockers.ref();
return flags;
}
void QtAndroidPrivate::setOnBindListener(QtAndroidPrivate::OnBindListener *listener)
{
- QMutexLocker lock(g_onBindListenerMutex());
- *g_onBindListener = listener;
- if (!g_serviceSetupLockers->deref())
+ const auto lock = qt_scoped_lock(g_onBindListenerMutex);
+ g_onBindListener = listener;
+ if (!g_serviceSetupLockers.deref())
g_waitForServiceSetupSemaphore->release();
}
jobject QtAndroidPrivate::callOnBindListener(jobject intent)
{
- QMutexLocker lock(g_onBindListenerMutex());
- if (*g_onBindListener)
- return (*g_onBindListener)->onBind(intent);
+ const auto lock = qt_scoped_lock(g_onBindListenerMutex);
+ if (g_onBindListener)
+ return g_onBindListener->onBind(intent);
return nullptr;
}
+Q_CONSTINIT static QBasicAtomicInt g_androidDeadlockProtector = Q_BASIC_ATOMIC_INITIALIZER(0);
+
+bool QtAndroidPrivate::acquireAndroidDeadlockProtector()
+{
+ return g_androidDeadlockProtector.testAndSetAcquire(0, 1);
+}
+
+void QtAndroidPrivate::releaseAndroidDeadlockProtector()
+{
+ g_androidDeadlockProtector.storeRelease(0);
+}
+
QT_END_NAMESPACE
-Q_CORE_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
Q_UNUSED(reserved);
@@ -598,5 +384,3 @@ Q_CORE_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
return JNI_VERSION_1_6;
}
-
-#include "qjnihelpers.moc"