androidc++qtandroid-ndk

C++ code is unable to iterate app folder when deploying a Qt app on Android


I'm migrating some code from Qt 6.2.2 to 6.9.0 under Android.

I have some code looking in the application folder for .so files (for potential plugins to be loaded). This code used to iterate the app folder using standard std::filesystem code. It does not work anymore. I can iterate the HOME folder, but not the app folder. Surprisingly QDir is able to do so, but my code doing this is pure C++/STL code, I cannot use Qt at this level.

Did something change in apk structure? Why STL cannot see the app folder? Is there any workaround?

Here is a very simple sample program:

#include <QApplication>
#include <QMainWindow>
#include <QLabel>
#include <QDebug>
#include <QDir>
#include <filesystem>
void listDirContent(QString path)
{
    {
        QDir dir(path);
        qDebug() << "Using Qt, " << path << ":";
        for (auto file : dir.entryList())
            qDebug() << file;
    }
    {
        std::filesystem::path std_path = path.toStdString();
        qDebug() << "using std, " << std_path.c_str() << ":";
        std::filesystem::directory_iterator itDir;
        std::filesystem::directory_iterator itEnd;
        std::filesystem::path Path;
        try
        {
            itDir = std::filesystem::directory_iterator(std_path);
            qDebug() << "Found files:";
            for (; itDir != itEnd; itDir++)
            {
                qDebug() << QString(itDir->path().c_str());
            }
            qDebug() << "end files";
        }
        catch (const std::exception& e)
        {
            qDebug() << "FAILED" << e.what();
        }
    }
}

int main( int argc, char* argv[] )
{
    QApplication app(argc, argv);
    QMainWindow wnd;
    wnd.setCentralWidget(new QLabel("Hello",&wnd));
    wnd.show();
    listDirContent(qApp->applicationDirPath());
    listDirContent(QDir::homePath());
    return app.exec();
}

This program outputs:

D/default (18701): Using Qt,  "/data/app/org.qtproject.example.TestListDir-UzLzgFYV8gnNNAzUQkD1Lw==/base.apk!/lib/armeabi-v7a" :
D/default (18701): "armeabi-v7a"
D/default (18701): "libc++_shared.so"
D/default (18701): "libplugins_iconengines_qsvgicon_armeabi-v7a.so"
D/default (18701): "libplugins_imageformats_qgif_armeabi-v7a.so"
D/default (18701): "libplugins_imageformats_qico_armeabi-v7a.so"
D/default (18701): "libplugins_imageformats_qjpeg_armeabi-v7a.so"
D/default (18701): "libplugins_imageformats_qsvg_armeabi-v7a.so"
D/default (18701): "libplugins_platforms_qtforandroid_armeabi-v7a.so"
D/default (18701): "libplugins_styles_qandroidstyle_armeabi-v7a.so"
D/default (18701): "libQt6Core_armeabi-v7a.so"
D/default (18701): "libQt6Gui_armeabi-v7a.so"
D/default (18701): "libQt6Svg_armeabi-v7a.so"
D/default (18701): "libQt6Widgets_armeabi-v7a.so"
D/default (18701): "libTestListDir_armeabi-v7a.so"
D/default (18701): using std,  /data/app/org.qtproject.example.TestListDir-UzLzgFYV8gnNNAzUQkD1Lw==/base.apk!/lib/armeabi-v7a :
D/default (18701): FAILED filesystem error: in directory_iterator::directory_iterator(...): No such file or directory ["/data/app/org.qtproject.example.TestListDir-FvVxa-eHXbBc9Zpa2Q0uGQ==/base.apk!/lib/armeabi-v7a"]
D/default (18701): Using Qt,  "/data/user/0/org.qtproject.example.TestListDir/files" :
D/default (18701): "."
D/default (18701): ".."
D/default (18701): "profileInstalled"
D/default (18701): "profileinstaller_profileWrittenFor_lastUpdateTime.dat"
D/default (18701): using std,  /data/user/0/org.qtproject.example.TestListDir/files :
D/default (18701): Found files:
D/default (18701): /data/user/0/org.qtproject.example.TestListDir/files/profileinstaller_profileWrittenFor_lastUpdateTime.dat
D/default (18701): /data/user/0/org.qtproject.example.TestListDir/files/profileInstalled
D/default (18701): end files

See that std::filesystem is unable to iterate /data/app/org.qtproject.example.TestListDir-UzLzgFYV8gnNNAzUQkD1Lw==/base.apk!/lib/armeabi-v7a folder, it reports the folder does not exist, while Qt is able to iterate through it.

With Qt 6.2.2, it used to list the files from the app folder (which was slightly different, there was no "base.apk!" in path.

I reported a Qt bug: https://bugreports.qt.io/browse/QTBUG-136214

Does anyone have an idea how to workaround this issue? How to make std::filestem be able to look into the app folder?


Edit, std::filesystem is able to iterate /data/app/org.qtproject.example.TestListDir-UzLzgFYV8gnNNAzUQkD1Lw==, it sees base.apk as being a file. Looks like QDir supports a special case where apk file name is followed by ! and is able to go look into it as if it was actually a folder...


Solution

  • As you discovered, Android used to extract the native libraries from the APK to a plain directory. This default was changed to not extract them, but instead read libraries directly from the APK. If you change extractNativeLibraries to true you get back the old behavior, at the cost of more disk usage.

    The application file path you received (/data/app/.../base.apk!/lib/armeabi-v7a) uses the Android convention of a ! to indicate "a file path within this APK". The Android core libraries know how to work with these paths.

    The Android C++ standard library, however, uses the normal opendir and readdir system calls and rightfully complains that /.../base.apk!/lib/armeabi-v7a does not exist (as only /.../base.apk exists on disk).

    Finally, the reason why Qt's QDir supports this is because Qt registers an APK-aware "File engine" which understands this path notation and allows you to access APKs like regular directories. There are similar file engines for assets and Android's content URIs.