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