Android: Fix deadlock when calling requestPermission

The hang happend when using QtAndroidPrivate::requestPermissions before
the QApplication::exec. Android UI calls "sendRequestPermissionsResult"
which was blocking until the event is delivered, but the qt main loop is
blocked and waits for the main surface to be created by the Android UI
thread which is already blocked.

With this patch sendRequestPermissionsResult won't block for the result
to be delivered.

Change-Id: I48ada65fe9ea63471ab46d8a9d839ba1b91d17b3
Reviewed-by: Christian Stromme <christian.stromme@qt.io>
This commit is contained in:
BogDan Vatra 2017-04-11 13:17:21 +03:00
parent d31deab632
commit e8ea1edd89
2 changed files with 14 additions and 9 deletions

View File

@ -80,13 +80,13 @@ 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); }
Q_INVOKABLE void sendResult(const QtAndroidPrivate::PermissionsHash &result) { m_func(result); delete this;}
private:
QtAndroidPrivate::PermissionsResultFunc m_func;
};
typedef QHash<int, QSharedPointer<PermissionsResultClass>> PendingPermissionRequestsHash;
typedef QHash<int, PermissionsResultClass*> PendingPermissionRequestsHash;
Q_GLOBAL_STATIC(PendingPermissionRequestsHash, g_pendingPermissionRequests);
static QBasicMutex g_pendingPermissionRequestsMutex;
static int nextRequestCode()
@ -131,11 +131,11 @@ static void sendRequestPermissionsResult(JNIEnv *env, jobject /*obj*/, jint requ
// show an error or something ?
return;
}
auto request = std::move(*it);
auto request = *it;
g_pendingPermissionRequests->erase(it);
locker.unlock();
Qt::ConnectionType connection = QThread::currentThread() == request->thread() ? Qt::DirectConnection : Qt::BlockingQueuedConnection;
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]);
@ -147,7 +147,7 @@ static void sendRequestPermissionsResult(JNIEnv *env, jobject /*obj*/, jint requ
QtAndroidPrivate::PermissionsResult::Denied;
hash[permission] = value;
}
QMetaObject::invokeMethod(request.data(), "sendResult", connection, Q_ARG(QtAndroidPrivate::PermissionsHash, hash));
QMetaObject::invokeMethod(request, "sendResult", connection, Q_ARG(QtAndroidPrivate::PermissionsHash, hash));
}
static jboolean dispatchGenericMotionEvent(JNIEnv *, jclass, jobject event)
@ -410,6 +410,7 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
g_jNativeClass = static_cast<jclass>(env->NewGlobalRef(jQtNative));
env->DeleteLocalRef(jQtNative);
qRegisterMetaType<QtAndroidPrivate::PermissionsHash>();
return JNI_OK;
}
@ -491,13 +492,13 @@ void QtAndroidPrivate::requestPermissions(JNIEnv *env, const QStringList &permis
const int requestCode = nextRequestCode();
if (!directCall) {
QMutexLocker locker(&g_pendingPermissionRequestsMutex);
(*g_pendingPermissionRequests)[requestCode] = QSharedPointer<PermissionsResultClass>::create(callbackFunc);
(*g_pendingPermissionRequests)[requestCode] = new PermissionsResultClass(callbackFunc);
}
runOnAndroidThread([permissions, callbackFunc, requestCode, directCall] {
if (directCall) {
QMutexLocker locker(&g_pendingPermissionRequestsMutex);
(*g_pendingPermissionRequests)[requestCode] = QSharedPointer<PermissionsResultClass>::create(callbackFunc);
(*g_pendingPermissionRequests)[requestCode] = new PermissionsResultClass(callbackFunc);
}
QJNIEnvironmentPrivate env;

View File

@ -52,8 +52,10 @@
//
#include <jni.h>
#include <QtCore/private/qglobal_p.h>
#include <functional>
#include <QtCore/private/qglobal_p.h>
#include <QHash>
#include <QMetaType>
QT_BEGIN_NAMESPACE
@ -117,7 +119,7 @@ namespace QtAndroidPrivate
Q_CORE_EXPORT void runOnAndroidThreadSync(const Runnable &runnable, JNIEnv *env, int timeoutMs = INT_MAX);
Q_CORE_EXPORT void runOnUiThread(QRunnable *runnable, JNIEnv *env);
Q_CORE_EXPORT void requestPermissions(JNIEnv *env, const QStringList &permissions, const PermissionsResultFunc &callbackFunc, bool directCall = false);
Q_CORE_EXPORT QHash<QString, PermissionsResult> requestPermissionsSync(JNIEnv *env, const QStringList &permissions, int timeoutMs = INT_MAX);
Q_CORE_EXPORT PermissionsHash requestPermissionsSync(JNIEnv *env, const QStringList &permissions, int timeoutMs = INT_MAX);
Q_CORE_EXPORT PermissionsResult checkPermission(const QString &permission);
Q_CORE_EXPORT bool shouldShowRequestPermissionRationale(const QString &permission);
@ -145,4 +147,6 @@ namespace QtAndroidPrivate
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QtAndroidPrivate::PermissionsHash)
#endif // QJNIHELPERS_H