• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
16 package android.content.res;
17 
18 import android.annotation.IntDef;
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.content.om.OverlayableInfo;
23 import android.content.res.loader.AssetsProvider;
24 import android.content.res.loader.ResourcesProvider;
25 import android.text.TextUtils;
26 
27 import com.android.internal.annotations.GuardedBy;
28 
29 import java.io.FileDescriptor;
30 import java.io.IOException;
31 import java.io.PrintWriter;
32 import java.lang.annotation.Retention;
33 import java.lang.annotation.RetentionPolicy;
34 import java.util.Objects;
35 
36 /**
37  * The loaded, immutable, in-memory representation of an APK.
38  *
39  * The main implementation is native C++ and there is very little API surface exposed here. The APK
40  * is mainly accessed via {@link AssetManager}.
41  *
42  * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers,
43  * making the creation of AssetManagers very cheap.
44  * @hide
45  */
46 public final class ApkAssets {
47 
48     /**
49      * The apk assets contains framework resource values specified by the system.
50      * This allows some functions to filter out this package when computing what
51      * configurations/resources are available.
52      */
53     public static final int PROPERTY_SYSTEM = 1 << 0;
54 
55     /**
56      * The apk assets is a shared library or was loaded as a shared library by force.
57      * The package ids of dynamic apk assets are assigned at runtime instead of compile time.
58      */
59     public static final int PROPERTY_DYNAMIC = 1 << 1;
60 
61     /**
62      * The apk assets has been loaded dynamically using a {@link ResourcesProvider}.
63      * Loader apk assets overlay resources like RROs except they are not backed by an idmap.
64      */
65     public static final int PROPERTY_LOADER = 1 << 2;
66 
67     /**
68      * The apk assets is a RRO.
69      * An RRO overlays resource values of its target package.
70      */
71     private static final int PROPERTY_OVERLAY = 1 << 3;
72 
73     /**
74      * The apk assets is owned by the application running in this process and incremental crash
75      * protections for this APK must be disabled.
76      */
77     public static final int PROPERTY_DISABLE_INCREMENTAL_HARDENING = 1 << 4;
78 
79     /** Flags that change the behavior of loaded apk assets. */
80     @IntDef(prefix = { "PROPERTY_" }, value = {
81             PROPERTY_SYSTEM,
82             PROPERTY_DYNAMIC,
83             PROPERTY_LOADER,
84             PROPERTY_OVERLAY,
85     })
86     @Retention(RetentionPolicy.SOURCE)
87     public @interface PropertyFlags {}
88 
89     /** The path used to load the apk assets represents an APK file. */
90     private static final int FORMAT_APK = 0;
91 
92     /** The path used to load the apk assets represents an idmap file. */
93     private static final int FORMAT_IDMAP = 1;
94 
95     /** The path used to load the apk assets represents an resources.arsc file. */
96     private static final int FORMAT_ARSC = 2;
97 
98     /** the path used to load the apk assets represents a directory. */
99     private static final int FORMAT_DIR = 3;
100 
101     // Format types that change how the apk assets are loaded.
102     @IntDef(prefix = { "FORMAT_" }, value = {
103             FORMAT_APK,
104             FORMAT_IDMAP,
105             FORMAT_ARSC,
106             FORMAT_DIR
107     })
108     @Retention(RetentionPolicy.SOURCE)
109     public @interface FormatType {}
110 
111     @GuardedBy("this")
112     private long mNativePtr;  // final, except cleared in finalizer.
113 
114     @Nullable
115     @GuardedBy("this")
116     private final StringBlock mStringBlock;  // null or closed if mNativePtr = 0.
117 
118     @PropertyFlags
119     private final int mFlags;
120 
121     @Nullable
122     private final AssetsProvider mAssets;
123 
124     /**
125      * Creates a new ApkAssets instance from the given path on disk.
126      *
127      * @param path The path to an APK on disk.
128      * @return a new instance of ApkAssets.
129      * @throws IOException if a disk I/O error or parsing error occurred.
130      */
loadFromPath(@onNull String path)131     public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException {
132         return loadFromPath(path, 0 /* flags */);
133     }
134 
135     /**
136      * Creates a new ApkAssets instance from the given path on disk.
137      *
138      * @param path The path to an APK on disk.
139      * @param flags flags that change the behavior of loaded apk assets
140      * @return a new instance of ApkAssets.
141      * @throws IOException if a disk I/O error or parsing error occurred.
142      */
loadFromPath(@onNull String path, @PropertyFlags int flags)143     public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags)
144             throws IOException {
145         return new ApkAssets(FORMAT_APK, path, flags, null /* assets */);
146     }
147 
148     /**
149      * Creates a new ApkAssets instance from the given path on disk.
150      *
151      * @param path The path to an APK on disk.
152      * @param flags flags that change the behavior of loaded apk assets
153      * @param assets The assets provider that overrides the loading of file-based resources
154      * @return a new instance of ApkAssets.
155      * @throws IOException if a disk I/O error or parsing error occurred.
156      */
loadFromPath(@onNull String path, @PropertyFlags int flags, @Nullable AssetsProvider assets)157     public static @NonNull ApkAssets loadFromPath(@NonNull String path, @PropertyFlags int flags,
158             @Nullable AssetsProvider assets) throws IOException {
159         return new ApkAssets(FORMAT_APK, path, flags, assets);
160     }
161 
162     /**
163      * Creates a new ApkAssets instance from the given file descriptor.
164      *
165      * Performs a dup of the underlying fd, so you must take care of still closing
166      * the FileDescriptor yourself (and can do that whenever you want).
167      *
168      * @param fd The FileDescriptor of an open, readable APK.
169      * @param friendlyName The friendly name used to identify this ApkAssets when logging.
170      * @param flags flags that change the behavior of loaded apk assets
171      * @param assets The assets provider that overrides the loading of file-based resources
172      * @return a new instance of ApkAssets.
173      * @throws IOException if a disk I/O error or parsing error occurred.
174      */
loadFromFd(@onNull FileDescriptor fd, @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)175     public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
176             @NonNull String friendlyName, @PropertyFlags int flags,
177             @Nullable AssetsProvider assets) throws IOException {
178         return new ApkAssets(FORMAT_APK, fd, friendlyName, flags, assets);
179     }
180 
181     /**
182      * Creates a new ApkAssets instance from the given file descriptor.
183      *
184      * Performs a dup of the underlying fd, so you must take care of still closing
185      * the FileDescriptor yourself (and can do that whenever you want).
186      *
187      * @param fd The FileDescriptor of an open, readable APK.
188      * @param friendlyName The friendly name used to identify this ApkAssets when logging.
189      * @param offset The location within the file that the apk starts. This must be 0 if length is
190      *               {@link AssetFileDescriptor#UNKNOWN_LENGTH}.
191      * @param length The number of bytes of the apk, or {@link AssetFileDescriptor#UNKNOWN_LENGTH}
192      *               if it extends to the end of the file.
193      * @param flags flags that change the behavior of loaded apk assets
194      * @param assets The assets provider that overrides the loading of file-based resources
195      * @return a new instance of ApkAssets.
196      * @throws IOException if a disk I/O error or parsing error occurred.
197      */
loadFromFd(@onNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags, @Nullable AssetsProvider assets)198     public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
199             @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
200             @Nullable AssetsProvider assets)
201             throws IOException {
202         return new ApkAssets(FORMAT_APK, fd, friendlyName, offset, length, flags, assets);
203     }
204 
205     /**
206      * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path
207      * is encoded within the IDMAP.
208      *
209      * @param idmapPath Path to the IDMAP of an overlay APK.
210      * @param flags flags that change the behavior of loaded apk assets
211      * @return a new instance of ApkAssets.
212      * @throws IOException if a disk I/O error or parsing error occurred.
213      */
loadOverlayFromPath(@onNull String idmapPath, @PropertyFlags int flags)214     public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath,
215             @PropertyFlags int flags) throws IOException {
216         return new ApkAssets(FORMAT_IDMAP, idmapPath, flags, null /* assets */);
217     }
218 
219     /**
220      * Creates a new ApkAssets instance from the given file descriptor representing a resources.arsc
221      * for use with a {@link ResourcesProvider}.
222      *
223      * Performs a dup of the underlying fd, so you must take care of still closing
224      * the FileDescriptor yourself (and can do that whenever you want).
225      *
226      * @param fd The FileDescriptor of an open, readable resources.arsc.
227      * @param friendlyName The friendly name used to identify this ApkAssets when logging.
228      * @param flags flags that change the behavior of loaded apk assets
229      * @param assets The assets provider that overrides the loading of file-based resources
230      * @return a new instance of ApkAssets.
231      * @throws IOException if a disk I/O error or parsing error occurred.
232      */
loadTableFromFd(@onNull FileDescriptor fd, @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)233     public static @NonNull ApkAssets loadTableFromFd(@NonNull FileDescriptor fd,
234             @NonNull String friendlyName, @PropertyFlags int flags,
235             @Nullable AssetsProvider assets) throws IOException {
236         return new ApkAssets(FORMAT_ARSC, fd, friendlyName, flags, assets);
237     }
238 
239     /**
240      * Creates a new ApkAssets instance from the given file descriptor representing a resources.arsc
241      * for use with a {@link ResourcesProvider}.
242      *
243      * Performs a dup of the underlying fd, so you must take care of still closing
244      * the FileDescriptor yourself (and can do that whenever you want).
245      *
246      * @param fd The FileDescriptor of an open, readable resources.arsc.
247      * @param friendlyName The friendly name used to identify this ApkAssets when logging.
248      * @param offset The location within the file that the table starts. This must be 0 if length is
249      *               {@link AssetFileDescriptor#UNKNOWN_LENGTH}.
250      * @param length The number of bytes of the table, or {@link AssetFileDescriptor#UNKNOWN_LENGTH}
251      *               if it extends to the end of the file.
252      * @param flags flags that change the behavior of loaded apk assets
253      * @param assets The assets provider that overrides the loading of file-based resources
254      * @return a new instance of ApkAssets.
255      * @throws IOException if a disk I/O error or parsing error occurred.
256      */
loadTableFromFd(@onNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags, @Nullable AssetsProvider assets)257     public static @NonNull ApkAssets loadTableFromFd(@NonNull FileDescriptor fd,
258             @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
259             @Nullable AssetsProvider assets) throws IOException {
260         return new ApkAssets(FORMAT_ARSC, fd, friendlyName, offset, length, flags, assets);
261     }
262 
263     /**
264      * Creates a new ApkAssets instance from the given directory path. The directory should have the
265      * file structure of an APK.
266      *
267      * @param path The path to a directory on disk.
268      * @param flags flags that change the behavior of loaded apk assets
269      * @param assets The assets provider that overrides the loading of file-based resources
270      * @return a new instance of ApkAssets.
271      * @throws IOException if a disk I/O error or parsing error occurred.
272      */
loadFromDir(@onNull String path, @PropertyFlags int flags, @Nullable AssetsProvider assets)273     public static @NonNull ApkAssets loadFromDir(@NonNull String path,
274             @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException {
275         return new ApkAssets(FORMAT_DIR, path, flags, assets);
276     }
277 
278     /**
279      * Generates an entirely empty ApkAssets. Needed because the ApkAssets instance and presence
280      * is required for a lot of APIs, and it's easier to have a non-null reference rather than
281      * tracking a separate identifier.
282      *
283      * @param flags flags that change the behavior of loaded apk assets
284      * @param assets The assets provider that overrides the loading of file-based resources
285      */
286     @NonNull
loadEmptyForLoader(@ropertyFlags int flags, @Nullable AssetsProvider assets)287     public static ApkAssets loadEmptyForLoader(@PropertyFlags int flags,
288             @Nullable AssetsProvider assets) {
289         return new ApkAssets(flags, assets);
290     }
291 
ApkAssets(@ormatType int format, @NonNull String path, @PropertyFlags int flags, @Nullable AssetsProvider assets)292     private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags,
293             @Nullable AssetsProvider assets) throws IOException {
294         Objects.requireNonNull(path, "path");
295         mFlags = flags;
296         mNativePtr = nativeLoad(format, path, flags, assets);
297         mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
298         mAssets = assets;
299     }
300 
ApkAssets(@ormatType int format, @NonNull FileDescriptor fd, @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)301     private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
302             @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)
303             throws IOException {
304         Objects.requireNonNull(fd, "fd");
305         Objects.requireNonNull(friendlyName, "friendlyName");
306         mFlags = flags;
307         mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets);
308         mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
309         mAssets = assets;
310     }
311 
ApkAssets(@ormatType int format, @NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags, @Nullable AssetsProvider assets)312     private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
313             @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
314             @Nullable AssetsProvider assets) throws IOException {
315         Objects.requireNonNull(fd, "fd");
316         Objects.requireNonNull(friendlyName, "friendlyName");
317         mFlags = flags;
318         mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets);
319         mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
320         mAssets = assets;
321     }
322 
ApkAssets(@ropertyFlags int flags, @Nullable AssetsProvider assets)323     private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) {
324         mFlags = flags;
325         mNativePtr = nativeLoadEmpty(flags, assets);
326         mStringBlock = null;
327         mAssets = assets;
328     }
329 
330     @UnsupportedAppUsage
getAssetPath()331     public @NonNull String getAssetPath() {
332         synchronized (this) {
333             return TextUtils.emptyIfNull(nativeGetAssetPath(mNativePtr));
334         }
335     }
336 
337     /** @hide */
getDebugName()338     public @NonNull String getDebugName() {
339         synchronized (this) {
340             return nativeGetDebugName(mNativePtr);
341         }
342     }
343 
344     @Nullable
getStringFromPool(int idx)345     CharSequence getStringFromPool(int idx) {
346         if (mStringBlock == null) {
347             return null;
348         }
349 
350         synchronized (this) {
351             return mStringBlock.getSequence(idx);
352         }
353     }
354 
355     /** Returns whether this apk assets was loaded using a {@link ResourcesProvider}. */
isForLoader()356     public boolean isForLoader() {
357         return (mFlags & PROPERTY_LOADER) != 0;
358     }
359 
360     /**
361      * Returns the assets provider that overrides the loading of assets present in this apk assets.
362      */
363     @Nullable
getAssetsProvider()364     public AssetsProvider getAssetsProvider() {
365         return mAssets;
366     }
367 
368     /**
369      * Retrieve a parser for a compiled XML file. This is associated with a single APK and
370      * <em>NOT</em> a full AssetManager. This means that shared-library references will not be
371      * dynamically assigned runtime package IDs.
372      *
373      * @param fileName The path to the file within the APK.
374      * @return An XmlResourceParser.
375      * @throws IOException if the file was not found or an error occurred retrieving it.
376      */
openXml(@onNull String fileName)377     public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException {
378         Objects.requireNonNull(fileName, "fileName");
379         synchronized (this) {
380             long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName);
381             try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) {
382                 XmlResourceParser parser = block.newParser();
383                 // If nativeOpenXml doesn't throw, it will always return a valid native pointer,
384                 // which makes newParser always return non-null. But let's be careful.
385                 if (parser == null) {
386                     throw new AssertionError("block.newParser() returned a null parser");
387                 }
388                 return parser;
389             }
390         }
391     }
392 
393     /** @hide */
394     @Nullable
getOverlayableInfo(String overlayableName)395     public OverlayableInfo getOverlayableInfo(String overlayableName) throws IOException {
396         synchronized (this) {
397             return nativeGetOverlayableInfo(mNativePtr, overlayableName);
398         }
399     }
400 
401     /** @hide */
definesOverlayable()402     public boolean definesOverlayable() throws IOException {
403         synchronized (this) {
404             return nativeDefinesOverlayable(mNativePtr);
405         }
406     }
407 
408     /**
409      * Returns false if the underlying APK was changed since this ApkAssets was loaded.
410      */
isUpToDate()411     public boolean isUpToDate() {
412         synchronized (this) {
413             return nativeIsUpToDate(mNativePtr);
414         }
415     }
416 
417     @Override
toString()418     public String toString() {
419         return "ApkAssets{path=" + getDebugName() + "}";
420     }
421 
422     @Override
finalize()423     protected void finalize() throws Throwable {
424         close();
425     }
426 
427     /**
428      * Closes this class and the contained {@link #mStringBlock}.
429      */
close()430     public void close() {
431         synchronized (this) {
432             if (mNativePtr != 0) {
433                 if (mStringBlock != null) {
434                     mStringBlock.close();
435                 }
436                 nativeDestroy(mNativePtr);
437                 mNativePtr = 0;
438             }
439         }
440     }
441 
dump(PrintWriter pw, String prefix)442     void dump(PrintWriter pw, String prefix) {
443         pw.println(prefix + "class=" + getClass());
444         pw.println(prefix + "debugName=" + getDebugName());
445         pw.println(prefix + "assetPath=" + getAssetPath());
446     }
447 
nativeLoad(@ormatType int format, @NonNull String path, @PropertyFlags int flags, @Nullable AssetsProvider asset)448     private static native long nativeLoad(@FormatType int format, @NonNull String path,
449             @PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
nativeLoadEmpty(@ropertyFlags int flags, @Nullable AssetsProvider asset)450     private static native long nativeLoadEmpty(@PropertyFlags int flags,
451             @Nullable AssetsProvider asset);
nativeLoadFd(@ormatType int format, @NonNull FileDescriptor fd, @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider asset)452     private static native long nativeLoadFd(@FormatType int format, @NonNull FileDescriptor fd,
453             @NonNull String friendlyName, @PropertyFlags int flags,
454             @Nullable AssetsProvider asset) throws IOException;
nativeLoadFdOffsets(@ormatType int format, @NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags, @Nullable AssetsProvider asset)455     private static native long nativeLoadFdOffsets(@FormatType int format,
456             @NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length,
457             @PropertyFlags int flags, @Nullable AssetsProvider asset) throws IOException;
nativeDestroy(long ptr)458     private static native void nativeDestroy(long ptr);
nativeGetAssetPath(long ptr)459     private static native @NonNull String nativeGetAssetPath(long ptr);
nativeGetDebugName(long ptr)460     private static native @NonNull String nativeGetDebugName(long ptr);
nativeGetStringBlock(long ptr)461     private static native long nativeGetStringBlock(long ptr);
nativeIsUpToDate(long ptr)462     private static native boolean nativeIsUpToDate(long ptr);
nativeOpenXml(long ptr, @NonNull String fileName)463     private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException;
nativeGetOverlayableInfo(long ptr, String overlayableName)464     private static native @Nullable OverlayableInfo nativeGetOverlayableInfo(long ptr,
465             String overlayableName) throws IOException;
nativeDefinesOverlayable(long ptr)466     private static native boolean nativeDefinesOverlayable(long ptr) throws IOException;
467 }
468