From b81df3e65808409e6840198a744d7053a463e251 Mon Sep 17 00:00:00 2001
From: Sharaf Zaman <sharafzaz121@gmail.com>
Date: Mon, 21 Sep 2020 11:06:37 +0000
Subject: [PATCH] Android: Properly close the File Descriptor

In some we are not supposed to detach the file
descriptor from the original object that created it.
With this patch, we save the don't detach, but
save the ParcelFileDescriptor in a Map and close it
when we receive close() from C++.

Direct effect of this can be seen in Krita, where some
content providers would not allow writing to some detached
FD.
---
 .../org/qtproject/qt5/android/QtNative.java   | 29 +++++++++++++++++--
 .../android/androidcontentfileengine.cpp      | 17 +++++++++--
 .../android/androidcontentfileengine.h        |  6 ++++
 3 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java
index 688c8146bf..0ae113d1a5 100644
--- a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java
+++ b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java
@@ -43,6 +43,7 @@ package org.qtproject.qt5.android;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.concurrent.Semaphore;
 import java.io.IOException;
 
@@ -111,6 +112,9 @@ public class QtNative
     private static boolean m_usePrimaryClip = false;
     public static QtThread m_qtThread = new QtThread();
     private static Method m_addItemMethod = null;
+
+    private static HashMap<Integer, ParcelFileDescriptor> m_parcelFileDescriptors = new HashMap<Integer, ParcelFileDescriptor>();
+
     private static final Runnable runPendingCppRunnablesRunnable = new Runnable() {
         @Override
         public void run() {
@@ -176,8 +180,9 @@ public class QtNative
                 if (!openMode.equals("r"))
                    isRightPermission = permissions.get(i).isWritePermission();
 
-                if (iterUri.getPath().equals(uriStr) && isRightPermission)
+                if (iterUri.getPath().equals(uriStr) && isRightPermission) {
                     return iterUri;
+                }
             }
 
             return null;
@@ -230,7 +235,8 @@ public class QtNative
         try {
             ContentResolver resolver = context.getContentResolver();
             ParcelFileDescriptor fdDesc = resolver.openFileDescriptor(uri, openMode);
-            return fdDesc.detachFd();
+            m_parcelFileDescriptors.put(fdDesc.getFd(), fdDesc);
+            return fdDesc.getFd();
         } catch (FileNotFoundException e) {
             return error;
         } catch (IllegalArgumentException e) {
@@ -239,6 +245,23 @@ public class QtNative
         }
     }
 
+    public static boolean closeFd(int fd)
+    {
+        ParcelFileDescriptor pfd = m_parcelFileDescriptors.get(fd);
+        if (pfd == null) {
+            Log.wtf(QtTAG, "File descriptor doesn't exist in cache");
+            return false;
+        }
+
+        try {
+            pfd.close();
+            return true;
+        } catch (IOException e) {
+            Log.e(QtTAG, "closeFd(): Failed to close the FD", e);
+            return false;
+        }
+    }
+
     public static long getSize(Context context, String contentUrl)
     {
         Uri uri = getUriWithValidPermission(context, contentUrl, "r");
@@ -299,7 +322,7 @@ public class QtNative
     {
         Uri uri = getUriWithValidPermission(context, contentUrl, "r");
         if (uri == null) {
-            Log.e(QtTAG, "getFileNameFromUri(): No permissions to open Uri");
+            Log.e(QtTAG, "getFileNameFromUri(): No permissions to open Uri:" + contentUrl);
             return null;
         }
 
diff --git a/src/plugins/platforms/android/androidcontentfileengine.cpp b/src/plugins/platforms/android/androidcontentfileengine.cpp
index 31f68780df..cdefacfaf9 100644
--- a/src/plugins/platforms/android/androidcontentfileengine.cpp
+++ b/src/plugins/platforms/android/androidcontentfileengine.cpp
@@ -45,7 +45,7 @@
 #include <QDebug>
 
 AndroidContentFileEngine::AndroidContentFileEngine(const QString &f)
-    : m_file(f), m_resolvedName(QString())
+    : m_fd(-1), m_file(f), m_resolvedName(QString())
 {
     setFileName(f);
     setResolvedFileName(f);
@@ -77,7 +77,15 @@ bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode)
         return false;
     }
 
-    return QFSFileEngine::open(openMode, fd, QFile::AutoCloseHandle);
+    setFileDescriptor(fd);
+    return QFSFileEngine::open(openMode, m_fd, QFile::AutoCloseHandle);
+}
+
+bool AndroidContentFileEngine::close()
+{
+    return QJNIObjectPrivate::callStaticMethod<jboolean>(
+        "org/qtproject/qt5/android/QtNative", "closeFd",
+        "(I)Z", m_fd);
 }
 
 qint64 AndroidContentFileEngine::size() const
@@ -141,6 +149,11 @@ void AndroidContentFileEngine::setResolvedFileName(const QString& uri)
     }
 }
 
+void AndroidContentFileEngine::setFileDescriptor(const int fd)
+{
+    m_fd = fd;
+}
+
 
 AndroidContentFileEngineHandler::AndroidContentFileEngineHandler() = default;
 AndroidContentFileEngineHandler::~AndroidContentFileEngineHandler() = default;
diff --git a/src/plugins/platforms/android/androidcontentfileengine.h b/src/plugins/platforms/android/androidcontentfileengine.h
index bb97bd6975..abf969ba20 100644
--- a/src/plugins/platforms/android/androidcontentfileengine.h
+++ b/src/plugins/platforms/android/androidcontentfileengine.h
@@ -47,13 +47,19 @@ class AndroidContentFileEngine : public QFSFileEngine
 public:
     AndroidContentFileEngine(const QString &fileName);
     bool open(QIODevice::OpenMode openMode) override;
+    bool close() override;
     qint64 size() const override;
     FileFlags fileFlags(FileFlags type = FileInfoAll) const override;
     QString fileName(FileName file = DefaultName) const override;
 
     /// Resolves the URI to the actual filename
     void setResolvedFileName(const QString& uri);
+
+private:
+    void setFileDescriptor(const int fd);
+
 private:
+    int m_fd;
     QString m_file;
     QString m_resolvedName;
 };
-- 
2.28.0

