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 if (!byteArray.isValid())
131 return -1;
132
133 QJniEnvironment env;
134 env->GetByteArrayRegion(byteArray.arrayObject(), 0, byteArray.size(), (jbyte*)data);
135
136 if (env.checkAndClearExceptions())
137 return -1;
138
139 return byteArray.size();
140}
141
143{
144 if (m_fileInfo) {
145 FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
146 if (m_fileInfo->isDir)
147 return type & (DirectoryType | commonFlags);
148 else
149 return type & (FileType | commonFlags);
150 }
151
152 return {};
153}
154
156{
157 switch (file) {
158 case PathName:
159 case AbsolutePathName:
160 case CanonicalPathName:
161 case DefaultName:
162 case AbsoluteName:
163 case CanonicalName:
164 return m_fileName;
165 case BaseName:
166 return m_fileName.mid(m_fileName.lastIndexOf(u'/') + 1);
167 default:
168 return QString();
169 }
170}
171
172void QAndroidApkFileEngine::setFileName(const QString &file)
173{
174 m_fileName = file;
175 if (m_fileName.endsWith(u'/'))
176 m_fileName.chop(1);
177}
178
179uchar *QAndroidApkFileEngine::map(qint64 offset, qint64 size, QFileDevice::MemoryMapFlags flags)
180{
181 if (flags & QFile::MapPrivateOption) {
182 qCritical() << "Mapping an in-APK file with private mode is not supported.";
183 return nullptr;
184 }
185 if (!m_apkFileEngine.isValid())
186 return nullptr;
187
188 const MappedByteBuffer mappedBuffer = m_apkFileEngine.callMethod<MappedByteBuffer>(
189 "getMappedByteBuffer", jlong(offset), jlong(size));
190
191 if (!mappedBuffer.isValid())
192 return nullptr;
193
194 void *address = QJniEnvironment::getJniEnv()->GetDirectBufferAddress(mappedBuffer.object());
195
196 return const_cast<uchar *>(reinterpret_cast<const uchar *>(address));
197}
198
199bool QAndroidApkFileEngine::extension(Extension extension, const ExtensionOption *option,
200 ExtensionReturn *output)
201{
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);
207 }
208 return false;
209}
210
211bool QAndroidApkFileEngine::supportsExtension(Extension extension) const
212{
213 if (extension == MapExtension)
214 return true;
215 return false;
216}
217
218#ifndef QT_NO_FILESYSTEMITERATOR
220 const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames)
221{
222 return std::make_unique<QAndroidApkFileEngineIterator>(path, filters, filterNames);
223}
224
226 const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames)
228{
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);
234 }
235}
236
238
240{
241 if (!m_infos.isEmpty() && m_index < m_infos.size() - 1) {
242 ++m_index;
243 return true;
244 }
245
246 return false;
247}
248
250{
251 return m_infos.at(m_index)->relativePath.mid(m_prefixLength);
252}
253#endif
254
256QAndroidApkFileEngineHandler::create(const QString &fileName) const
257{
258 if (QtAndroidPrivate::resolveApkPath(fileName).isEmpty())
259 return {};
260
261 return std::make_unique<QAndroidApkFileEngine>(fileName);
262}
263
264QT_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.
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.
Combined button and popup list for selecting options.
QList< QAndroidApkFileEngine::FileInfo > ApkFileInfos
static ApkFileInfos * apkFileInfos()