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);
146 if (m_fileInfo->isDir)
147 return type & (DirectoryType | commonFlags);
149 return type & (FileType | commonFlags);
159 case AbsolutePathName:
160 case CanonicalPathName:
166 return m_fileName.mid(m_fileName.lastIndexOf(u'/') + 1);
175 if (m_fileName.endsWith(u'/'))
181 if (flags & QFile::MapPrivateOption) {
182 qCritical() <<
"Mapping an in-APK file with private mode is not supported.";
185 if (!m_apkFileEngine.isValid())
188 const MappedByteBuffer mappedBuffer = m_apkFileEngine.callMethod<MappedByteBuffer>(
189 "getMappedByteBuffer", jlong(offset), jlong(size));
191 if (!mappedBuffer.isValid())
194 void *address = QJniEnvironment::getJniEnv()->GetDirectBufferAddress(mappedBuffer.object());
196 return const_cast<uchar *>(
reinterpret_cast<
const uchar *>(address));
200 ExtensionReturn *output)
202 if (extension == MapExtension) {
203 const auto *options =
static_cast<
const MapExtensionOption *>(option);
204 auto *returnValue =
static_cast<MapExtensionReturn *>(output);
205 returnValue->address = map(options->offset, options->size, options->flags);
206 return (returnValue->address !=
nullptr);
213 if (extension == MapExtension)
218#ifndef QT_NO_FILESYSTEMITERATOR
220 const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames)
222 return std::make_unique<QAndroidApkFileEngineIterator>(path, filters, filterNames);
226 const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames)
229 const QString relativePath = QAndroidApkFileEngine::relativePath(path);
230 m_prefixLength = relativePath.size() + 1;
231 for (QAndroidApkFileEngine::FileInfo &info : *apkFileInfos()) {
232 if (info.relativePath.startsWith(relativePath))
233 m_infos.append(&info);
241 if (!m_infos.isEmpty() && m_index < m_infos.size() - 1) {
251 return m_infos.at(m_index)->relativePath.mid(m_prefixLength);
258 if (QtAndroidPrivate::resolveApkPath(fileName).isEmpty())
261 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()