Android A11Y: Only access the main thread when it is not blocked

When the qtMainLoopThread calls QSGThreadedRenderLoop::polishAndSync(),
it waits for the QSGRenderThread.

In the QSGRenderThread, QAndroidPlatformOpenGLWindow::eglSurface()
calls QtAndroid::createSurface() and waits for the "android main
thread" to return a valid surface.
When the "android main thread" now calls "runInObjectContext" (e.g. by
calling QtAndroidAccessibility::childIdListForAccessibleObject()) it
waits for the qtMainLoopThread and the program is stuck in a deadlock.

To prevent this, we protect all BlockedQueuedConnection from the
"android main thread" to the qtMainLoopThread by acquiring the
AndroidDeadlockProtector.
When QAndroidPlatformOpenGLWindow::eglSurface() already acquired the
AndroidDeadlockProtector we abort the current A11y call with an emtpy
or default value.

Note: b8a95275440b8a143ee648466fd8b5401ee1e839 already tried to fix
this by checking "getSurfaceCount() != 0", but there are situations,
where a new surface is being created while an old surface is still
present.

Task-number: QTBUG-105958
Change-Id: Ie40e8654c99aace9e69b0b8412952fa22c89f071
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
(cherry picked from commit b832a5ac72c6015b6509d60b75b2ce5d5e570800)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Julian Greilich 2023-01-04 16:32:28 +01:00 committed by Qt Cherry-pick Bot
parent 1440ee1e3d
commit 126fe788e3

View File

@ -37,6 +37,7 @@
** **
****************************************************************************/ ****************************************************************************/
#include "androiddeadlockprotector.h"
#include "androidjniaccessibility.h" #include "androidjniaccessibility.h"
#include "androidjnimain.h" #include "androidjnimain.h"
#include "qandroidplatformintegration.h" #include "qandroidplatformintegration.h"
@ -94,6 +95,14 @@ namespace QtAndroidAccessibility
template <typename Func, typename Ret> template <typename Func, typename Ret>
void runInObjectContext(QObject *context, Func &&func, Ret *retVal) void runInObjectContext(QObject *context, Func &&func, Ret *retVal)
{ {
AndroidDeadlockProtector protector;
if (!protector.acquire()) {
__android_log_print(ANDROID_LOG_WARN, m_qtTag,
"Could not run accessibility call in object context, accessing "
"main thread could lead to deadlock");
return;
}
if (!QtAndroid::blockEventLoopsWhenSuspended() if (!QtAndroid::blockEventLoopsWhenSuspended()
|| QGuiApplication::applicationState() != Qt::ApplicationSuspended) { || QGuiApplication::applicationState() != Qt::ApplicationSuspended) {
QMetaObject::invokeMethod(context, func, Qt::BlockingQueuedConnection, retVal); QMetaObject::invokeMethod(context, func, Qt::BlockingQueuedConnection, retVal);