QT app is deactivated incorrectly on Android 10 in Gesture mode

If I enable Gesture mode on an Android 10 emulator by tapping System->Gestures->System Navigation->Gesture Navigation and then deactivate my app by swiping and reactivate it back, I get the following in the log:

adb logcat | grep "\ Lines"
05-04 21:52:36.389 11794 11939 D Lines   : Squircle::aboutToQuit()
05-04 21:52:36.389 11794 11939 D Lines   : InterstitialAdWrapper clear() called.
05-04 21:52:36.392 11794 11939 D Lines   : InterstitialAdWrapper InterstitialAd destructor called.
05-04 21:52:36.393 11794 11939 D Lines   : Squircle destructor called.
05-04 21:52:36.396 11794 11939 W Lines   : exit app 0
...
05-04 21:52:37.032 11794 11939 D Lines   : OslSoundPool destructor
05-04 21:52:37.117  2040  2134 W InputDispatcher: channel '8474831 net.geographx.LinesGame/net.geographx.MainActivity (server)' ~ Consumer closed input channel or an error occurred.  events=0x9
05-04 21:52:37.117  2040  2134 E InputDispatcher: channel '8474831 net.geographx.LinesGame/net.geographx.MainActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
05-04 21:52:37.123  2040  3120 I WindowManager: WIN DEATH: Window{8474831 u0 net.geographx.LinesGame/net.geographx.MainActivity}
05-04 21:52:37.123  2040  3120 W InputDispatcher: Attempted to unregister already unregistered input channel '8474831 net.geographx.LinesGame/net.geographx.MainActivity (server)'
05-04 21:52:37.136  2040  3120 W ActivityManager: Scheduling restart of crashed service net.geographx.LinesGame/org.chromium.content.app.SandboxedProcessService0 in 1000ms
05-04 21:52:37.151  2040  2057 I ActivityManager: Process net.geographx.LinesGame (pid 11794) has died: cch CRE
05-04 21:52:38.034  2040  2057 I ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=net.geographx.LinesGame/net.geographx.MainActivity bnds=[37,882][238,1191]} from uid 10090
05-04 21:52:38.055  2040  2057 I chatty  : uid=1000(system) Binder:2040_2 identical 8 lines
05-04 21:52:38.099  2040  2072 I ActivityManager: Start proc 12702:net.geographx.LinesGame/u0a134 for activity {net.geographx.LinesGame/net.geographx.MainActivity}
05-04 21:52:38.101 12702 12702 W raphx.LinesGam: Unexpected CPU variant for X86 using defaults: x86
05-04 21:52:38.124 12702 12702 E raphx.LinesGam: Not starting debugger since process cannot load the jdwp agent.
...
05-04 21:52:38.966  2040  2069 I ActivityTaskManager: Displayed net.geographx.LinesGame/net.geographx.MainActivity: +931ms
05-04 21:52:39.062 12702 12826 D libLinesGameQt_x86.so: Translation file has been loaded successfully:  "LinesGame_en"
05-04 21:52:39.062 12702 12826 D Lines   : App version:  "2.5.11, #133"
05-04 21:52:39.072 12702 12826 D Lines   : QT/SysInfo: Device Pixel Ratio: 3  Screen DPI:  147.131
05-04 21:52:39.106 12702 12826 D Lines   : InterstitialAdWrapper  valid
05-04 21:52:39.185 12702 12826 D Lines   : qml: Registering adfree
05-04 21:52:39.189  7632  9145 W Finsky  : [672] gcs.d(23): net.geographx.LinesGame: No account found.
05-04 21:52:39.411 12702 12826 D Lines   : qml: adfree Component.onCompleted
05-04 21:52:39.413 12702 12826 D Lines   : InterstitialAdWrapper: calling Java method ' initializeInterstitialAd ' from C++
05-04 21:52:39.413 12702 12826 D Lines   : Starting the application event loop...
05-04 21:52:39.463 12702 12826 D Lines   : onApplicationStateChanged(Qt::ApplicationActive)

The picture below demonstrates the swiping:

It is not clear for me what happens and what is “WIN DEATH” and “Channel is unrecoverably broken“, but the first thing I noticed was that aboutToQuit is called when the app is deactivated, but onApplicationStateChanged is not. For example, if I deactivate the app not by swiping but by clicking the bottom bar, onApplicationStateChanged is called with Qt::ApplicationInactive and Qt::ApplicationSuspended parameters and I have the following in the logs:

05-04 23:42:03.654  1809  2657 E BufferQueueProducer: [SurfaceView - net.geographx.LinesGame/net.geographx.MainActivity#0] dequeueBuffer: BufferQueue has no connected producer
05-04 23:42:03.654  8975  9081 W Lines   : QEGLPlatformContext: eglSwapBuffers failed: 300d
05-04 23:42:03.654  8975  9056 D Lines   : onApplicationStateChanged(Qt::ApplicationInactive)
05-04 23:42:03.671  8975  9081 W Lines   : QEGLPlatformContext: eglSwapBuffers failed: 300d
05-04 23:42:03.672  1809  2657 E BufferQueueProducer: [SurfaceView - net.geographx.LinesGame/net.geographx.MainActivity#0] disconnect: not connected (req=1)
05-04 23:42:03.673  8975  9056 D Lines   : onApplicationStateChanged(Qt::ApplicationSuspended)
05-04 23:42:07.768  8975  9056 D Lines   : onApplicationStateChanged(Qt::ApplicationActive)

In 3-buttons mode I have the same logs.

So I have an impression that there is some specific behavior related to swiping in Gesture mode (and there is an answer on stackoverflow.com describing the same behavior of back button).

Steps to reproduce

I reproduced this behavior by adding the following event handlers to a test app:

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    MyApplication app(argc, argv);

    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    //fired on Desktop
    QObject::connect(&app, &QGuiApplication::aboutToQuit, []()
    {
        qDebug() << "aboutToQuit()";

        switch (qApp->applicationState())
        {
            case Qt::ApplicationSuspended:
                qDebug() << "applicationState: Qt::ApplicationSuspended";
                break;

            case Qt::ApplicationHidden:
                qDebug() << "applicationState: Qt::ApplicationHidden";
                break;

            case Qt::ApplicationInactive:
                qDebug() << "applicationState: Qt::ApplicationInactive";
                break;

            case Qt::ApplicationActive:
                qDebug() << "applicationState: Qt::ApplicationActive";
                break;
        }
    });

    //fired on Mobile
    QObject::connect(&app, &QGuiApplication::applicationStateChanged, [](Qt::ApplicationState state)
    {
        switch (state)
        {
            case Qt::ApplicationSuspended:
                qDebug() << "onApplicationStateChanged(Qt::ApplicationSuspended)";
                break;

            case Qt::ApplicationHidden:
                qDebug() << "onApplicationStateChanged(Qt::ApplicationHidden)";
                break;

            case Qt::ApplicationInactive:
                qDebug() << "onApplicationStateChanged(Qt::ApplicationInactive)";
                break;

            case Qt::ApplicationActive:
                qDebug() << "onApplicationStateChanged(Qt::ApplicationActive)";
                break;
        }
    });

    return app.exec();
}

A quick workaround

When aboutToQuit is called, the application is in active state, so a possible workaround is:

//fired on Desktop and on Android from MainActivity::onDestroy()
void Squircle::aboutToQuit()
{
    qDebug() << "Squircle::aboutToQuit()";

#if defined(PLATFORM_DESKTOP)
    saveCurrentGame();
#else
    switch (qApp->applicationState())
    {
        case Qt::ApplicationSuspended:
            qDebug() << "applicationState: Qt::ApplicationSuspended";
            break;

        case Qt::ApplicationHidden:
            qDebug() << "applicationState: Qt::ApplicationHidden";
            break;

        case Qt::ApplicationInactive:
            qDebug() << "applicationState: Qt::ApplicationInactive";
            break;

        case Qt::ApplicationActive:
            qDebug() << "applicationState: Qt::ApplicationActive, saving the current game (Gesture Swipe on Android 10 workaroung)";
            saveCurrentGame();
            break;
    }

#endif
}

where qApp is a macro definition in QGuiApplication header:

#define qApp (static_cast<QGuiApplication *>(QCoreApplication::instance()))

with this workaround my game is saved, but something is still wrong with app lifecycle events. And of course, I am not sure that this code will work correctly 🙂

Notes about splitting

I was able to split the screen in both 3-buttons and Gesture modes and the app worked with the same effects:

When I interact with Google Chrome at the bottom part of the screen, my app at the top part of the screen remains active.

Notes about using the emulator

If the emulator hangs up when I try to close it, I kill it from Windows Command Prompt with the command:

Taskkill /IM qemu-system-x86_64.exe /F /T

or

wmic process where "name='qemu-system-x86_64.exe'" delete

but periodically I get

ERROR: The process with PID 15744 (child process of PID 16120) could not be terminated.
Reason: There is no running instance of the task.
ERROR: The process with PID 10016 (child process of PID 15780) could not be terminated.
Reason: There is no running instance of the task.

or

Deleting instance \\DESKTOP-L5J499D\ROOT\CIMV2:Win32_Process.Handle="15744"
ERROR:
Description = Access denied

even if I run the commands with administrative privileges. There is a long discussion about that on stackoverflow.com.

Leave a Reply

Your email address will not be published. Required fields are marked *