diff options
Diffstat (limited to 'src/corelib/kernel/qjnihelpers.cpp')
-rw-r--r-- | src/corelib/kernel/qjnihelpers.cpp | 484 |
1 files changed, 126 insertions, 358 deletions
diff --git a/src/corelib/kernel/qjnihelpers.cpp b/src/corelib/kernel/qjnihelpers.cpp index 45f4a4d895..d900b74d37 100644 --- a/src/corelib/kernel/qjnihelpers.cpp +++ b/src/corelib/kernel/qjnihelpers.cpp @@ -1,54 +1,18 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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 <QtCore/qrunnable.h> +#include "qreadwritelock.h" +#include <QtCore/private/qcoreapplication_p.h> +#include <QtCore/private/qlocking_p.h> +#include <android/log.h> #include <deque> #include <memory> @@ -62,125 +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; -static jmethodID g_hideSplashScreenMethodID = 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 { @@ -288,89 +180,50 @@ void QtAndroidPrivate::handleResume() listeners.at(i)->handleResume(); } -static void setAndroidSdkVersion(JNIEnv *env) -{ - jclass androidVersionClass = env->FindClass("android/os/Build$VERSION"); - if (QJniEnvironment::exceptionCheckAndClear(env)) - return; - - jfieldID androidSDKFieldID = env->GetStaticFieldID(androidVersionClass, "SDK_INT", "I"); - if (QJniEnvironment::exceptionCheckAndClear(env)) - return; - - g_androidSdkVersion = env->GetStaticIntField(androidVersionClass, androidSDKFieldID); -} - -static void setNativeActivity(JNIEnv *env, jclass, jobject activity) -{ - if (g_jActivity != 0) - env->DeleteGlobalRef(g_jActivity); - - if (activity != 0) { - g_jActivity = env->NewGlobalRef(activity); - env->DeleteLocalRef(activity); - } else { - g_jActivity = 0; - } -} - -static void setNativeService(JNIEnv *env, jclass, jobject service) -{ - if (g_jService != 0) - env->DeleteGlobalRef(g_jService); - - if (service != 0) { - g_jService = env->NewGlobalRef(service); - env->DeleteLocalRef(service); - } else { - g_jService = 0; - } -} - jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env) { + g_javaVM = vm; + jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative"); - if (QJniEnvironment::exceptionCheckAndClear(env)) + if (QJniEnvironment::checkAndClearExceptions(env)) return JNI_ERR; jmethodID activityMethodID = env->GetStaticMethodID(jQtNative, "activity", "()Landroid/app/Activity;"); - if (QJniEnvironment::exceptionCheckAndClear(env)) + if (QJniEnvironment::checkAndClearExceptions(env)) return JNI_ERR; jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID); - if (QJniEnvironment::exceptionCheckAndClear(env)) + if (QJniEnvironment::checkAndClearExceptions(env)) return JNI_ERR; jmethodID serviceMethodID = env->GetStaticMethodID(jQtNative, "service", "()Landroid/app/Service;"); - if (QJniEnvironment::exceptionCheckAndClear(env)) + if (QJniEnvironment::checkAndClearExceptions(env)) return JNI_ERR; jobject service = env->CallStaticObjectMethod(jQtNative, serviceMethodID); - if (QJniEnvironment::exceptionCheckAndClear(env)) + if (QJniEnvironment::checkAndClearExceptions(env)) return JNI_ERR; jmethodID classLoaderMethodID = env->GetStaticMethodID(jQtNative, "classLoader", "()Ljava/lang/ClassLoader;"); - if (QJniEnvironment::exceptionCheckAndClear(env)) + if (QJniEnvironment::checkAndClearExceptions(env)) return JNI_ERR; jobject classLoader = env->CallStaticObjectMethod(jQtNative, classLoaderMethodID); - if (QJniEnvironment::exceptionCheckAndClear(env)) + if (QJniEnvironment::checkAndClearExceptions(env)) return JNI_ERR; - setAndroidSdkVersion(env); - g_jClassLoader = env->NewGlobalRef(classLoader); env->DeleteLocalRef(classLoader); if (activity) { @@ -381,52 +234,61 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env) g_jService = env->NewGlobalRef(service); env->DeleteLocalRef(service); } - g_javaVM = vm; 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)}, - {"setNativeActivity", "(Landroid/app/Activity;)V", reinterpret_cast<void *>(setNativeActivity)}, - {"setNativeService", "(Landroid/app/Service;)V", reinterpret_cast<void *>(setNativeService)}, - {"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 (!regOk && QJniEnvironment::exceptionCheckAndClear(env)) + QJniEnvironment qJniEnv; + if (!registerPermissionNatives(qJniEnv)) return JNI_ERR; - g_runPendingCppRunnablesMethodID = env->GetStaticMethodID(jQtNative, - "runPendingCppRunnablesOnAndroidThread", - "()V"); - g_hideSplashScreenMethodID = env->GetStaticMethodID(jQtNative, "hideSplashScreen", "(I)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() @@ -441,178 +303,84 @@ jobject QtAndroidPrivate::classLoader() jint QtAndroidPrivate::androidSdkVersion() { - return g_androidSdkVersion; -} - -void QtAndroidPrivate::runOnUiThread(QRunnable *runnable, JNIEnv *env) -{ - runOnAndroidThread([runnable]() { - runnable->run(); - if (runnable->autoDelete()) - delete runnable; - }, env); + static jint sdkVersion = 0; + if (!sdkVersion) + sdkVersion = QJniObject::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT"); + return sdkVersion; } -void QtAndroidPrivate::runOnAndroidThread(const QtAndroidPrivate::Runnable &runnable, JNIEnv *env) +void QtAndroidPrivate::waitForServiceSetup() { - 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); + g_waitForServiceSetupSemaphore->acquire(); } -static bool waitForSemaphore(int timeoutMs, QSharedPointer<QSemaphore> sem) +int QtAndroidPrivate::acuqireServiceSetup(int flags) { - while (timeoutMs > 0) { - if (sem->tryAcquire(1, 10)) - return true; - timeoutMs -= 10; - QCoreApplication::processEvents(); - } - return false; + g_serviceSetupLockers.ref(); + return flags; } -void QtAndroidPrivate::runOnAndroidThreadSync(const QtAndroidPrivate::Runnable &runnable, JNIEnv *env, int timeoutMs) +void QtAndroidPrivate::setOnBindListener(QtAndroidPrivate::OnBindListener *listener) { - QSharedPointer<QSemaphore> sem(new QSemaphore); - runOnAndroidThread([&runnable, sem]{ - runnable(); - sem->release(); - }, env); - waitForSemaphore(timeoutMs, sem); + const auto lock = qt_scoped_lock(g_onBindListenerMutex); + g_onBindListener = listener; + if (!g_serviceSetupLockers.deref()) + g_waitForServiceSetupSemaphore->release(); } -void QtAndroidPrivate::requestPermissions(JNIEnv *env, - const QStringList &permissions, - const QtAndroidPrivate::PermissionsResultFunc &callbackFunc, - bool directCall) +jobject QtAndroidPrivate::callOnBindListener(jobject intent) { - 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.exceptionCheckAndClear()) - 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); + const auto lock = qt_scoped_lock(g_onBindListenerMutex); + if (g_onBindListener) + return g_onBindListener->onBind(intent); + return nullptr; } -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>(); -} +Q_CONSTINIT static QBasicAtomicInt g_androidDeadlockProtector = Q_BASIC_ATOMIC_INITIALIZER(0); -QtAndroidPrivate::PermissionsResult QtAndroidPrivate::checkPermission(const QString &permission) +bool QtAndroidPrivate::acquireAndroidDeadlockProtector() { - 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; + return g_androidDeadlockProtector.testAndSetAcquire(0, 1); } -bool QtAndroidPrivate::shouldShowRequestPermissionRationale(const QString &permission) +void QtAndroidPrivate::releaseAndroidDeadlockProtector() { - if (androidSdkVersion() < 23 || !activity()) - return false; - - return QJniObject(activity()).callMethod<jboolean>("shouldShowRequestPermissionRationale", - "(Ljava/lang/String;)Z", - QJniObject::fromString(permission).object()); + g_androidDeadlockProtector.storeRelease(0); } -void QtAndroidPrivate::registerGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener) -{ - QMutexLocker locker(&g_genericMotionEventListeners()->mutex); - g_genericMotionEventListeners()->listeners.push_back(listener); -} +QT_END_NAMESPACE -void QtAndroidPrivate::unregisterGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener) +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { - QMutexLocker locker(&g_genericMotionEventListeners()->mutex); - g_genericMotionEventListeners()->listeners.removeOne(listener); -} + Q_UNUSED(reserved); -void QtAndroidPrivate::registerKeyEventListener(QtAndroidPrivate::KeyEventListener *listener) -{ - QMutexLocker locker(&g_keyEventListeners()->mutex); - g_keyEventListeners()->listeners.push_back(listener); -} + static const char logTag[] = "QtCore"; + static bool initialized = false; + if (initialized) + return JNI_VERSION_1_6; + initialized = true; -void QtAndroidPrivate::unregisterKeyEventListener(QtAndroidPrivate::KeyEventListener *listener) -{ - QMutexLocker locker(&g_keyEventListeners()->mutex); - g_keyEventListeners()->listeners.removeOne(listener); -} + typedef union { + JNIEnv *nenv; + void *venv; + } _JNIEnv; -void QtAndroidPrivate::hideSplashScreen(JNIEnv *env, int duration) -{ - env->CallStaticVoidMethod(g_jNativeClass, g_hideSplashScreenMethodID, duration); -} + __android_log_print(ANDROID_LOG_INFO, logTag, "Start"); -void QtAndroidPrivate::waitForServiceSetup() -{ - g_waitForServiceSetupSemaphore->acquire(); -} + _JNIEnv uenv; + uenv.venv = nullptr; -int QtAndroidPrivate::acuqireServiceSetup(int flags) -{ - g_serviceSetupLockers->ref(); - return flags; -} + if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) { + __android_log_print(ANDROID_LOG_FATAL, logTag, "GetEnv failed"); + return JNI_ERR; + } -void QtAndroidPrivate::setOnBindListener(QtAndroidPrivate::OnBindListener *listener) -{ - QMutexLocker lock(g_onBindListenerMutex()); - *g_onBindListener = listener; - if (!g_serviceSetupLockers->deref()) - g_waitForServiceSetupSemaphore->release(); -} + JNIEnv *env = uenv.nenv; + const jint ret = QT_PREPEND_NAMESPACE(QtAndroidPrivate::initJNI(vm, env)); + if (ret != 0) { + __android_log_print(ANDROID_LOG_FATAL, logTag, "initJNI failed"); + return ret; + } -jobject QtAndroidPrivate::callOnBindListener(jobject intent) -{ - QMutexLocker lock(g_onBindListenerMutex()); - if (*g_onBindListener) - return (*g_onBindListener)->onBind(intent); - return nullptr; + return JNI_VERSION_1_6; } - -QT_END_NAMESPACE - -#include "qjnihelpers.moc" |