Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qandroidapkfileengine.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:file-handling
4
6
7#include <QtCore/QCoreApplication>
8#include <QtCore/QJniEnvironment>
9#include <QtCore/QReadWriteLock>
10
11#include <private/qjnihelpers_p.h>
12
14
15Q_DECLARE_JNI_CLASS(JFileInfo, "org/qtproject/qt/android/JFileInfo")
16Q_DECLARE_JNI_CLASS(MappedByteBuffer, "java/nio/MappedByteBuffer")
17
18using namespace Qt::StringLiterals;
19using namespace QtJniTypes;
20using namespace QNativeInterface;
21
23namespace {
24struct ApkFileInfosGlobalData
25{
26 QReadWriteLock apkInfosLock;
27 ApkFileInfos apkFileInfos;
28};
29}
30Q_GLOBAL_STATIC(ApkFileInfosGlobalData, g_apkFileInfosGlobal)
31
33{
34 {
35 QReadLocker lock(&g_apkFileInfosGlobal->apkInfosLock);
36 if (!g_apkFileInfosGlobal->apkFileInfos.isEmpty())
37 return &g_apkFileInfosGlobal->apkFileInfos;
38 }
39
40 QWriteLocker lock(&g_apkFileInfosGlobal->apkInfosLock);
41 ArrayList arrayList = QtApkFileEngine::callStaticMethod<ArrayList>(
42 "getApkFileInfos", QAndroidApkFileEngine::apkPath());
43
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);
51 }
52
53 return &g_apkFileInfosGlobal->apkFileInfos;
54}
55
58{
59 setFileName(fileName);
60
61 QString relativePath = QAndroidApkFileEngine::relativePath(m_fileName);
62 for (QAndroidApkFileEngine::FileInfo &info : *apkFileInfos()) {
63 if (info.relativePath == relativePath) {
64 m_fileInfo = &info;
65 break;
66 }
67 }
68}
69
74
75QString QAndroidApkFileEngine::apkPath()
76{
77 static QString apkPath = QtApkFileEngine::callStaticMethod<QString>("getAppApkFilePath");
78 return apkPath;
79}
80
81QString QAndroidApkFileEngine::relativePath(const QString &filePath)
82{
83 const static int apkPathPrefixSize = apkPath().size() + 2;
84 return filePath.right(filePath.size() - apkPathPrefixSize);
85}
86
87bool QAndroidApkFileEngine::open(QIODevice::OpenMode openMode,
88 std::optional<QFile::Permissions> permissions)
89{
90 Q_UNUSED(permissions);
91
92 if (!(openMode & QIODevice::ReadOnly))
93 return false;
94
95 if (!m_apkFileEngine.isValid() || !m_fileInfo)
96 return false;
97
98 if (m_fileInfo->relativePath.isEmpty())
99 return false;
100
101 return m_apkFileEngine.callMethod<bool>("open", m_fileInfo->relativePath);
102}
103
105{
106 return m_apkFileEngine.isValid() ? m_apkFileEngine.callMethod<bool>("close") : false;
107}
108
110{
111 return m_fileInfo ? m_fileInfo->size : -1;
112}
113
115{
116 return m_apkFileEngine.isValid() ? m_apkFileEngine.callMethod<jlong>("pos") : -1;
117}
118
120{
121 return m_apkFileEngine.isValid() ? m_apkFileEngine.callMethod<bool>("seek", jint(pos)) : false;
122}
123
124qint64 QAndroidApkFileEngine::read(char *data, qint64 maxlen)
125{
126 if (!m_apkFileEngine.isValid())
127 return -1;
128
129 QJniArray<jbyte> byteArray = m_apkFileEngine.callMethod<jbyte[]>("read", jlong(maxlen));
130
131 QJniEnvironment env;
132 env->GetByteArrayRegion(byteArray.arrayObject(), 0, byteArray.size(), (jbyte*)data);
133
134 if (env.checkAndClearExceptions())
135 return -1;
136
137 return byteArray.size();
138}
139
141{
142 if (m_fileInfo) {
143 FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
144 if (m_fileInfo->isDir)
145 return type & (DirectoryType | commonFlags);
146 else
147 return type & (FileType | commonFlags);
148 }
149
150 return {};
151}
152
154{
155 switch (file) {
156 case PathName:
157 case AbsolutePathName:
158 case CanonicalPathName:
159 case DefaultName:
160 case AbsoluteName:
161 case CanonicalName:
162 return m_fileName;
163 case BaseName:
164 return m_fileName.mid(m_fileName.lastIndexOf(u'/') + 1);
165 default:
166 return QString();
167 }
168}
169
170void QAndroidApkFileEngine::setFileName(const QString &file)
171{
172 m_fileName = file;
173 if (m_fileName.endsWith(u'/'))
174 m_fileName.chop(1);
175}
176
177uchar *QAndroidApkFileEngine::map(qint64 offset, qint64 size, QFileDevice::MemoryMapFlags flags)
178{
179 if (flags & QFile::MapPrivateOption) {
180 qCritical() << "Mapping an in-APK file with private mode is not supported.";
181 return nullptr;
182 }
183 if (!m_apkFileEngine.isValid())
184 return nullptr;
185
186 const MappedByteBuffer mappedBuffer = m_apkFileEngine.callMethod<MappedByteBuffer>(
187 "getMappedByteBuffer", jlong(offset), jlong(size));
188
189 if (!mappedBuffer.isValid())
190 return nullptr;
191
192 void *address = QJniEnvironment::getJniEnv()->GetDirectBufferAddress(mappedBuffer.object());
193
194 return const_cast<uchar *>(reinterpret_cast<const uchar *>(address));
195}
196
197bool QAndroidApkFileEngine::extension(Extension extension, const ExtensionOption *option,
198 ExtensionReturn *output)
199{
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);
205 }
206 return false;
207}
208
209bool QAndroidApkFileEngine::supportsExtension(Extension extension) const
210{
211 if (extension == MapExtension)
212 return true;
213 return false;
214}
215
216#ifndef QT_NO_FILESYSTEMITERATOR
218 const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames)
219{
220 return std::make_unique<QAndroidApkFileEngineIterator>(path, filters, filterNames);
221}
222
224 const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames)
226{
227 const QString relativePath = QAndroidApkFileEngine::relativePath(path);
228 for (QAndroidApkFileEngine::FileInfo &info : *apkFileInfos()) {
229 if (info.relativePath.startsWith(relativePath))
230 m_infos.append(&info);
231 }
232}
233
235
237{
238 if (!m_infos.isEmpty() && m_index < m_infos.size() - 1) {
239 ++m_index;
240 return true;
241 }
242
243 return false;
244}
245
247{
248 return m_infos.at(m_index)->relativePath;
249}
250
252{
253 return QAndroidApkFileEngine::apkPath() + "!/" + currentFileName();
254}
255#endif
256
258QAndroidApkFileEngineHandler::create(const QString &fileName) const
259{
260 if (QtAndroidPrivate::resolveApkPath(fileName).isEmpty())
261 return {};
262
263 return std::make_unique<QAndroidApkFileEngine>(fileName);
264}
265
266QT_END_NAMESPACE
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(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()