blob: de1d514d0a5b51fe64b156ae9280afef70e47bb7 [file] [log] [blame]
Adam Lesinskibebfcc42018-02-12 14:27:46 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package android.content.res;
17
18import android.annotation.NonNull;
Winson9947f1e2019-08-16 10:20:39 -070019import android.annotation.Nullable;
Mathew Inwood5c0d3542018-08-14 13:54:31 +010020import android.annotation.UnsupportedAppUsage;
Winson9947f1e2019-08-16 10:20:39 -070021import android.content.res.loader.ResourcesProvider;
22import android.text.TextUtils;
Adam Lesinskibebfcc42018-02-12 14:27:46 -080023
24import com.android.internal.annotations.GuardedBy;
25import com.android.internal.util.Preconditions;
26
27import java.io.FileDescriptor;
28import java.io.IOException;
29
30/**
31 * The loaded, immutable, in-memory representation of an APK.
32 *
33 * The main implementation is native C++ and there is very little API surface exposed here. The APK
34 * is mainly accessed via {@link AssetManager}.
35 *
36 * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers,
37 * making the creation of AssetManagers very cheap.
38 * @hide
39 */
40public final class ApkAssets {
41 @GuardedBy("this") private final long mNativePtr;
Winson9947f1e2019-08-16 10:20:39 -070042
43 @Nullable
Winson7a3d82a2019-03-07 12:54:24 -080044 @GuardedBy("this") private final StringBlock mStringBlock;
45
46 @GuardedBy("this") private boolean mOpen = true;
Adam Lesinskibebfcc42018-02-12 14:27:46 -080047
Winson9947f1e2019-08-16 10:20:39 -070048 private final boolean mForLoader;
49
Adam Lesinskibebfcc42018-02-12 14:27:46 -080050 /**
51 * Creates a new ApkAssets instance from the given path on disk.
52 *
53 * @param path The path to an APK on disk.
54 * @return a new instance of ApkAssets.
55 * @throws IOException if a disk I/O error or parsing error occurred.
56 */
57 public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException {
Winson9947f1e2019-08-16 10:20:39 -070058 return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/,
59 false /*arscOnly*/, false /*forLoader*/);
Adam Lesinskibebfcc42018-02-12 14:27:46 -080060 }
61
62 /**
63 * Creates a new ApkAssets instance from the given path on disk.
64 *
65 * @param path The path to an APK on disk.
66 * @param system When true, the APK is loaded as a system APK (framework).
67 * @return a new instance of ApkAssets.
68 * @throws IOException if a disk I/O error or parsing error occurred.
69 */
70 public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system)
71 throws IOException {
Winson9947f1e2019-08-16 10:20:39 -070072 return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/,
73 false /*arscOnly*/, false /*forLoader*/);
Adam Lesinskibebfcc42018-02-12 14:27:46 -080074 }
75
76 /**
77 * Creates a new ApkAssets instance from the given path on disk.
78 *
79 * @param path The path to an APK on disk.
80 * @param system When true, the APK is loaded as a system APK (framework).
81 * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
82 * loaded as a shared library.
83 * @return a new instance of ApkAssets.
84 * @throws IOException if a disk I/O error or parsing error occurred.
85 */
86 public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system,
87 boolean forceSharedLibrary) throws IOException {
Winson9947f1e2019-08-16 10:20:39 -070088 return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/,
89 false /*arscOnly*/, false /*forLoader*/);
Adam Lesinskibebfcc42018-02-12 14:27:46 -080090 }
91
92 /**
93 * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications.
94 *
95 * Performs a dup of the underlying fd, so you must take care of still closing
96 * the FileDescriptor yourself (and can do that whenever you want).
97 *
98 * @param fd The FileDescriptor of an open, readable APK.
99 * @param friendlyName The friendly name used to identify this ApkAssets when logging.
100 * @param system When true, the APK is loaded as a system APK (framework).
101 * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
102 * loaded as a shared library.
103 * @return a new instance of ApkAssets.
104 * @throws IOException if a disk I/O error or parsing error occurred.
105 */
106 public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
107 @NonNull String friendlyName, boolean system, boolean forceSharedLibrary)
108 throws IOException {
Winson9947f1e2019-08-16 10:20:39 -0700109 return new ApkAssets(fd, friendlyName, system, forceSharedLibrary, false /*arscOnly*/,
110 false /*forLoader*/);
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800111 }
112
113 /**
114 * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path
115 * is encoded within the IDMAP.
116 *
117 * @param idmapPath Path to the IDMAP of an overlay APK.
118 * @param system When true, the APK is loaded as a system APK (framework).
119 * @return a new instance of ApkAssets.
120 * @throws IOException if a disk I/O error or parsing error occurred.
121 */
122 public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system)
123 throws IOException {
Winson9947f1e2019-08-16 10:20:39 -0700124 return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/,
125 false /*arscOnly*/, false /*forLoader*/);
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800126 }
127
Winson9947f1e2019-08-16 10:20:39 -0700128 /**
129 * Creates a new ApkAssets instance from the given path on disk for use with a
130 * {@link ResourcesProvider}.
131 *
132 * @param path The path to an APK on disk.
133 * @return a new instance of ApkAssets.
134 * @throws IOException if a disk I/O error or parsing error occurred.
135 */
136 public static @NonNull ApkAssets loadApkForLoader(@NonNull String path)
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800137 throws IOException {
Winson9947f1e2019-08-16 10:20:39 -0700138 return new ApkAssets(path, false /*system*/, false /*forceSharedLibrary*/,
139 false /*overlay*/, false /*arscOnly*/, true /*forLoader*/);
140 }
141
142 /**
143 * Creates a new ApkAssets instance from the given file descriptor for use with a
144 * {@link ResourcesProvider}.
145 *
146 * Performs a dup of the underlying fd, so you must take care of still closing
147 * the FileDescriptor yourself (and can do that whenever you want).
148 *
149 * @param fd The FileDescriptor of an open, readable APK.
150 * @return a new instance of ApkAssets.
151 * @throws IOException if a disk I/O error or parsing error occurred.
152 */
153 @NonNull
154 public static ApkAssets loadApkForLoader(@NonNull FileDescriptor fd) throws IOException {
155 return new ApkAssets(fd, TextUtils.emptyIfNull(fd.toString()),
156 false /*system*/, false /*forceSharedLib*/, false /*arscOnly*/, true /*forLoader*/);
157 }
158
159 /**
160 * Creates a new ApkAssets instance from the given file descriptor representing an ARSC
161 * for use with a {@link ResourcesProvider}.
162 *
163 * Performs a dup of the underlying fd, so you must take care of still closing
164 * the FileDescriptor yourself (and can do that whenever you want).
165 *
166 * @param fd The FileDescriptor of an open, readable .arsc.
167 * @return a new instance of ApkAssets.
168 * @throws IOException if a disk I/O error or parsing error occurred.
169 */
170 public static @NonNull ApkAssets loadArscForLoader(@NonNull FileDescriptor fd)
171 throws IOException {
172 return new ApkAssets(fd, TextUtils.emptyIfNull(fd.toString()),
173 false /*system*/, false /*forceSharedLib*/, true /*arscOnly*/, true /*forLoader*/);
174 }
175
176 /**
177 * Generates an entirely empty ApkAssets. Needed because the ApkAssets instance and presence
178 * is required for a lot of APIs, and it's easier to have a non-null reference rather than
179 * tracking a separate identifier.
180 */
181 @NonNull
182 public static ApkAssets loadEmptyForLoader() {
183 return new ApkAssets(true);
184 }
185
186 private ApkAssets(boolean forLoader) {
187 mForLoader = forLoader;
188 mNativePtr = nativeLoadEmpty(forLoader);
189 mStringBlock = null;
190 }
191
192 private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay,
193 boolean arscOnly, boolean forLoader) throws IOException {
194 mForLoader = forLoader;
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800195 Preconditions.checkNotNull(path, "path");
Winson9947f1e2019-08-16 10:20:39 -0700196 mNativePtr = arscOnly ? nativeLoadArsc(path, forLoader)
197 : nativeLoad(path, system, forceSharedLib, overlay, forLoader);
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800198 mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
199 }
200
201 private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system,
Winson9947f1e2019-08-16 10:20:39 -0700202 boolean forceSharedLib, boolean arscOnly, boolean forLoader) throws IOException {
203 mForLoader = forLoader;
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800204 Preconditions.checkNotNull(fd, "fd");
205 Preconditions.checkNotNull(friendlyName, "friendlyName");
Winson9947f1e2019-08-16 10:20:39 -0700206 mNativePtr = arscOnly ? nativeLoadArscFromFd(fd, friendlyName, forLoader)
207 : nativeLoadFromFd(fd, friendlyName, system, forceSharedLib, forLoader);
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800208 mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
209 }
210
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100211 @UnsupportedAppUsage
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800212 public @NonNull String getAssetPath() {
213 synchronized (this) {
214 return nativeGetAssetPath(mNativePtr);
215 }
216 }
217
218 CharSequence getStringFromPool(int idx) {
Winson9947f1e2019-08-16 10:20:39 -0700219 if (mStringBlock == null) {
220 return null;
221 }
222
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800223 synchronized (this) {
224 return mStringBlock.get(idx);
225 }
226 }
227
Winson9947f1e2019-08-16 10:20:39 -0700228 public boolean isForLoader() {
229 return mForLoader;
230 }
231
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800232 /**
233 * Retrieve a parser for a compiled XML file. This is associated with a single APK and
234 * <em>NOT</em> a full AssetManager. This means that shared-library references will not be
235 * dynamically assigned runtime package IDs.
236 *
237 * @param fileName The path to the file within the APK.
238 * @return An XmlResourceParser.
239 * @throws IOException if the file was not found or an error occurred retrieving it.
240 */
241 public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException {
242 Preconditions.checkNotNull(fileName, "fileName");
243 synchronized (this) {
244 long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName);
245 try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) {
246 XmlResourceParser parser = block.newParser();
247 // If nativeOpenXml doesn't throw, it will always return a valid native pointer,
248 // which makes newParser always return non-null. But let's be paranoid.
249 if (parser == null) {
250 throw new AssertionError("block.newParser() returned a null parser");
251 }
252 return parser;
253 }
254 }
255 }
256
257 /**
258 * Returns false if the underlying APK was changed since this ApkAssets was loaded.
259 */
260 public boolean isUpToDate() {
261 synchronized (this) {
262 return nativeIsUpToDate(mNativePtr);
263 }
264 }
265
266 @Override
267 public String toString() {
268 return "ApkAssets{path=" + getAssetPath() + "}";
269 }
270
271 @Override
272 protected void finalize() throws Throwable {
Winson7a3d82a2019-03-07 12:54:24 -0800273 close();
274 }
275
276 /**
277 * Closes this class and the contained {@link #mStringBlock}.
278 */
Ryan Mitchell4043ca72019-06-03 16:11:24 -0700279 public void close() {
Winson7a3d82a2019-03-07 12:54:24 -0800280 synchronized (this) {
281 if (mOpen) {
282 mOpen = false;
Winson9947f1e2019-08-16 10:20:39 -0700283 if (mStringBlock != null) {
284 mStringBlock.close();
285 }
Winson7a3d82a2019-03-07 12:54:24 -0800286 nativeDestroy(mNativePtr);
287 }
288 }
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800289 }
290
Winson9947f1e2019-08-16 10:20:39 -0700291 private static native long nativeLoad(@NonNull String path, boolean system,
292 boolean forceSharedLib, boolean overlay, boolean forLoader)
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800293 throws IOException;
294 private static native long nativeLoadFromFd(@NonNull FileDescriptor fd,
Winson9947f1e2019-08-16 10:20:39 -0700295 @NonNull String friendlyName, boolean system, boolean forceSharedLib,
296 boolean forLoader)
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800297 throws IOException;
Winson9947f1e2019-08-16 10:20:39 -0700298 private static native long nativeLoadArsc(@NonNull String path, boolean forLoader)
299 throws IOException;
300 private static native long nativeLoadArscFromFd(@NonNull FileDescriptor fd,
301 @NonNull String friendlyName, boolean forLoader) throws IOException;
302 private static native long nativeLoadEmpty(boolean forLoader);
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800303 private static native void nativeDestroy(long ptr);
304 private static native @NonNull String nativeGetAssetPath(long ptr);
305 private static native long nativeGetStringBlock(long ptr);
306 private static native boolean nativeIsUpToDate(long ptr);
307 private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException;
308}