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));
132 env->GetByteArrayRegion(byteArray.arrayObject(), 0, byteArray.size(), (jbyte*)data);
134 if (env.checkAndClearExceptions())
137 return byteArray.size();
143 FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
144 if (m_fileInfo->isDir)
145 return type & (DirectoryType | commonFlags);
147 return type & (FileType | commonFlags);
157 case AbsolutePathName:
158 case CanonicalPathName:
164 return m_fileName.mid(m_fileName.lastIndexOf(u'/') + 1);
173 if (m_fileName.endsWith(u'/'))
179 if (flags & QFile::MapPrivateOption) {
180 qCritical() <<
"Mapping an in-APK file with private mode is not supported.";
183 if (!m_apkFileEngine.isValid())
186 const MappedByteBuffer mappedBuffer = m_apkFileEngine.callMethod<MappedByteBuffer>(
187 "getMappedByteBuffer", jlong(offset), jlong(size));
189 if (!mappedBuffer.isValid())
192 void *address = QJniEnvironment::getJniEnv()->GetDirectBufferAddress(mappedBuffer.object());
194 return const_cast<uchar *>(
reinterpret_cast<
const uchar *>(address));
198 ExtensionReturn *output)
200 if (extension == MapExtension) {
201 const auto *options =
static_cast<
const MapExtensionOption *>(option);
202 auto *returnValue =
static_cast<MapExtensionReturn *>(output);
203 returnValue->address = map(options->offset, options->size, options->flags);
204 return (returnValue->address !=
nullptr);
211 if (extension == MapExtension)
216#ifndef QT_NO_FILESYSTEMITERATOR
218 const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames)
220 return std::make_unique<QAndroidApkFileEngineIterator>(path, filters, filterNames);
224 const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames)
227 const QString relativePath = QAndroidApkFileEngine::relativePath(path);
228 for (QAndroidApkFileEngine::FileInfo &info : *apkFileInfos()) {
229 if (info.relativePath.startsWith(relativePath))
230 m_infos.append(&info);
238 if (!m_infos.isEmpty() && m_index < m_infos.size() - 1) {
248 return m_infos.at(m_index)->relativePath;
253 return QAndroidApkFileEngine::apkPath() +
"!/" + currentFileName();
260 if (QtAndroidPrivate::resolveApkPath(fileName).isEmpty())
263 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.
QString currentFilePath() const override
Returns the path to the current directory entry.
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.
QList< QAndroidApkFileEngine::FileInfo > ApkFileInfos
static ApkFileInfos * apkFileInfos()