Failed to create QFile when deploying to Android device



  • I have deployed a c++ application to my Android phone (LG G4, Android 6). However, when I run the app, it crashes as a QFile cannot be created. Here is my code:

    /*!
     * \brief so_SslClient::certFileOnDisk to read or write the certificate file to the disk.
     * \param mode QIODevice::ReadOnly to read. \n
     *             QIODevice::WriteOnly to write.
     * \param cert the certificate to write.
     * \return the read certificate.
     *
     *  The files are saved under applicationDir/certs/'Hostname.pem'
     */
    QSslCertificate so_SslClient::certFileOnDisk(const OpenMode mode, const QSslCertificate &cert)
    {
        //Get the current directory
        QString currentDir = QCoreApplication::applicationDirPath();
        QString certDir = currentDir + QDir::separator() + "certs";
    
        if (!QDir(certDir).exists()) {
            QDir(currentDir).mkdir("certs");
            qDebug() << "so_SslClient: No 'certs' folder in app-dir, created one";
        }
    
        //Save the file under the hostname + 'pem' file ending
        QString certNameOnDisk = _hostName + "pem";
    
        //Open the File
        QFile certFile(certDir + QDir::separator() + certNameOnDisk, this);
    
        QString errMsg;
        QFileDevice::FileError err = QFileDevice::NoError;
        
        if(!certFile.exists()) {
            qWarning() << "certFile does not exist";
        }
    
        if (!certFile.open(mode)) {
            errMsg = certFile.errorString();
            err = certFile.error();
    
            qWarning() << "so_SslClient: Error reading cert-File";
            qWarning() << QIODevice::errorString();
            return QSslCertificate();
        }
    
        //Write the file to disk
        if (mode == QIODevice::WriteOnly) {
            if (certFile.write(cert.toPem()) == cert.toPem().length()) {
                qDebug() << "so_SslClient: New trusted certificate written 
                with success";
            }
            return QSslCertificate();
        }
    
        //Read the file from disk
        QSslCertificate localCert = QSslCertificate(&certFile, QSsl::Pem);
        if (localCert.isNull()) {
            qWarning() << "so_SslClient: Certificate file empty";
        }
        return localCert;
    
    }
    
    

    The directories are:

    currentDir: "/data/app/org.qtproject.example.sozius_client-2/lib/arm"
    certDir: "/data/app/org.qtproject.example.sozius_client-2/lib/arm/certs"

    While debugging I get the message

    certFile "" QFile
    			exists	<not callable>	
    				error	No symbol $QFile$ in current context
    

    The directory is created but the QFile certFile is not. I thought it to be a permission issue. This are the permissions in the AndroidManifest.xml file:

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    

    I have tried adding "READ_EXTERNAL_STORAGE" permission. Also I have set permissions with "certFile::setPermissions()" without success. And I pushed my .apk to my device with

    ./adb install -g /path/to/apk /data/local/tmp
    

    My question here is, does this look like a permissions problem with my Android device? If so, would it be a solution to root my device and then give root privileges to my app?



  • AFAIK, applicationDirPath() is not writeable, so you need to install any certificates by the deployment package, or store them somewere else (such as /sdcard/<your_app>)



  • @mvuori thank you for your reply!
    You mean path /data/local/tmp requires special privileges to write to it like in linux /usr/bin? Is that the reason why I can´t write to this path?
    Regarding the deployment package: How would you add the file? As external library? Under assets in the apk?
    I will definitely try to install the file to the sdcard on my phone.
    Do I need to add
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ?



  • @a_so Android apps are sandboxed, you can use the whole file system only in a rooted device. See e.g. https://developer.android.com/guide/topics/data/data-storage.html#filesInternal. Basically you just should give no absolute path. I haven't tried creating files at all but I hope the given link guides you forward.


  • Lifetime Qt Champion

    Hi,

    QStandardPaths can be used to get writable locations.

    Hope it helps



  • What I have tried so far is to add my certificate file under assets and with the resource system (qrc file) to the apk but on non-rooted device. While debugging it tells me that my file exists (as I added it) but that it is not callable.

    Consequently, I will definitely root my device (although I am afraid of crashing it) and give super user privileges to my app created with qt. Would then my file paths be writeable?
    I would also change the path for file creation to one outside of the apk. Has anyone done this so far?
    I will let you know about my progress.



  • @a_so hi,

    first of, @SGaist is right QStandardPath should solve your problem

    QString writableLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    

    in writableLocation you should be able to read and write files as you please, even without permission from the manifest!

    Secendly, do you really need to save it as a file? If the certificate is only used inside your application, you can simply load it from your qrc-file



  • @J.Hilk thank you for pointing that out to me. I read about it in the documentation and tried to use function "locate" with no success.

    I applied your code line and get the following directory:
    "/data/user/0/org.qtproject.example.sozius_client/files"

    Do I understand correctly that in this directory I can create/read/write files?
    If so, I would use this directory as the one to create my certificate file in or maybe push the certificate file with adb shell to this location.

    How do you load a file with qrc on Android?
    I tried this way:

    QFile file(":/icons/icons/192.168.0.7pem");

    ----edit----
    I tried to create my certificate file with

    QString certNameOnDisk = _hostName + "pem";
    QString writeableLocation;
    writeableLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QFile certFile(writeableLocation + QDir::separator() + certNameOnDisk, this);
    
    if(certFile.exists()) {
        qWarning() << "cert file exists";
    }
    else
    {
        qWarning() << "cert file does not exist";
    }
    
    

    Debugging tells me that "cert file does not exist" and in variable certFile it states "not callable - No symbol $QFile$ in current context. "

    ----edit----
    pushing the certfile to the directory of "writeableLocation" gives error

    [staff@vm-centos7 platform-tools]$ ./adb push /media/qt5-qwt6/so_socius/aso1_dbl1/software/qt/qt55/build/Linux/debug/certs/192.168.0.7pem /data/user/0/org.qtproject.example.sozius_client/files
    
    adb: error: failed to copy '/media/qt5-qwt6/so_socius/aso1_dbl1/software/qt/qt55/build/Linux/debug/certs/192.168.0.7pem' to '/data/user/0/org.qtproject.example.sozius_client/files/192.168.0.7pem': remote Permission denied
    /media/qt5-qwt6/so_socius/aso1_dbl1/so...ushed. 0.0 MB/s (1220 bytes in 0.053s)
    
    

  • Lifetime Qt Champion

    Why do you need to write that file on disk ?



  • @SGaist Every time when my client connects to a different host (development board that runs Server application) or the hosts ip changes, there is a test of the SSL certificate implemented. As long as these parameters don't change I do not need to write a file. The certificate is then only compared to existing ones.

    That is why I thought in the first place to add the certificate to qrc or assets then only a read operation would be needed.



  • @a_so said in Failed to create QFile when deploying to Android device:

    QFile certFile

    I believe simply writing

    QFile f(filename);
    

    does not create a file, you'll have ton open the file

    QFile f(filename);
    if(f.open(QIODevice::readOnly))
        f.close();
    


  • @J.Hilk I added the file.open() command in ReadOnly mode. But this does not help as

    if(!file.exists())
    request tells me there is no file under directory
    /data/user/0/org.qtproject.example.sozius_client/files.

    Is there a way that my App could access my SD card and manipulate or just read a file from it?


  • Lifetime Qt Champion

    Do you mean you want to comparing the content of the certificate itself ?


  • Moderators

    @a_so "request tells me there is no file under directory" - I thought you want to create a file?
    "Is there a way that my App could access my SD card and manipulate or just read a file from it?" - yes, using QFile, but you need to do it properly. You need to make sure your app has the rights to write there.



  • @jsulm Yes I want to create a file but as this did not work for me I tried to copy a file in the directory and just open and read it .

    How do I give my app read/write permissions? I have in my AndroidManifest.xml permissions for "READ_EXTERNAL_STORAGE" and "WRITE_EXTERNAL_STORAGE". I have a 8 GB SanDisk ultra SdCard in my Slot. Does the sdcard need to be formatted in a special way?


  • Moderators

    @a_so You wrote: "if(!file.exists())
    request tells me there is no file under directory
    /data/user/0/org.qtproject.example.sozius_client/files."
    Which file do you expect to be where and who is writing this file to this directory?



  • @a_so What API are you using?
    23+ Was kinda broken and you had to grand your app the requested permissions manually in the settings of your Hardwaredevice

    Chances are that this is adressed in Qt5.9. But I haven't tested that yet.



  • @jsulm hi, I tried to write a file to /data/user/0/org.qtproject.example.sozius_client/files as @J-Hilk said that by using QStandardPaths class should enable me to read/write to such a location even without permission from the manifest. Here is the relevant code part

    QString certNameOnDisk = _hostName + "pem";
    
    //Open the File
    QString writeableLocation;
    writeableLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QFile certFile(writeableLocation + QDir::separator() + certNameOnDisk, this);
    
             if(certFile.exists()) {
                qWarning() << "cert file exists";
            }
            else
            {
                qWarning() << "cert file does not exist";
            }
    
            certFile.open(QIODevice::ReadOnly);
            if(certFile.open(QIODevice::ReadOnly)) {
                qWarning() << "certFile is open";
                certFile.close();
            }
          
            if (!certFile.open(mode)) {
                qCritical() << "so_SslClient: Error reading or writing cert-File";
                return QSslCertificate();
            }
    
            //Write the file to disk
            if (mode == QIODevice::WriteOnly) {
                if (certFile.write(cert.toPem()) == cert.toPem().length()) {
                    qDebug() << "so_SslClient: New trusted certificate written with success";
                }
                return QSslCertificate();
            }
    
            //Read the file from disk
            QSslCertificate localCert = QSslCertificate(&certFile, QSsl::Pem);
            if (localCert.isNull()) {
                qWarning() << "so_SslClient: Certificate file empty";
            }
            return localCert;
    }
    

    The file should then be called e.g. 192.168.0.7pem and be located in /data/user/0/org.qtproject.example.sozius_client/files. My app, which is basically a client, should create this file.


  • Moderators

    @a_so said in Failed to create QFile when deploying to Android device:

    QStandardPaths class should enable me to read/write to such a location even without permission from the manifest

    No, it should not - it does not give you any permissions to anything. It can tell you in which directories you have write access.

    Why do you open the file first in read only mode and then in "mode" mode (what ever mode is set to)?
    Did you verify that you can open the file in write mode?



  • @J.Hilk I use API level 23 and Qt 5.8. I also tried level 21 and 22


  • Moderators

    @a_so said in Failed to create QFile when deploying to Android device:

    certFile.open(QIODevice::ReadOnly);
    if(certFile.open(QIODevice::ReadOnly)) {
    qWarning() << "certFile is open";
    certFile.close();
    }

    Why do you open the file twice in read only mode?!



  • @a_so api 23 is Android 6.0 Marshmallow, comes with the problems, you could try a lower one.

    Adjustment of your code sample, to what @jsulm said:

            if(certFile.exists()) {
                qWarning() << "cert file exists";
            }
            else
            {
                qWarning() << "cert file does not exist";
                if(certFile.open(QIODevice::ReadOnly)) {
                        qWarning() << "certFile created";
                        certFile.close();
                }else qWarning() << "certFIle could not be created";
            }
          
            if (!certFile.open(mode)) {
                qCritical() << "so_SslClient: Error reading or writing cert-File";
                return QSslCertificate();
            }
    

    edit:
    Also you return, without closing the file! You shouldn't do that...



  • @jsulm this is the very original code. My client runs also on Linux and Windows. There the folder for certificates is created in the current directory of my project, where .pro and all header and source files are located.

    QSslCertificate so_SslClient::certFileOnDisk(const OpenMode mode, const QSslCertificate &cert)
    {
        //Get the current directory
        QString currentDir = QCoreApplication::applicationDirPath();
        QString certDir = currentDir + QDir::separator() + "certs";
    
        if (!QDir(certDir).exists()) {
            QDir(currentDir).mkdir("certs");
            qDebug() << "so_SslClient: No 'certs' folder in app-dir, created one";
        }
    
        //Save the file under the hostname + 'pem' file ending
        QString certNameOnDisk = _hostName + "pem";
    
        //Open the File
        QFile certFile(certDir + QDir::separator() + certNameOnDisk, this);
        if (!certFile.open(mode)) {
            qCritical() << "so_SslClient: Error reading or writing cert-File";
            return QSslCertificate();
        }
    
        //Write the file to disk
        if (mode == QIODevice::WriteOnly) {
            if (certFile.write(cert.toPem()) == cert.toPem().length()) {
                qDebug() << "so_SslClient: New trusted certificate written with success";
            }
            return QSslCertificate();
        }
    
        //Read the file from disk
        QSslCertificate localCert = QSslCertificate(&certFile, QSsl::Pem);
        if (localCert.isNull()) {
            qWarning() << "so_SslClient: Certificate file empty";
        }
        return localCert;
    }
      
    


  • @jsulm I am sorry. My mistake, not intentionally.



  • Guys thank you so much! The adjusted code from @J-Hilk did it for me + I used target SDK 21. Thank you for your help!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.