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
QtApkFileEngine.java
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
5package org.qtproject.qt.android;
6
7import android.content.Context;
8import android.content.res.AssetFileDescriptor;
9import android.content.res.AssetManager;
10import android.os.Build;
11import android.content.pm.ApplicationInfo;
12import android.content.pm.PackageManager;
13
14import java.io.FileInputStream;
15import java.io.ByteArrayOutputStream;
16import java.io.IOException;
17
18import java.util.zip.ZipFile;
19import java.util.Enumeration;
20import java.util.zip.ZipEntry;
21import java.util.ArrayList;
22import java.util.Arrays;
23import java.util.HashSet;
24import java.util.Comparator;
25
26import android.util.Log;
27
28import java.nio.channels.FileChannel;
29import java.nio.channels.FileChannel.MapMode;
30import java.nio.MappedByteBuffer;
31import java.nio.ByteOrder;
32
34class QtApkFileEngine {
35 private final static String QtTAG = QtApkFileEngine.class.getSimpleName();
36 private static String m_appApkPath;
37
38 private AssetFileDescriptor m_assetFd;
39 private final AssetManager m_assetManager;
40 private FileInputStream m_assetInputStream;
41 private long m_pos = -1;
42
43 QtApkFileEngine(Context context)
44 {
45 m_assetManager = context.getAssets();
46 }
47
48 boolean open(String fileName)
49 {
50 try {
51 m_assetFd = m_assetManager.openNonAssetFd(fileName);
52 m_assetInputStream = m_assetFd.createInputStream();
53 } catch (IOException e) {
54 Log.e(QtTAG, "Failed to open the app APK with " + e);
55 }
56
57 return m_assetInputStream != null;
58 }
59
60 boolean close()
61 {
62 try {
63 if (m_assetInputStream != null)
64 m_assetInputStream.close();
65 if (m_assetFd != null)
66 m_assetFd.close();
67 } catch (IOException e) {
68 Log.e(QtTAG, "Failed to close resources with " + e);
69 }
70
71 return m_assetInputStream == null && m_assetFd == null;
72 }
73
74 long pos()
75 {
76 return m_pos;
77 }
78
79 boolean seek(int pos)
80 {
81 if (m_assetInputStream != null && m_assetInputStream.markSupported()) {
82 try {
83 m_assetInputStream.mark(pos);
84 m_assetInputStream.reset();
85 m_pos = pos;
86 return true;
87 } catch (IOException ignored) { }
88 }
89
90 return false;
91 }
92
93 MappedByteBuffer getMappedByteBuffer(long offset, long size)
94 {
95 try {
96 FileChannel fileChannel = m_assetInputStream.getChannel();
97 long position = fileChannel.position() + offset;
98 MappedByteBuffer mapped = fileChannel.map(MapMode.READ_ONLY, position, size);
99 mapped.order(ByteOrder.LITTLE_ENDIAN);
100 fileChannel.close();
101
102 return mapped;
103 } catch (Exception e) {
104 Log.e(QtTAG, "Failed to map APK file to memory with " + e);
105 }
106
107 return null;
108 }
109
110 byte[] read(long maxlen)
111 {
112 if (m_assetInputStream == null)
113 return null;
114
115 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
116 int bytesRead;
117 int totalBytesRead = 0;
118 byte[] buffer = new byte[1024];
119 try {
120 while (totalBytesRead < maxlen) {
121 int remainingBytes = (int) maxlen - totalBytesRead;
122 int bytesToRead = Math.min(buffer.length, remainingBytes);
123 if ((bytesRead = m_assetInputStream.read(buffer, 0, bytesToRead)) == -1)
124 break;
125 outputStream.write(buffer, 0, bytesRead);
126 totalBytesRead += bytesRead;
127 }
128
129 outputStream.close();
130 } catch (IOException e) {
131 Log.e(QtTAG, "Failed to read content with " + e);
132 }
133
134 return outputStream.toByteArray();
135 }
136
137 static String getAppApkFilePath()
138 {
139 if (m_appApkPath != null)
140 return m_appApkPath;
141
142 try {
143 Context context = QtNative.getContext();
144 PackageManager pm = context.getPackageManager();
145 ApplicationInfo applicationInfo = pm.getApplicationInfo(context.getPackageName(), 0);
146 if (applicationInfo.splitSourceDirs != null) {
147 m_appApkPath = Arrays.stream(applicationInfo.splitSourceDirs)
148 .filter(file -> Arrays.stream(Build.SUPPORTED_ABIS)
149 .anyMatch(abi -> file.endsWith(abi.replace('-', '_') + ".apk")))
150 .findFirst()
151 .orElse(null);
152
153 if (m_appApkPath == null)
154 Log.d(QtTAG, "No ABI specific split APK found, defaulting to the main APK.");
155 }
156
157 if (m_appApkPath == null)
158 m_appApkPath = applicationInfo.sourceDir;
159 } catch (PackageManager.NameNotFoundException e) {
160 Log.e(QtTAG, "Failed to get the app APK path with " + e);
161 return null;
162 }
163 return m_appApkPath;
164 }
165
166 static class JFileInfo
167 {
168 String relativePath;
169 boolean isDir;
170 long size;
171 }
172
173 static ArrayList<JFileInfo> getApkFileInfos(String apkPath)
174 {
175 ArrayList<JFileInfo> fileInfos = new ArrayList<>();
176 HashSet<String> dirSet = new HashSet<>();
177 HashSet<String> allDirsSet = new HashSet<>();
178
179 try (ZipFile zipFile = new ZipFile(apkPath)) {
180 Enumeration<? extends ZipEntry> enumerator = zipFile.entries();
181 while (enumerator.hasMoreElements()) {
182 ZipEntry entry = enumerator.nextElement();
183 String name = entry.getName();
184
185 // Limit the listing to lib directory
186 if (name.startsWith("lib/")) {
187 JFileInfo info = new JFileInfo();
188 info.relativePath = name;
189 info.isDir = entry.isDirectory();
190 info.size = entry.getSize();
191 fileInfos.add(info);
192
193 // check directories
194 dirSet.add(name.substring(0, name.lastIndexOf("/") + 1));
195 }
196 }
197
198 // ZipFile iterator doesn't seem to add directories, so add them manually.
199 for (String path : dirSet) {
200 int index = 0;
201 while ((index = path.indexOf("/", index + 1)) != -1) {
202 String dir = path.substring(0, index);
203 allDirsSet.add(dir);
204 }
205 }
206
207 for (String dir : allDirsSet) {
208 JFileInfo info = new JFileInfo();
209 info.relativePath = dir;
210 info.isDir = true;
211 info.size = -1;
212 fileInfos.add(info);
213 }
214
215 // sort alphabetically based on the file path
216 fileInfos.sort(Comparator.comparing(info -> info.relativePath));
217 } catch (Exception e) {
218 Log.e(QtTAG, "Failed to list App's APK files with " + e);
219 }
220
221 return fileInfos;
222 }
223}
QPainter Context
static const QString context()
Definition java.cpp:398
QFuture< QtPrivate::MapResultType< Sequence, MapFunctor > > mapped(QThreadPool *pool, Sequence &&sequence, MapFunctor &&map)
GLenum GLuint GLintptr GLsizeiptr size
GLuint index
GLenum GLuint GLintptr offset
GLuint entry
GLsizei const GLchar *const * path
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
EGLContext EGLenum EGLClientBuffer buffer
EGLImageKHR EGLint * name
QHostInfo info
[0]