7#include <QtCore/QCoreApplication>
8#include <QtCore/QJniEnvironment>
9#include <QtCore/QReadWriteLock>
11#include <private/qjnihelpers_p.h>
15Q_DECLARE_JNI_CLASS(JFileInfo,
"org/qtproject/qt/android/JFileInfo")
16Q_DECLARE_JNI_CLASS(MappedByteBuffer,
"java/nio/MappedByteBuffer")
18using namespace Qt::StringLiterals;
19using namespace QtJniTypes;
20using namespace QNativeInterface;
24struct ApkFileInfosGlobalData
26 QReadWriteLock apkInfosLock;
30Q_GLOBAL_STATIC(ApkFileInfosGlobalData, g_apkFileInfosGlobal)
35 QReadLocker lock(&g_apkFileInfosGlobal->apkInfosLock);
36 if (!g_apkFileInfosGlobal->apkFileInfos.isEmpty())
37 return &g_apkFileInfosGlobal->apkFileInfos;
40 QWriteLocker lock(&g_apkFileInfosGlobal->apkInfosLock);
41 ArrayList arrayList = QtApkFileEngine::callStaticMethod<ArrayList>(
42 "getApkFileInfos", QAndroidApkFileEngine::apkPath());
44 for (
int i = 0; i < arrayList.callMethod<
int>(
"size"); ++i) {
45 JFileInfo jInfo = arrayList.callMethod<jobject>(
"get", i);
47 info.relativePath = jInfo.getField<QString>(
"relativePath");
48 info.size = jInfo.getField<jlong>(
"size");
49 info
.isDir = jInfo.getField<jboolean>(
"isDir");
50 g_apkFileInfosGlobal->apkFileInfos.append(info);
53 return &g_apkFileInfosGlobal->apkFileInfos;
59 setFileName(fileName);
61 QString relativePath = QAndroidApkFileEngine::relativePath(m_fileName);
62 for (QAndroidApkFileEngine::FileInfo &info : *apkFileInfos()) {
63 if (info.relativePath == relativePath) {
77 static QString apkPath = QtApkFileEngine::callStaticMethod<QString>(
"getAppApkFilePath");
83 const static int apkPathPrefixSize = apkPath().size() + 2;
84 return filePath.right(filePath.size() - apkPathPrefixSize);
88 std::optional<QFile::Permissions> permissions)
90 Q_UNUSED(permissions);
92 if (!(openMode & QIODevice::ReadOnly))
95 if (!m_apkFileEngine.isValid() || !m_fileInfo)
98 if (m_fileInfo->relativePath.isEmpty())
101 return m_apkFileEngine.callMethod<
bool>(
"open", m_fileInfo->relativePath);
106 return m_apkFileEngine.isValid() ? m_apkFileEngine.callMethod<
bool>(
"close") :
false;
111 return m_fileInfo ? m_fileInfo->size : -1;
116 return m_apkFileEngine.isValid() ? m_apkFileEngine.callMethod<jlong>(
"pos") : -1;
121 return m_apkFileEngine.isValid() ? m_apkFileEngine.callMethod<
bool>(
"seek", jint(pos)) :
false;
126 if (!m_apkFileEngine.isValid())
129 QJniArray<jbyte> byteArray = m_apkFileEngine.callMethod<jbyte[]>(
"read", jlong(maxlen));
130 if (!byteArray.isValid())
134 env->GetByteArrayRegion(byteArray.arrayObject(), 0, byteArray.size(), (jbyte*)data);
136 if (env.checkAndClearExceptions())
139 return byteArray.size();
145 FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
147 return type & (DirectoryType | commonFlags);
149 return type & (FileType | commonFlags);
158 case CanonicalPathName:
165 case AbsolutePathName:
170 return m_fileName.mid(m_fileName.lastIndexOf(u'/') + 1);
179 if (m_fileName.endsWith(u'/'))
185 if (flags & QFile::MapPrivateOption) {
186 qCritical() <<
"Mapping an in-APK file with private mode is not supported.";
189 if (!m_apkFileEngine.isValid())
192 const MappedByteBuffer mappedBuffer = m_apkFileEngine.callMethod<MappedByteBuffer>(
193 "getMappedByteBuffer", jlong(offset), jlong(size));
195 if (!mappedBuffer.isValid())
198 void *address = QJniEnvironment::getJniEnv()->GetDirectBufferAddress(mappedBuffer.object());
200 return const_cast<uchar *>(
reinterpret_cast<
const uchar *>(address));
204 ExtensionReturn *output)
206 if (extension == MapExtension) {
207 const auto *options =
static_cast<
const MapExtensionOption *>(option);
208 auto *returnValue =
static_cast<MapExtensionReturn *>(output);
209 returnValue->address = map(options->offset, options->size, options->flags);
210 return (returnValue->address !=
nullptr);
217 if (extension == MapExtension)
222#ifndef QT_NO_FILESYSTEMITERATOR
224 const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames)
226 return std::make_unique<QAndroidApkFileEngineIterator>(path, filters, filterNames);
230 const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames)
233 const QString relativePath = QAndroidApkFileEngine::relativePath(path);
234 m_prefixLength = relativePath.size() + 1;
235 for (QAndroidApkFileEngine::FileInfo &info : *apkFileInfos()) {
236 if (info.relativePath.startsWith(relativePath))
237 m_infos.append(&info);
245 if (!m_infos.isEmpty() && m_index < m_infos.size() - 1) {
255 return m_infos.at(m_index)->relativePath.mid(m_prefixLength);
262 if (QtAndroidPrivate::resolveApkPath(fileName).isEmpty())
265 return std::make_unique<QAndroidApkFileEngine>(fileName);
std::unique_ptr< QAbstractFileEngine > create(const QString &fileName) const override
If this file handler can handle fileName, this method creates a file engine and returns it wrapped in...
QString currentFileName() const override
This pure virtual function returns the name of the current directory entry, excluding the path.
bool advance() override
This pure virtual function advances the iterator to the next directory entry; if the operation was su...
~QAndroidApkFileEngineIterator()
QAndroidApkFileEngineIterator(const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames)
IteratorUniquePtr beginEntryList(const QString &, QDirListing::IteratorFlags filters, const QStringList &filterNames) override
Returns a QAbstractFileEngine::IteratorUniquePtr, that can be used to iterate over the entries in pat...
FileFlags fileFlags(FileFlags type=FileInfoAll) const override
This function should return the set of OR'd flags that are true for the file engine's file,...
qint64 size() const override
Returns the size of the file.
void setFileName(const QString &file) override
Sets the file engine's file name to file.
bool supportsExtension(Extension extension) const override
QString fileName(FileName file=DefaultName) const override
Return the file engine's current file name in the format specified by file.
bool close() override
Closes the file, returning true if successful; otherwise returns false.
bool seek(qint64 pos) override
Sets the file position to the given offset.
bool extension(Extension extension, const ExtensionOption *option=nullptr, ExtensionReturn *output=nullptr) override
QAndroidApkFileEngine(const QString &fileName)
qint64 pos() const override
Returns the current file position.
uchar * map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
qint64 read(char *data, qint64 maxlen) override
Reads a number of characters from the file into data.
Combined button and popup list for selecting options.
QList< QAndroidApkFileEngine::FileInfo > ApkFileInfos
static ApkFileInfos * apkFileInfos()