blob: a35ad567ed81aa6378e9ff90a7340afc7d41c213 [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;
Mathew Inwood5c0d3542018-08-14 13:54:31 +010019import android.annotation.UnsupportedAppUsage;
Adam Lesinskibebfcc42018-02-12 14:27:46 -080020
21import com.android.internal.annotations.GuardedBy;
22import com.android.internal.util.Preconditions;
23
24import java.io.FileDescriptor;
25import java.io.IOException;
26
27/**
28 * The loaded, immutable, in-memory representation of an APK.
29 *
30 * The main implementation is native C++ and there is very little API surface exposed here. The APK
31 * is mainly accessed via {@link AssetManager}.
32 *
33 * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers,
34 * making the creation of AssetManagers very cheap.
35 * @hide
36 */
37public final class ApkAssets {
38 @GuardedBy("this") private final long mNativePtr;
Winson7a3d82a2019-03-07 12:54:24 -080039 @GuardedBy("this") private final StringBlock mStringBlock;
40
41 @GuardedBy("this") private boolean mOpen = true;
Adam Lesinskibebfcc42018-02-12 14:27:46 -080042
43 /**
44 * Creates a new ApkAssets instance from the given path on disk.
45 *
46 * @param path The path to an APK on disk.
47 * @return a new instance of ApkAssets.
48 * @throws IOException if a disk I/O error or parsing error occurred.
49 */
50 public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException {
51 return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/);
52 }
53
54 /**
55 * Creates a new ApkAssets instance from the given path on disk.
56 *
57 * @param path The path to an APK on disk.
58 * @param system When true, the APK is loaded as a system APK (framework).
59 * @return a new instance of ApkAssets.
60 * @throws IOException if a disk I/O error or parsing error occurred.
61 */
62 public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system)
63 throws IOException {
64 return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/);
65 }
66
67 /**
68 * Creates a new ApkAssets instance from the given path on disk.
69 *
70 * @param path The path to an APK on disk.
71 * @param system When true, the APK is loaded as a system APK (framework).
72 * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
73 * loaded as a shared library.
74 * @return a new instance of ApkAssets.
75 * @throws IOException if a disk I/O error or parsing error occurred.
76 */
77 public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system,
78 boolean forceSharedLibrary) throws IOException {
79 return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/);
80 }
81
82 /**
83 * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications.
84 *
85 * Performs a dup of the underlying fd, so you must take care of still closing
86 * the FileDescriptor yourself (and can do that whenever you want).
87 *
88 * @param fd The FileDescriptor of an open, readable APK.
89 * @param friendlyName The friendly name used to identify this ApkAssets when logging.
90 * @param system When true, the APK is loaded as a system APK (framework).
91 * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
92 * loaded as a shared library.
93 * @return a new instance of ApkAssets.
94 * @throws IOException if a disk I/O error or parsing error occurred.
95 */
96 public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
97 @NonNull String friendlyName, boolean system, boolean forceSharedLibrary)
98 throws IOException {
99 return new ApkAssets(fd, friendlyName, system, forceSharedLibrary);
100 }
101
102 /**
103 * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path
104 * is encoded within the IDMAP.
105 *
106 * @param idmapPath Path to the IDMAP of an overlay APK.
107 * @param system When true, the APK is loaded as a system APK (framework).
108 * @return a new instance of ApkAssets.
109 * @throws IOException if a disk I/O error or parsing error occurred.
110 */
111 public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system)
112 throws IOException {
113 return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/);
114 }
115
116 private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
117 throws IOException {
118 Preconditions.checkNotNull(path, "path");
119 mNativePtr = nativeLoad(path, system, forceSharedLib, overlay);
120 mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
121 }
122
123 private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system,
124 boolean forceSharedLib) throws IOException {
125 Preconditions.checkNotNull(fd, "fd");
126 Preconditions.checkNotNull(friendlyName, "friendlyName");
127 mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib);
128 mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
129 }
130
Mathew Inwood5c0d3542018-08-14 13:54:31 +0100131 @UnsupportedAppUsage
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800132 public @NonNull String getAssetPath() {
133 synchronized (this) {
134 return nativeGetAssetPath(mNativePtr);
135 }
136 }
137
138 CharSequence getStringFromPool(int idx) {
139 synchronized (this) {
140 return mStringBlock.get(idx);
141 }
142 }
143
144 /**
145 * Retrieve a parser for a compiled XML file. This is associated with a single APK and
146 * <em>NOT</em> a full AssetManager. This means that shared-library references will not be
147 * dynamically assigned runtime package IDs.
148 *
149 * @param fileName The path to the file within the APK.
150 * @return An XmlResourceParser.
151 * @throws IOException if the file was not found or an error occurred retrieving it.
152 */
153 public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException {
154 Preconditions.checkNotNull(fileName, "fileName");
155 synchronized (this) {
156 long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName);
157 try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) {
158 XmlResourceParser parser = block.newParser();
159 // If nativeOpenXml doesn't throw, it will always return a valid native pointer,
160 // which makes newParser always return non-null. But let's be paranoid.
161 if (parser == null) {
162 throw new AssertionError("block.newParser() returned a null parser");
163 }
164 return parser;
165 }
166 }
167 }
168
169 /**
170 * Returns false if the underlying APK was changed since this ApkAssets was loaded.
171 */
172 public boolean isUpToDate() {
173 synchronized (this) {
174 return nativeIsUpToDate(mNativePtr);
175 }
176 }
177
178 @Override
179 public String toString() {
180 return "ApkAssets{path=" + getAssetPath() + "}";
181 }
182
183 @Override
184 protected void finalize() throws Throwable {
Winson7a3d82a2019-03-07 12:54:24 -0800185 close();
186 }
187
188 /**
189 * Closes this class and the contained {@link #mStringBlock}.
190 */
Ryan Mitchell4043ca72019-06-03 16:11:24 -0700191 public void close() {
Winson7a3d82a2019-03-07 12:54:24 -0800192 synchronized (this) {
193 if (mOpen) {
194 mOpen = false;
195 mStringBlock.close();
196 nativeDestroy(mNativePtr);
197 }
198 }
Adam Lesinskibebfcc42018-02-12 14:27:46 -0800199 }
200
201 private static native long nativeLoad(
202 @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
203 throws IOException;
204 private static native long nativeLoadFromFd(@NonNull FileDescriptor fd,
205 @NonNull String friendlyName, boolean system, boolean forceSharedLib)
206 throws IOException;
207 private static native void nativeDestroy(long ptr);
208 private static native @NonNull String nativeGetAssetPath(long ptr);
209 private static native long nativeGetStringBlock(long ptr);
210 private static native boolean nativeIsUpToDate(long ptr);
211 private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException;
212}