blob: ad375552837d53dd9990b19d1b668b65632f7201 [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;
Winsond9d17362019-10-02 12:41:29 -070021import android.content.om.OverlayableInfo;
Winson9947f1e2019-08-16 10:20:39 -070022import android.content.res.loader.ResourcesProvider;
23import android.text.TextUtils;
Adam Lesinskibebfcc42018-02-12 14:27:46 -080024
25import com.android.internal.annotations.GuardedBy;
26import com.android.internal.util.Preconditions;
27
28import java.io.FileDescriptor;
29import java.io.IOException;
30
31/**
32 * The loaded, immutable, in-memory representation of an APK.
33 *
34 * The main implementation is native C++ and there is very little API surface exposed here. The APK
35 * is mainly accessed via {@link AssetManager}.
36 *
37 * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers,
38 * making the creation of AssetManagers very cheap.
39 * @hide
40 */
41public final class ApkAssets {
42 @GuardedBy("this") private final long mNativePtr;
Winson9947f1e2019-08-16 10:20:39 -070043
44 @Nullable
Winson7a3d82a2019-03-07 12:54:24 -080045 @GuardedBy("this") private final StringBlock mStringBlock;
46
47 @GuardedBy("this") private boolean mOpen = true;
Adam Lesinskibebfcc42018-02-12 14:27:46 -080048
Winson9947f1e2019-08-16 10:20:39 -070049 private final boolean mForLoader;
50
Adam Lesinskibebfcc42018-02-12 14:27:46 -080051 /**
52 * Creates a new ApkAssets instance from the given path on disk.
53 *
54 * @param path The path to an APK on disk.
55 * @return a new instance of ApkAssets.
56 * @throws IOException if a disk I/O error or parsing error occurred.
57 */
58 public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException {
Winson9947f1e2019-08-16 10:20:39 -070059 return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/,
60 false /*arscOnly*/, false /*forLoader*/);
Adam Lesinskibebfcc42018-02-12 14:27:46 -080061 }
62
63 /**
64 * Creates a new ApkAssets instance from the given path on disk.
65 *
66 * @param path The path to an APK on disk.
67 * @param system When true, the APK is loaded as a system APK (framework).
68 * @return a new instance of ApkAssets.
69 * @throws IOException if a disk I/O error or parsing error occurred.
70 */
71 public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system)
72 throws IOException {
Winson9947f1e2019-08-16 10:20:39 -070073 return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/,
74 false /*arscOnly*/, false /*forLoader*/);
Adam Lesinskibebfcc42018-02-12 14:27:46 -080075 }
76
77 /**
78 * Creates a new ApkAssets instance from the given path on disk.
79 *
80 * @param path The path to an APK on disk.
81 * @param system When true, the APK is loaded as a system APK (framework).
82 * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
83 * loaded as a shared library.
84 * @return a new instance of ApkAssets.
85 * @throws IOException if a disk I/O error or parsing error occurred.
86 */
87 public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system,
88 boolean forceSharedLibrary) throws IOException {
Winson9947f1e2019-08-16 10:20:39 -070089 return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/,
90 false /*arscOnly*/, false /*forLoader*/);
Adam Lesinskibebfcc42018-02-12 14:27:46 -080091 }
92
93 /**
94 * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications.
95 *
96 * Performs a dup of the underlying fd, so you must take care of still closing
97 * the FileDescriptor yourself (and can do that whenever you want).
98 *
99 * @param fd The FileDescriptor of an open, readable APK.
100 * @param friendlyName The friendly name used to identify this ApkAssets when logging.
101 * @param system When true, the APK is loaded as a system APK (framework).
102 * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
103 * loaded as a shared library.
104 * @return a new instance of ApkAssets.
105 * @throws IOException if a disk I/O error or parsing error occurred.
106 */
107 public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
108 @NonNull String friendlyName, boolean system, boolean forceSharedLibrary)
109 throws IOException {
Winson9947f1e2019-08-16 10:20:39 -0700110 return new ApkAssets(fd, friendlyName, system, forceSharedLibrary, false /*arscOnly*/,
111 false /*forLoader*/);
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800112 }
113
114 /**
115 * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path
116 * is encoded within the IDMAP.
117 *
118 * @param idmapPath Path to the IDMAP of an overlay APK.
119 * @param system When true, the APK is loaded as a system APK (framework).
120 * @return a new instance of ApkAssets.
121 * @throws IOException if a disk I/O error or parsing error occurred.
122 */
123 public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system)
124 throws IOException {
Winson9947f1e2019-08-16 10:20:39 -0700125 return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/,
126 false /*arscOnly*/, false /*forLoader*/);
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800127 }
128
Winson9947f1e2019-08-16 10:20:39 -0700129 /**
130 * Creates a new ApkAssets instance from the given path on disk for use with a
131 * {@link ResourcesProvider}.
132 *
133 * @param path The path to an APK on disk.
134 * @return a new instance of ApkAssets.
135 * @throws IOException if a disk I/O error or parsing error occurred.
136 */
137 public static @NonNull ApkAssets loadApkForLoader(@NonNull String path)
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800138 throws IOException {
Winson9947f1e2019-08-16 10:20:39 -0700139 return new ApkAssets(path, false /*system*/, false /*forceSharedLibrary*/,
140 false /*overlay*/, false /*arscOnly*/, true /*forLoader*/);
141 }
142
143 /**
144 * Creates a new ApkAssets instance from the given file descriptor for use with a
145 * {@link ResourcesProvider}.
146 *
147 * Performs a dup of the underlying fd, so you must take care of still closing
148 * the FileDescriptor yourself (and can do that whenever you want).
149 *
150 * @param fd The FileDescriptor of an open, readable APK.
151 * @return a new instance of ApkAssets.
152 * @throws IOException if a disk I/O error or parsing error occurred.
153 */
154 @NonNull
155 public static ApkAssets loadApkForLoader(@NonNull FileDescriptor fd) throws IOException {
156 return new ApkAssets(fd, TextUtils.emptyIfNull(fd.toString()),
157 false /*system*/, false /*forceSharedLib*/, false /*arscOnly*/, true /*forLoader*/);
158 }
159
160 /**
161 * Creates a new ApkAssets instance from the given file descriptor representing an ARSC
162 * for use with a {@link ResourcesProvider}.
163 *
164 * Performs a dup of the underlying fd, so you must take care of still closing
165 * the FileDescriptor yourself (and can do that whenever you want).
166 *
167 * @param fd The FileDescriptor of an open, readable .arsc.
168 * @return a new instance of ApkAssets.
169 * @throws IOException if a disk I/O error or parsing error occurred.
170 */
171 public static @NonNull ApkAssets loadArscForLoader(@NonNull FileDescriptor fd)
172 throws IOException {
173 return new ApkAssets(fd, TextUtils.emptyIfNull(fd.toString()),
174 false /*system*/, false /*forceSharedLib*/, true /*arscOnly*/, true /*forLoader*/);
175 }
176
177 /**
178 * Generates an entirely empty ApkAssets. Needed because the ApkAssets instance and presence
179 * is required for a lot of APIs, and it's easier to have a non-null reference rather than
180 * tracking a separate identifier.
181 */
182 @NonNull
183 public static ApkAssets loadEmptyForLoader() {
184 return new ApkAssets(true);
185 }
186
187 private ApkAssets(boolean forLoader) {
188 mForLoader = forLoader;
189 mNativePtr = nativeLoadEmpty(forLoader);
190 mStringBlock = null;
191 }
192
193 private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay,
194 boolean arscOnly, boolean forLoader) throws IOException {
195 mForLoader = forLoader;
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800196 Preconditions.checkNotNull(path, "path");
Winson9947f1e2019-08-16 10:20:39 -0700197 mNativePtr = arscOnly ? nativeLoadArsc(path, forLoader)
198 : nativeLoad(path, system, forceSharedLib, overlay, forLoader);
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800199 mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
200 }
201
202 private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system,
Winson9947f1e2019-08-16 10:20:39 -0700203 boolean forceSharedLib, boolean arscOnly, boolean forLoader) throws IOException {
204 mForLoader = forLoader;
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800205 Preconditions.checkNotNull(fd, "fd");
206 Preconditions.checkNotNull(friendlyName, "friendlyName");
Winson9947f1e2019-08-16 10:20:39 -0700207 mNativePtr = arscOnly ? nativeLoadArscFromFd(fd, friendlyName, forLoader)
208 : nativeLoadFromFd(fd, friendlyName, system, forceSharedLib, forLoader);
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800209 mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
210 }
211
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100212 @UnsupportedAppUsage
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800213 public @NonNull String getAssetPath() {
214 synchronized (this) {
215 return nativeGetAssetPath(mNativePtr);
216 }
217 }
218
219 CharSequence getStringFromPool(int idx) {
Winson9947f1e2019-08-16 10:20:39 -0700220 if (mStringBlock == null) {
221 return null;
222 }
223
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800224 synchronized (this) {
225 return mStringBlock.get(idx);
226 }
227 }
228
Winson9947f1e2019-08-16 10:20:39 -0700229 public boolean isForLoader() {
230 return mForLoader;
231 }
232
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800233 /**
234 * Retrieve a parser for a compiled XML file. This is associated with a single APK and
235 * <em>NOT</em> a full AssetManager. This means that shared-library references will not be
236 * dynamically assigned runtime package IDs.
237 *
238 * @param fileName The path to the file within the APK.
239 * @return An XmlResourceParser.
240 * @throws IOException if the file was not found or an error occurred retrieving it.
241 */
242 public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException {
243 Preconditions.checkNotNull(fileName, "fileName");
244 synchronized (this) {
245 long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName);
246 try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) {
247 XmlResourceParser parser = block.newParser();
248 // If nativeOpenXml doesn't throw, it will always return a valid native pointer,
249 // which makes newParser always return non-null. But let's be paranoid.
250 if (parser == null) {
251 throw new AssertionError("block.newParser() returned a null parser");
252 }
253 return parser;
254 }
255 }
256 }
257
Winsond9d17362019-10-02 12:41:29 -0700258 /** @hide */
259 @Nullable
260 public OverlayableInfo getOverlayableInfo(String overlayableName) throws IOException {
261 return nativeGetOverlayableInfo(mNativePtr, overlayableName);
262 }
263
264 /** @hide */
265 public boolean definesOverlayable() throws IOException {
266 return nativeDefinesOverlayable(mNativePtr);
267 }
268
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800269 /**
270 * Returns false if the underlying APK was changed since this ApkAssets was loaded.
271 */
272 public boolean isUpToDate() {
273 synchronized (this) {
274 return nativeIsUpToDate(mNativePtr);
275 }
276 }
277
278 @Override
279 public String toString() {
280 return "ApkAssets{path=" + getAssetPath() + "}";
281 }
282
283 @Override
284 protected void finalize() throws Throwable {
Winson7a3d82a2019-03-07 12:54:24 -0800285 close();
286 }
287
288 /**
289 * Closes this class and the contained {@link #mStringBlock}.
290 */
Ryan Mitchell4043ca72019-06-03 16:11:24 -0700291 public void close() {
Winson7a3d82a2019-03-07 12:54:24 -0800292 synchronized (this) {
293 if (mOpen) {
294 mOpen = false;
Winson9947f1e2019-08-16 10:20:39 -0700295 if (mStringBlock != null) {
296 mStringBlock.close();
297 }
Winson7a3d82a2019-03-07 12:54:24 -0800298 nativeDestroy(mNativePtr);
299 }
300 }
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800301 }
302
Winson9947f1e2019-08-16 10:20:39 -0700303 private static native long nativeLoad(@NonNull String path, boolean system,
304 boolean forceSharedLib, boolean overlay, boolean forLoader)
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800305 throws IOException;
306 private static native long nativeLoadFromFd(@NonNull FileDescriptor fd,
Winson9947f1e2019-08-16 10:20:39 -0700307 @NonNull String friendlyName, boolean system, boolean forceSharedLib,
308 boolean forLoader)
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800309 throws IOException;
Winson9947f1e2019-08-16 10:20:39 -0700310 private static native long nativeLoadArsc(@NonNull String path, boolean forLoader)
311 throws IOException;
312 private static native long nativeLoadArscFromFd(@NonNull FileDescriptor fd,
313 @NonNull String friendlyName, boolean forLoader) throws IOException;
314 private static native long nativeLoadEmpty(boolean forLoader);
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800315 private static native void nativeDestroy(long ptr);
316 private static native @NonNull String nativeGetAssetPath(long ptr);
317 private static native long nativeGetStringBlock(long ptr);
318 private static native boolean nativeIsUpToDate(long ptr);
319 private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException;
Winsond9d17362019-10-02 12:41:29 -0700320 private static native @Nullable OverlayableInfo nativeGetOverlayableInfo(long ptr,
321 String overlayableName) throws IOException;
322 private static native boolean nativeDefinesOverlayable(long ptr) throws IOException;
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800323}