1 /*
2  * Copyright (C) 2006 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 
17 package android.content.res;
18 
19 import android.annotation.AnyRes;
20 import android.annotation.ArrayRes;
21 import android.annotation.AttrRes;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.StringRes;
25 import android.annotation.StyleRes;
26 import android.annotation.TestApi;
27 import android.compat.annotation.UnsupportedAppUsage;
28 import android.content.pm.ActivityInfo;
29 import android.content.res.Configuration.NativeConfig;
30 import android.os.ParcelFileDescriptor;
31 import android.util.ArraySet;
32 import android.util.Log;
33 import android.util.SparseArray;
34 import android.util.TypedValue;
35 
36 import com.android.internal.annotations.GuardedBy;
37 import com.android.internal.util.Preconditions;
38 
39 import libcore.io.IoUtils;
40 
41 import java.io.BufferedReader;
42 import java.io.FileInputStream;
43 import java.io.FileNotFoundException;
44 import java.io.IOException;
45 import java.io.InputStream;
46 import java.io.InputStreamReader;
47 import java.nio.channels.FileLock;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.HashMap;
51 import java.util.Map;
52 
53 /**
54  * Provides access to an application's raw asset files; see {@link Resources}
55  * for the way most applications will want to retrieve their resource data.
56  * This class presents a lower-level API that allows you to open and read raw
57  * files that have been bundled with the application as a simple stream of
58  * bytes.
59  */
60 public final class AssetManager implements AutoCloseable {
61     private static final String TAG = "AssetManager";
62     private static final boolean DEBUG_REFS = false;
63     private static final boolean FEATURE_FLAG_IDMAP2 = true;
64 
65     private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
66 
67     private static final Object sSync = new Object();
68 
69     private static final ApkAssets[] sEmptyApkAssets = new ApkAssets[0];
70 
71     // Not private for LayoutLib's BridgeAssetManager.
72     @UnsupportedAppUsage
73     @GuardedBy("sSync") static AssetManager sSystem = null;
74 
75     @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0];
76     @GuardedBy("sSync") private static ArraySet<ApkAssets> sSystemApkAssetsSet;
77 
78     /**
79      * Mode for {@link #open(String, int)}: no specific information about how
80      * data will be accessed.
81      */
82     public static final int ACCESS_UNKNOWN = 0;
83     /**
84      * Mode for {@link #open(String, int)}: Read chunks, and seek forward and
85      * backward.
86      */
87     public static final int ACCESS_RANDOM = 1;
88     /**
89      * Mode for {@link #open(String, int)}: Read sequentially, with an
90      * occasional forward seek.
91      */
92     public static final int ACCESS_STREAMING = 2;
93     /**
94      * Mode for {@link #open(String, int)}: Attempt to load contents into
95      * memory, for fast small reads.
96      */
97     public static final int ACCESS_BUFFER = 3;
98 
99     @GuardedBy("this") private final TypedValue mValue = new TypedValue();
100     @GuardedBy("this") private final long[] mOffsets = new long[2];
101 
102     // Pointer to native implementation, stuffed inside a long.
103     @UnsupportedAppUsage
104     @GuardedBy("this") private long mObject;
105 
106     // The loaded asset paths.
107     @GuardedBy("this") private ApkAssets[] mApkAssets;
108 
109     // Debug/reference counting implementation.
110     @GuardedBy("this") private boolean mOpen = true;
111     @GuardedBy("this") private int mNumRefs = 1;
112     @GuardedBy("this") private HashMap<Long, RuntimeException> mRefStacks;
113 
114     /**
115      * A Builder class that helps create an AssetManager with only a single invocation of
116      * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. Without using this builder,
117      * AssetManager must ensure there are system ApkAssets loaded at all times, which when combined
118      * with the user's call to add additional ApkAssets, results in multiple calls to
119      * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}.
120      * @hide
121      */
122     public static class Builder {
123         private ArrayList<ApkAssets> mUserApkAssets = new ArrayList<>();
124 
addApkAssets(ApkAssets apkAssets)125         public Builder addApkAssets(ApkAssets apkAssets) {
126             mUserApkAssets.add(apkAssets);
127             return this;
128         }
129 
build()130         public AssetManager build() {
131             // Retrieving the system ApkAssets forces their creation as well.
132             final ApkAssets[] systemApkAssets = getSystem().getApkAssets();
133 
134             final int totalApkAssetCount = systemApkAssets.length + mUserApkAssets.size();
135             final ApkAssets[] apkAssets = new ApkAssets[totalApkAssetCount];
136 
137             System.arraycopy(systemApkAssets, 0, apkAssets, 0, systemApkAssets.length);
138 
139             final int userApkAssetCount = mUserApkAssets.size();
140             for (int i = 0; i < userApkAssetCount; i++) {
141                 apkAssets[i + systemApkAssets.length] = mUserApkAssets.get(i);
142             }
143 
144             // Calling this constructor prevents creation of system ApkAssets, which we took care
145             // of in this Builder.
146             final AssetManager assetManager = new AssetManager(false /*sentinel*/);
147             assetManager.mApkAssets = apkAssets;
148             AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets,
149                     false /*invalidateCaches*/);
150             return assetManager;
151         }
152     }
153 
154     /**
155      * Create a new AssetManager containing only the basic system assets.
156      * Applications will not generally use this method, instead retrieving the
157      * appropriate asset manager with {@link Resources#getAssets}.    Not for
158      * use by applications.
159      * @hide
160      */
161     @UnsupportedAppUsage
AssetManager()162     public AssetManager() {
163         final ApkAssets[] assets;
164         synchronized (sSync) {
165             createSystemAssetsInZygoteLocked();
166             assets = sSystemApkAssets;
167         }
168 
169         mObject = nativeCreate();
170         if (DEBUG_REFS) {
171             mNumRefs = 0;
172             incRefsLocked(hashCode());
173         }
174 
175         // Always set the framework resources.
176         setApkAssets(assets, false /*invalidateCaches*/);
177     }
178 
179     /**
180      * Private constructor that doesn't call ensureSystemAssets.
181      * Used for the creation of system assets.
182      */
183     @SuppressWarnings("unused")
AssetManager(boolean sentinel)184     private AssetManager(boolean sentinel) {
185         mObject = nativeCreate();
186         if (DEBUG_REFS) {
187             mNumRefs = 0;
188             incRefsLocked(hashCode());
189         }
190     }
191 
192     /**
193      * This must be called from Zygote so that system assets are shared by all applications.
194      */
195     @GuardedBy("sSync")
createSystemAssetsInZygoteLocked()196     private static void createSystemAssetsInZygoteLocked() {
197         if (sSystem != null) {
198             return;
199         }
200 
201 
202         try {
203             final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
204             apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/));
205             if (FEATURE_FLAG_IDMAP2) {
206                 final String[] systemIdmapPaths =
207                     nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
208                 if (systemIdmapPaths != null) {
209                     for (String idmapPath : systemIdmapPaths) {
210                         apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
211                     }
212                 } else {
213                     Log.w(TAG, "'idmap2 --scan' failed: no static=\"true\" overlays targeting "
214                             + "\"android\" will be loaded");
215                 }
216             } else {
217                 nativeVerifySystemIdmaps();
218                 loadStaticRuntimeOverlays(apkAssets);
219             }
220 
221             sSystemApkAssetsSet = new ArraySet<>(apkAssets);
222             sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]);
223             sSystem = new AssetManager(true /*sentinel*/);
224             sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/);
225         } catch (IOException e) {
226             throw new IllegalStateException("Failed to create system AssetManager", e);
227         }
228     }
229 
230     /**
231      * Loads the static runtime overlays declared in /data/resource-cache/overlays.list.
232      * Throws an exception if the file is corrupt or if loading the APKs referenced by the file
233      * fails. Returns quietly if the overlays.list file doesn't exist.
234      * @param outApkAssets The list to fill with the loaded ApkAssets.
235      */
loadStaticRuntimeOverlays(ArrayList<ApkAssets> outApkAssets)236     private static void loadStaticRuntimeOverlays(ArrayList<ApkAssets> outApkAssets)
237             throws IOException {
238         final FileInputStream fis;
239         try {
240             fis = new FileInputStream("/data/resource-cache/overlays.list");
241         } catch (FileNotFoundException e) {
242             // We might not have any overlays, this is fine. We catch here since ApkAssets
243             // loading can also fail with the same exception, which we would want to propagate.
244             Log.i(TAG, "no overlays.list file found");
245             return;
246         }
247 
248         try {
249             // Acquire a lock so that any idmap scanning doesn't impact the current set.
250             // The order of this try-with-resources block matters. We must release the lock, and
251             // then close the file streams when exiting the block.
252             try (final BufferedReader br = new BufferedReader(new InputStreamReader(fis));
253                  final FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, true /*shared*/)) {
254                 for (String line; (line = br.readLine()) != null; ) {
255                     final String idmapPath = line.split(" ")[1];
256                     outApkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
257                 }
258             }
259         } finally {
260             // When BufferedReader is closed above, FileInputStream is closed as well. But let's be
261             // paranoid.
262             IoUtils.closeQuietly(fis);
263         }
264     }
265 
266     /**
267      * Return a global shared asset manager that provides access to only
268      * system assets (no application assets).
269      * @hide
270      */
271     @UnsupportedAppUsage
getSystem()272     public static AssetManager getSystem() {
273         synchronized (sSync) {
274             createSystemAssetsInZygoteLocked();
275             return sSystem;
276         }
277     }
278 
279     /**
280      * Close this asset manager.
281      */
282     @Override
close()283     public void close() {
284         synchronized (this) {
285             if (!mOpen) {
286                 return;
287             }
288 
289             mOpen = false;
290             decRefsLocked(hashCode());
291         }
292     }
293 
294     /**
295      * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)}
296      * family of methods.
297      *
298      * @param apkAssets The new set of paths.
299      * @param invalidateCaches Whether to invalidate any caches. This should almost always be true.
300      *                         Set this to false if you are appending new resources
301      *                         (not new configurations).
302      * @hide
303      */
setApkAssets(@onNull ApkAssets[] apkAssets, boolean invalidateCaches)304     public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) {
305         Preconditions.checkNotNull(apkAssets, "apkAssets");
306 
307         ApkAssets[] newApkAssets = new ApkAssets[sSystemApkAssets.length + apkAssets.length];
308 
309         // Copy the system assets first.
310         System.arraycopy(sSystemApkAssets, 0, newApkAssets, 0, sSystemApkAssets.length);
311 
312         // Copy the given ApkAssets if they are not already in the system list.
313         int newLength = sSystemApkAssets.length;
314         for (ApkAssets apkAsset : apkAssets) {
315             if (!sSystemApkAssetsSet.contains(apkAsset)) {
316                 newApkAssets[newLength++] = apkAsset;
317             }
318         }
319 
320         // Truncate if necessary.
321         if (newLength != newApkAssets.length) {
322             newApkAssets = Arrays.copyOf(newApkAssets, newLength);
323         }
324 
325         synchronized (this) {
326             ensureOpenLocked();
327             mApkAssets = newApkAssets;
328             nativeSetApkAssets(mObject, mApkAssets, invalidateCaches);
329             if (invalidateCaches) {
330                 // Invalidate all caches.
331                 invalidateCachesLocked(-1);
332             }
333         }
334     }
335 
336     /**
337      * Invalidates the caches in this AssetManager according to the bitmask `diff`.
338      *
339      * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}.
340      * @see ActivityInfo.Config
341      */
invalidateCachesLocked(int diff)342     private void invalidateCachesLocked(int diff) {
343         // TODO(adamlesinski): Currently there are no caches to invalidate in Java code.
344     }
345 
346     /**
347      * Returns the set of ApkAssets loaded by this AssetManager. If the AssetManager is closed, this
348      * returns a 0-length array.
349      * @hide
350      */
351     @UnsupportedAppUsage
getApkAssets()352     public @NonNull ApkAssets[] getApkAssets() {
353         synchronized (this) {
354             if (mOpen) {
355                 return mApkAssets;
356             }
357         }
358         return sEmptyApkAssets;
359     }
360 
361     /** @hide */
362     @TestApi
getApkPaths()363     public @NonNull String[] getApkPaths() {
364         synchronized (this) {
365             if (mOpen) {
366                 String[] paths = new String[mApkAssets.length];
367                 final int count = mApkAssets.length;
368                 for (int i = 0; i < count; i++) {
369                     paths[i] = mApkAssets[i].getAssetPath();
370                 }
371                 return paths;
372             }
373         }
374         return new String[0];
375     }
376 
377     /**
378      * Returns a cookie for use with the other APIs of AssetManager.
379      * @return 0 if the path was not found, otherwise a positive integer cookie representing
380      * this path in the AssetManager.
381      * @hide
382      */
findCookieForPath(@onNull String path)383     public int findCookieForPath(@NonNull String path) {
384         Preconditions.checkNotNull(path, "path");
385         synchronized (this) {
386             ensureValidLocked();
387             final int count = mApkAssets.length;
388             for (int i = 0; i < count; i++) {
389                 if (path.equals(mApkAssets[i].getAssetPath())) {
390                     return i + 1;
391                 }
392             }
393         }
394         return 0;
395     }
396 
397     /**
398      * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
399      * @hide
400      */
401     @Deprecated
402     @UnsupportedAppUsage
addAssetPath(String path)403     public int addAssetPath(String path) {
404         return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/);
405     }
406 
407     /**
408      * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
409      * @hide
410      */
411     @Deprecated
412     @UnsupportedAppUsage
addAssetPathAsSharedLibrary(String path)413     public int addAssetPathAsSharedLibrary(String path) {
414         return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/);
415     }
416 
417     /**
418      * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
419      * @hide
420      */
421     @Deprecated
422     @UnsupportedAppUsage
addOverlayPath(String path)423     public int addOverlayPath(String path) {
424         return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/);
425     }
426 
addAssetPathInternal(String path, boolean overlay, boolean appAsLib)427     private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) {
428         Preconditions.checkNotNull(path, "path");
429         synchronized (this) {
430             ensureOpenLocked();
431             final int count = mApkAssets.length;
432 
433             // See if we already have it loaded.
434             for (int i = 0; i < count; i++) {
435                 if (mApkAssets[i].getAssetPath().equals(path)) {
436                     return i + 1;
437                 }
438             }
439 
440             final ApkAssets assets;
441             try {
442                 if (overlay) {
443                     // TODO(b/70343104): This hardcoded path will be removed once
444                     // addAssetPathInternal is deleted.
445                     final String idmapPath = "/data/resource-cache/"
446                             + path.substring(1).replace('/', '@')
447                             + "@idmap";
448                     assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/);
449                 } else {
450                     assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib);
451                 }
452             } catch (IOException e) {
453                 return 0;
454             }
455 
456             mApkAssets = Arrays.copyOf(mApkAssets, count + 1);
457             mApkAssets[count] = assets;
458             nativeSetApkAssets(mObject, mApkAssets, true);
459             invalidateCachesLocked(-1);
460             return count + 1;
461         }
462     }
463 
464     /**
465      * Ensures that the native implementation has not been destroyed.
466      * The AssetManager may have been closed, but references to it still exist
467      * and therefore the native implementation is not destroyed.
468      */
469     @GuardedBy("this")
ensureValidLocked()470     private void ensureValidLocked() {
471         if (mObject == 0) {
472             throw new RuntimeException("AssetManager has been destroyed");
473         }
474     }
475 
476     /**
477      * Ensures that the AssetManager has not been explicitly closed. If this method passes,
478      * then this implies that ensureValidLocked() also passes.
479      */
480     @GuardedBy("this")
ensureOpenLocked()481     private void ensureOpenLocked() {
482         // If mOpen is true, this implies that mObject != 0.
483         if (!mOpen) {
484             throw new RuntimeException("AssetManager has been closed");
485         }
486     }
487 
488     /**
489      * Populates {@code outValue} with the data associated a particular
490      * resource identifier for the current configuration.
491      *
492      * @param resId the resource identifier to load
493      * @param densityDpi the density bucket for which to load the resource
494      * @param outValue the typed value in which to put the data
495      * @param resolveRefs {@code true} to resolve references, {@code false}
496      *                    to leave them unresolved
497      * @return {@code true} if the data was loaded into {@code outValue},
498      *         {@code false} otherwise
499      */
500     @UnsupportedAppUsage
getResourceValue(@nyRes int resId, int densityDpi, @NonNull TypedValue outValue, boolean resolveRefs)501     boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
502             boolean resolveRefs) {
503         Preconditions.checkNotNull(outValue, "outValue");
504         synchronized (this) {
505             ensureValidLocked();
506             final int cookie = nativeGetResourceValue(
507                     mObject, resId, (short) densityDpi, outValue, resolveRefs);
508             if (cookie <= 0) {
509                 return false;
510             }
511 
512             // Convert the changing configurations flags populated by native code.
513             outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
514                     outValue.changingConfigurations);
515 
516             if (outValue.type == TypedValue.TYPE_STRING) {
517                 outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
518             }
519             return true;
520         }
521     }
522 
523     /**
524      * Retrieves the string value associated with a particular resource
525      * identifier for the current configuration.
526      *
527      * @param resId the resource identifier to load
528      * @return the string value, or {@code null}
529      */
530     @UnsupportedAppUsage
getResourceText(@tringRes int resId)531     @Nullable CharSequence getResourceText(@StringRes int resId) {
532         synchronized (this) {
533             final TypedValue outValue = mValue;
534             if (getResourceValue(resId, 0, outValue, true)) {
535                 return outValue.coerceToString();
536             }
537             return null;
538         }
539     }
540 
541     /**
542      * Retrieves the string value associated with a particular resource
543      * identifier for the current configuration.
544      *
545      * @param resId the resource identifier to load
546      * @param bagEntryId the index into the bag to load
547      * @return the string value, or {@code null}
548      */
549     @UnsupportedAppUsage
getResourceBagText(@tringRes int resId, int bagEntryId)550     @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
551         synchronized (this) {
552             ensureValidLocked();
553             final TypedValue outValue = mValue;
554             final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue);
555             if (cookie <= 0) {
556                 return null;
557             }
558 
559             // Convert the changing configurations flags populated by native code.
560             outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
561                     outValue.changingConfigurations);
562 
563             if (outValue.type == TypedValue.TYPE_STRING) {
564                 return mApkAssets[cookie - 1].getStringFromPool(outValue.data);
565             }
566             return outValue.coerceToString();
567         }
568     }
569 
getResourceArraySize(@rrayRes int resId)570     int getResourceArraySize(@ArrayRes int resId) {
571         synchronized (this) {
572             ensureValidLocked();
573             return nativeGetResourceArraySize(mObject, resId);
574         }
575     }
576 
577     /**
578      * Populates `outData` with array elements of `resId`. `outData` is normally
579      * used with
580      * {@link TypedArray}.
581      *
582      * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES}
583      * long,
584      * with the indices of the data representing the type, value, asset cookie,
585      * resource ID,
586      * configuration change mask, and density of the element.
587      *
588      * @param resId The resource ID of an array resource.
589      * @param outData The array to populate with data.
590      * @return The length of the array.
591      *
592      * @see TypedArray#STYLE_TYPE
593      * @see TypedArray#STYLE_DATA
594      * @see TypedArray#STYLE_ASSET_COOKIE
595      * @see TypedArray#STYLE_RESOURCE_ID
596      * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS
597      * @see TypedArray#STYLE_DENSITY
598      */
getResourceArray(@rrayRes int resId, @NonNull int[] outData)599     int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) {
600         Preconditions.checkNotNull(outData, "outData");
601         synchronized (this) {
602             ensureValidLocked();
603             return nativeGetResourceArray(mObject, resId, outData);
604         }
605     }
606 
607     /**
608      * Retrieves the string array associated with a particular resource
609      * identifier for the current configuration.
610      *
611      * @param resId the resource identifier of the string array
612      * @return the string array, or {@code null}
613      */
getResourceStringArray(@rrayRes int resId)614     @Nullable String[] getResourceStringArray(@ArrayRes int resId) {
615         synchronized (this) {
616             ensureValidLocked();
617             return nativeGetResourceStringArray(mObject, resId);
618         }
619     }
620 
621     /**
622      * Retrieve the text array associated with a particular resource
623      * identifier.
624      *
625      * @param resId the resource id of the string array
626      */
getResourceTextArray(@rrayRes int resId)627     @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) {
628         synchronized (this) {
629             ensureValidLocked();
630             final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId);
631             if (rawInfoArray == null) {
632                 return null;
633             }
634 
635             final int rawInfoArrayLen = rawInfoArray.length;
636             final int infoArrayLen = rawInfoArrayLen / 2;
637             final CharSequence[] retArray = new CharSequence[infoArrayLen];
638             for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) {
639                 int cookie = rawInfoArray[i];
640                 int index = rawInfoArray[i + 1];
641                 retArray[j] = (index >= 0 && cookie > 0)
642                         ? mApkAssets[cookie - 1].getStringFromPool(index) : null;
643             }
644             return retArray;
645         }
646     }
647 
getResourceIntArray(@rrayRes int resId)648     @Nullable int[] getResourceIntArray(@ArrayRes int resId) {
649         synchronized (this) {
650             ensureValidLocked();
651             return nativeGetResourceIntArray(mObject, resId);
652         }
653     }
654 
655     /**
656      * Get the attributes for a style resource. These are the &lt;item&gt;
657      * elements in
658      * a &lt;style&gt; resource.
659      * @param resId The resource ID of the style
660      * @return An array of attribute IDs.
661      */
getStyleAttributes(@tyleRes int resId)662     @AttrRes int[] getStyleAttributes(@StyleRes int resId) {
663         synchronized (this) {
664             ensureValidLocked();
665             return nativeGetStyleAttributes(mObject, resId);
666         }
667     }
668 
669     /**
670      * Populates {@code outValue} with the data associated with a particular
671      * resource identifier for the current configuration. Resolves theme
672      * attributes against the specified theme.
673      *
674      * @param theme the native pointer of the theme
675      * @param resId the resource identifier to load
676      * @param outValue the typed value in which to put the data
677      * @param resolveRefs {@code true} to resolve references, {@code false}
678      *                    to leave them unresolved
679      * @return {@code true} if the data was loaded into {@code outValue},
680      *         {@code false} otherwise
681      */
getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs)682     boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue,
683             boolean resolveRefs) {
684         Preconditions.checkNotNull(outValue, "outValue");
685         synchronized (this) {
686             ensureValidLocked();
687             final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue,
688                     resolveRefs);
689             if (cookie <= 0) {
690                 return false;
691             }
692 
693             // Convert the changing configurations flags populated by native code.
694             outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
695                     outValue.changingConfigurations);
696 
697             if (outValue.type == TypedValue.TYPE_STRING) {
698                 outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
699             }
700             return true;
701         }
702     }
703 
dumpTheme(long theme, int priority, String tag, String prefix)704     void dumpTheme(long theme, int priority, String tag, String prefix) {
705         synchronized (this) {
706             ensureValidLocked();
707             nativeThemeDump(mObject, theme, priority, tag, prefix);
708         }
709     }
710 
711     @UnsupportedAppUsage
getResourceName(@nyRes int resId)712     @Nullable String getResourceName(@AnyRes int resId) {
713         synchronized (this) {
714             ensureValidLocked();
715             return nativeGetResourceName(mObject, resId);
716         }
717     }
718 
719     @UnsupportedAppUsage
getResourcePackageName(@nyRes int resId)720     @Nullable String getResourcePackageName(@AnyRes int resId) {
721         synchronized (this) {
722             ensureValidLocked();
723             return nativeGetResourcePackageName(mObject, resId);
724         }
725     }
726 
727     @UnsupportedAppUsage
getResourceTypeName(@nyRes int resId)728     @Nullable String getResourceTypeName(@AnyRes int resId) {
729         synchronized (this) {
730             ensureValidLocked();
731             return nativeGetResourceTypeName(mObject, resId);
732         }
733     }
734 
735     @UnsupportedAppUsage
getResourceEntryName(@nyRes int resId)736     @Nullable String getResourceEntryName(@AnyRes int resId) {
737         synchronized (this) {
738             ensureValidLocked();
739             return nativeGetResourceEntryName(mObject, resId);
740         }
741     }
742 
743     @UnsupportedAppUsage
getResourceIdentifier(@onNull String name, @Nullable String defType, @Nullable String defPackage)744     @AnyRes int getResourceIdentifier(@NonNull String name, @Nullable String defType,
745             @Nullable String defPackage) {
746         synchronized (this) {
747             ensureValidLocked();
748             // name is checked in JNI.
749             return nativeGetResourceIdentifier(mObject, name, defType, defPackage);
750         }
751     }
752 
753     /**
754      * Enable resource resolution logging to track the steps taken to resolve the last resource
755      * entry retrieved. Stores the configuration and package names for each step.
756      *
757      * Default disabled.
758      *
759      * @param enabled Boolean indicating whether to enable or disable logging.
760      *
761      * @hide
762      */
setResourceResolutionLoggingEnabled(boolean enabled)763     public void setResourceResolutionLoggingEnabled(boolean enabled) {
764         synchronized (this) {
765             ensureValidLocked();
766             nativeSetResourceResolutionLoggingEnabled(mObject, enabled);
767         }
768     }
769 
770     /**
771      * Retrieve the last resource resolution path logged.
772      *
773      * @return Formatted string containing last resource ID/name and steps taken to resolve final
774      * entry, including configuration and package names.
775      *
776      * @hide
777      */
getLastResourceResolution()778     public @Nullable String getLastResourceResolution() {
779         synchronized (this) {
780             ensureValidLocked();
781             return nativeGetLastResourceResolution(mObject);
782         }
783     }
784 
getPooledStringForCookie(int cookie, int id)785     CharSequence getPooledStringForCookie(int cookie, int id) {
786         // Cookies map to ApkAssets starting at 1.
787         return getApkAssets()[cookie - 1].getStringFromPool(id);
788     }
789 
790     /**
791      * Open an asset using ACCESS_STREAMING mode.  This provides access to
792      * files that have been bundled with an application as assets -- that is,
793      * files placed in to the "assets" directory.
794      *
795      * @param fileName The name of the asset to open.  This name can be hierarchical.
796      *
797      * @see #open(String, int)
798      * @see #list
799      */
open(@onNull String fileName)800     public @NonNull InputStream open(@NonNull String fileName) throws IOException {
801         return open(fileName, ACCESS_STREAMING);
802     }
803 
804     /**
805      * Open an asset using an explicit access mode, returning an InputStream to
806      * read its contents.  This provides access to files that have been bundled
807      * with an application as assets -- that is, files placed in to the
808      * "assets" directory.
809      *
810      * @param fileName The name of the asset to open.  This name can be hierarchical.
811      * @param accessMode Desired access mode for retrieving the data.
812      *
813      * @see #ACCESS_UNKNOWN
814      * @see #ACCESS_STREAMING
815      * @see #ACCESS_RANDOM
816      * @see #ACCESS_BUFFER
817      * @see #open(String)
818      * @see #list
819      */
open(@onNull String fileName, int accessMode)820     public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException {
821         Preconditions.checkNotNull(fileName, "fileName");
822         synchronized (this) {
823             ensureOpenLocked();
824             final long asset = nativeOpenAsset(mObject, fileName, accessMode);
825             if (asset == 0) {
826                 throw new FileNotFoundException("Asset file: " + fileName);
827             }
828             final AssetInputStream assetInputStream = new AssetInputStream(asset);
829             incRefsLocked(assetInputStream.hashCode());
830             return assetInputStream;
831         }
832     }
833 
834     /**
835      * Open an uncompressed asset by mmapping it and returning an {@link AssetFileDescriptor}.
836      * This provides access to files that have been bundled with an application as assets -- that
837      * is, files placed in to the "assets" directory.
838      *
839      * The asset must be uncompressed, or an exception will be thrown.
840      *
841      * @param fileName The name of the asset to open.  This name can be hierarchical.
842      * @return An open AssetFileDescriptor.
843      */
openFd(@onNull String fileName)844     public @NonNull AssetFileDescriptor openFd(@NonNull String fileName) throws IOException {
845         Preconditions.checkNotNull(fileName, "fileName");
846         synchronized (this) {
847             ensureOpenLocked();
848             final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets);
849             if (pfd == null) {
850                 throw new FileNotFoundException("Asset file: " + fileName);
851             }
852             return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
853         }
854     }
855 
856     /**
857      * Return a String array of all the assets at the given path.
858      *
859      * @param path A relative path within the assets, i.e., "docs/home.html".
860      *
861      * @return String[] Array of strings, one for each asset.  These file
862      *         names are relative to 'path'.  You can open the file by
863      *         concatenating 'path' and a name in the returned string (via
864      *         File) and passing that to open().
865      *
866      * @see #open
867      */
list(@onNull String path)868     public @Nullable String[] list(@NonNull String path) throws IOException {
869         Preconditions.checkNotNull(path, "path");
870         synchronized (this) {
871             ensureValidLocked();
872             return nativeList(mObject, path);
873         }
874     }
875 
876     /**
877      * Open a non-asset file as an asset using ACCESS_STREAMING mode.  This
878      * provides direct access to all of the files included in an application
879      * package (not only its assets).  Applications should not normally use
880      * this.
881      *
882      * @param fileName Name of the asset to retrieve.
883      *
884      * @see #open(String)
885      * @hide
886      */
887     @UnsupportedAppUsage
openNonAsset(@onNull String fileName)888     public @NonNull InputStream openNonAsset(@NonNull String fileName) throws IOException {
889         return openNonAsset(0, fileName, ACCESS_STREAMING);
890     }
891 
892     /**
893      * Open a non-asset file as an asset using a specific access mode.  This
894      * provides direct access to all of the files included in an application
895      * package (not only its assets).  Applications should not normally use
896      * this.
897      *
898      * @param fileName Name of the asset to retrieve.
899      * @param accessMode Desired access mode for retrieving the data.
900      *
901      * @see #ACCESS_UNKNOWN
902      * @see #ACCESS_STREAMING
903      * @see #ACCESS_RANDOM
904      * @see #ACCESS_BUFFER
905      * @see #open(String, int)
906      * @hide
907      */
908     @UnsupportedAppUsage
openNonAsset(@onNull String fileName, int accessMode)909     public @NonNull InputStream openNonAsset(@NonNull String fileName, int accessMode)
910             throws IOException {
911         return openNonAsset(0, fileName, accessMode);
912     }
913 
914     /**
915      * Open a non-asset in a specified package.  Not for use by applications.
916      *
917      * @param cookie Identifier of the package to be opened.
918      * @param fileName Name of the asset to retrieve.
919      * @hide
920      */
921     @UnsupportedAppUsage
openNonAsset(int cookie, @NonNull String fileName)922     public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName)
923             throws IOException {
924         return openNonAsset(cookie, fileName, ACCESS_STREAMING);
925     }
926 
927     /**
928      * Open a non-asset in a specified package.  Not for use by applications.
929      *
930      * @param cookie Identifier of the package to be opened.
931      * @param fileName Name of the asset to retrieve.
932      * @param accessMode Desired access mode for retrieving the data.
933      * @hide
934      */
935     @UnsupportedAppUsage
openNonAsset(int cookie, @NonNull String fileName, int accessMode)936     public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName, int accessMode)
937             throws IOException {
938         Preconditions.checkNotNull(fileName, "fileName");
939         synchronized (this) {
940             ensureOpenLocked();
941             final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode);
942             if (asset == 0) {
943                 throw new FileNotFoundException("Asset absolute file: " + fileName);
944             }
945             final AssetInputStream assetInputStream = new AssetInputStream(asset);
946             incRefsLocked(assetInputStream.hashCode());
947             return assetInputStream;
948         }
949     }
950 
951     /**
952      * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
953      * This provides direct access to all of the files included in an application
954      * package (not only its assets).  Applications should not normally use this.
955      *
956      * The asset must not be compressed, or an exception will be thrown.
957      *
958      * @param fileName Name of the asset to retrieve.
959      */
openNonAssetFd(@onNull String fileName)960     public @NonNull AssetFileDescriptor openNonAssetFd(@NonNull String fileName)
961             throws IOException {
962         return openNonAssetFd(0, fileName);
963     }
964 
965     /**
966      * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
967      * This provides direct access to all of the files included in an application
968      * package (not only its assets).  Applications should not normally use this.
969      *
970      * The asset must not be compressed, or an exception will be thrown.
971      *
972      * @param cookie Identifier of the package to be opened.
973      * @param fileName Name of the asset to retrieve.
974      */
openNonAssetFd(int cookie, @NonNull String fileName)975     public @NonNull AssetFileDescriptor openNonAssetFd(int cookie, @NonNull String fileName)
976             throws IOException {
977         Preconditions.checkNotNull(fileName, "fileName");
978         synchronized (this) {
979             ensureOpenLocked();
980             final ParcelFileDescriptor pfd =
981                     nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets);
982             if (pfd == null) {
983                 throw new FileNotFoundException("Asset absolute file: " + fileName);
984             }
985             return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
986         }
987     }
988 
989     /**
990      * Retrieve a parser for a compiled XML file.
991      *
992      * @param fileName The name of the file to retrieve.
993      */
openXmlResourceParser(@onNull String fileName)994     public @NonNull XmlResourceParser openXmlResourceParser(@NonNull String fileName)
995             throws IOException {
996         return openXmlResourceParser(0, fileName);
997     }
998 
999     /**
1000      * Retrieve a parser for a compiled XML file.
1001      *
1002      * @param cookie Identifier of the package to be opened.
1003      * @param fileName The name of the file to retrieve.
1004      */
openXmlResourceParser(int cookie, @NonNull String fileName)1005     public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName)
1006             throws IOException {
1007         try (XmlBlock block = openXmlBlockAsset(cookie, fileName)) {
1008             XmlResourceParser parser = block.newParser();
1009             // If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with
1010             // a valid native pointer, which makes newParser always return non-null. But let's
1011             // be paranoid.
1012             if (parser == null) {
1013                 throw new AssertionError("block.newParser() returned a null parser");
1014             }
1015             return parser;
1016         }
1017     }
1018 
1019     /**
1020      * Retrieve a non-asset as a compiled XML file.  Not for use by applications.
1021      *
1022      * @param fileName The name of the file to retrieve.
1023      * @hide
1024      */
openXmlBlockAsset(@onNull String fileName)1025     @NonNull XmlBlock openXmlBlockAsset(@NonNull String fileName) throws IOException {
1026         return openXmlBlockAsset(0, fileName);
1027     }
1028 
1029     /**
1030      * Retrieve a non-asset as a compiled XML file.  Not for use by
1031      * applications.
1032      *
1033      * @param cookie Identifier of the package to be opened.
1034      * @param fileName Name of the asset to retrieve.
1035      * @hide
1036      */
openXmlBlockAsset(int cookie, @NonNull String fileName)1037     @NonNull XmlBlock openXmlBlockAsset(int cookie, @NonNull String fileName) throws IOException {
1038         Preconditions.checkNotNull(fileName, "fileName");
1039         synchronized (this) {
1040             ensureOpenLocked();
1041             final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
1042             if (xmlBlock == 0) {
1043                 throw new FileNotFoundException("Asset XML file: " + fileName);
1044             }
1045             final XmlBlock block = new XmlBlock(this, xmlBlock);
1046             incRefsLocked(block.hashCode());
1047             return block;
1048         }
1049     }
1050 
xmlBlockGone(int id)1051     void xmlBlockGone(int id) {
1052         synchronized (this) {
1053             decRefsLocked(id);
1054         }
1055     }
1056 
1057     @UnsupportedAppUsage
applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress, long outIndicesAddress)1058     void applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
1059             @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress,
1060             long outIndicesAddress) {
1061         Preconditions.checkNotNull(inAttrs, "inAttrs");
1062         synchronized (this) {
1063             // Need to synchronize on AssetManager because we will be accessing
1064             // the native implementation of AssetManager.
1065             ensureValidLocked();
1066             nativeApplyStyle(mObject, themePtr, defStyleAttr, defStyleRes,
1067                     parser != null ? parser.mParseState : 0, inAttrs, outValuesAddress,
1068                     outIndicesAddress);
1069         }
1070     }
1071 
getAttributeResolutionStack(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @StyleRes int xmlStyle)1072     int[] getAttributeResolutionStack(long themePtr, @AttrRes int defStyleAttr,
1073             @StyleRes int defStyleRes, @StyleRes int xmlStyle) {
1074         synchronized (this) {
1075             return nativeAttributeResolutionStack(
1076                     mObject, themePtr, xmlStyle, defStyleAttr, defStyleRes);
1077         }
1078     }
1079 
1080     @UnsupportedAppUsage
resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices)1081     boolean resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
1082             @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues,
1083             @NonNull int[] outIndices) {
1084         Preconditions.checkNotNull(inAttrs, "inAttrs");
1085         Preconditions.checkNotNull(outValues, "outValues");
1086         Preconditions.checkNotNull(outIndices, "outIndices");
1087         synchronized (this) {
1088             // Need to synchronize on AssetManager because we will be accessing
1089             // the native implementation of AssetManager.
1090             ensureValidLocked();
1091             return nativeResolveAttrs(mObject,
1092                     themePtr, defStyleAttr, defStyleRes, inValues, inAttrs, outValues, outIndices);
1093         }
1094     }
1095 
1096     @UnsupportedAppUsage
retrieveAttributes(@onNull XmlBlock.Parser parser, @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices)1097     boolean retrieveAttributes(@NonNull XmlBlock.Parser parser, @NonNull int[] inAttrs,
1098             @NonNull int[] outValues, @NonNull int[] outIndices) {
1099         Preconditions.checkNotNull(parser, "parser");
1100         Preconditions.checkNotNull(inAttrs, "inAttrs");
1101         Preconditions.checkNotNull(outValues, "outValues");
1102         Preconditions.checkNotNull(outIndices, "outIndices");
1103         synchronized (this) {
1104             // Need to synchronize on AssetManager because we will be accessing
1105             // the native implementation of AssetManager.
1106             ensureValidLocked();
1107             return nativeRetrieveAttributes(
1108                     mObject, parser.mParseState, inAttrs, outValues, outIndices);
1109         }
1110     }
1111 
1112     @UnsupportedAppUsage
createTheme()1113     long createTheme() {
1114         synchronized (this) {
1115             ensureValidLocked();
1116             long themePtr = nativeThemeCreate(mObject);
1117             incRefsLocked(themePtr);
1118             return themePtr;
1119         }
1120     }
1121 
releaseTheme(long themePtr)1122     void releaseTheme(long themePtr) {
1123         synchronized (this) {
1124             nativeThemeDestroy(themePtr);
1125             decRefsLocked(themePtr);
1126         }
1127     }
1128 
applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force)1129     void applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force) {
1130         synchronized (this) {
1131             // Need to synchronize on AssetManager because we will be accessing
1132             // the native implementation of AssetManager.
1133             ensureValidLocked();
1134             nativeThemeApplyStyle(mObject, themePtr, resId, force);
1135         }
1136     }
1137 
1138     @UnsupportedAppUsage
setThemeTo(long dstThemePtr, @NonNull AssetManager srcAssetManager, long srcThemePtr)1139     void setThemeTo(long dstThemePtr, @NonNull AssetManager srcAssetManager, long srcThemePtr) {
1140         synchronized (this) {
1141             ensureValidLocked();
1142             synchronized (srcAssetManager) {
1143                 srcAssetManager.ensureValidLocked();
1144                 nativeThemeCopy(mObject, dstThemePtr, srcAssetManager.mObject, srcThemePtr);
1145             }
1146         }
1147     }
1148 
1149     @Override
finalize()1150     protected void finalize() throws Throwable {
1151         if (DEBUG_REFS && mNumRefs != 0) {
1152             Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs);
1153             if (mRefStacks != null) {
1154                 for (RuntimeException e : mRefStacks.values()) {
1155                     Log.w(TAG, "Reference from here", e);
1156                 }
1157             }
1158         }
1159 
1160         synchronized (this) {
1161             if (mObject != 0) {
1162                 nativeDestroy(mObject);
1163                 mObject = 0;
1164             }
1165         }
1166     }
1167 
1168     /* No Locking is needed for AssetInputStream because an AssetInputStream is not-thread
1169     safe and it does not rely on AssetManager once it has been created. It completely owns the
1170     underlying Asset. */
1171     public final class AssetInputStream extends InputStream {
1172         private long mAssetNativePtr;
1173         private long mLength;
1174         private long mMarkPos;
1175 
1176         /**
1177          * @hide
1178          */
1179         @UnsupportedAppUsage
getAssetInt()1180         public final int getAssetInt() {
1181             throw new UnsupportedOperationException();
1182         }
1183 
1184         /**
1185          * @hide
1186          */
1187         @UnsupportedAppUsage
getNativeAsset()1188         public final long getNativeAsset() {
1189             return mAssetNativePtr;
1190         }
1191 
AssetInputStream(long assetNativePtr)1192         private AssetInputStream(long assetNativePtr) {
1193             mAssetNativePtr = assetNativePtr;
1194             mLength = nativeAssetGetLength(assetNativePtr);
1195         }
1196 
1197         @Override
read()1198         public final int read() throws IOException {
1199             ensureOpen();
1200             return nativeAssetReadChar(mAssetNativePtr);
1201         }
1202 
1203         @Override
read(@onNull byte[] b)1204         public final int read(@NonNull byte[] b) throws IOException {
1205             ensureOpen();
1206             Preconditions.checkNotNull(b, "b");
1207             return nativeAssetRead(mAssetNativePtr, b, 0, b.length);
1208         }
1209 
1210         @Override
read(@onNull byte[] b, int off, int len)1211         public final int read(@NonNull byte[] b, int off, int len) throws IOException {
1212             ensureOpen();
1213             Preconditions.checkNotNull(b, "b");
1214             return nativeAssetRead(mAssetNativePtr, b, off, len);
1215         }
1216 
1217         @Override
skip(long n)1218         public final long skip(long n) throws IOException {
1219             ensureOpen();
1220             long pos = nativeAssetSeek(mAssetNativePtr, 0, 0);
1221             if ((pos + n) > mLength) {
1222                 n = mLength - pos;
1223             }
1224             if (n > 0) {
1225                 nativeAssetSeek(mAssetNativePtr, n, 0);
1226             }
1227             return n;
1228         }
1229 
1230         @Override
available()1231         public final int available() throws IOException {
1232             ensureOpen();
1233             final long len = nativeAssetGetRemainingLength(mAssetNativePtr);
1234             return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len;
1235         }
1236 
1237         @Override
markSupported()1238         public final boolean markSupported() {
1239             return true;
1240         }
1241 
1242         @Override
mark(int readlimit)1243         public final void mark(int readlimit) {
1244             ensureOpen();
1245             mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0);
1246         }
1247 
1248         @Override
reset()1249         public final void reset() throws IOException {
1250             ensureOpen();
1251             nativeAssetSeek(mAssetNativePtr, mMarkPos, -1);
1252         }
1253 
1254         @Override
close()1255         public final void close() throws IOException {
1256             if (mAssetNativePtr != 0) {
1257                 nativeAssetDestroy(mAssetNativePtr);
1258                 mAssetNativePtr = 0;
1259 
1260                 synchronized (AssetManager.this) {
1261                     decRefsLocked(hashCode());
1262                 }
1263             }
1264         }
1265 
1266         @Override
finalize()1267         protected void finalize() throws Throwable {
1268             close();
1269         }
1270 
ensureOpen()1271         private void ensureOpen() {
1272             if (mAssetNativePtr == 0) {
1273                 throw new IllegalStateException("AssetInputStream is closed");
1274             }
1275         }
1276     }
1277 
1278     /**
1279      * Determine whether the state in this asset manager is up-to-date with
1280      * the files on the filesystem.  If false is returned, you need to
1281      * instantiate a new AssetManager class to see the new data.
1282      * @hide
1283      */
1284     @UnsupportedAppUsage
isUpToDate()1285     public boolean isUpToDate() {
1286         synchronized (this) {
1287             if (!mOpen) {
1288                 return false;
1289             }
1290 
1291             for (ApkAssets apkAssets : mApkAssets) {
1292                 if (!apkAssets.isUpToDate()) {
1293                     return false;
1294                 }
1295             }
1296 
1297             return true;
1298         }
1299     }
1300 
1301     /**
1302      * Get the locales that this asset manager contains data for.
1303      *
1304      * <p>On SDK 21 (Android 5.0: Lollipop) and above, Locale strings are valid
1305      * <a href="https://tools.ietf.org/html/bcp47">BCP-47</a> language tags and can be
1306      * parsed using {@link java.util.Locale#forLanguageTag(String)}.
1307      *
1308      * <p>On SDK 20 (Android 4.4W: Kitkat for watches) and below, locale strings
1309      * are of the form {@code ll_CC} where {@code ll} is a two letter language code,
1310      * and {@code CC} is a two letter country code.
1311      */
getLocales()1312     public String[] getLocales() {
1313         synchronized (this) {
1314             ensureValidLocked();
1315             return nativeGetLocales(mObject, false /*excludeSystem*/);
1316         }
1317     }
1318 
1319     /**
1320      * Same as getLocales(), except that locales that are only provided by the system (i.e. those
1321      * present in framework-res.apk or its overlays) will not be listed.
1322      *
1323      * For example, if the "system" assets support English, French, and German, and the additional
1324      * assets support Cherokee and French, getLocales() would return
1325      * [Cherokee, English, French, German], while getNonSystemLocales() would return
1326      * [Cherokee, French].
1327      * @hide
1328      */
getNonSystemLocales()1329     public String[] getNonSystemLocales() {
1330         synchronized (this) {
1331             ensureValidLocked();
1332             return nativeGetLocales(mObject, true /*excludeSystem*/);
1333         }
1334     }
1335 
1336     /**
1337      * @hide
1338      */
getSizeConfigurations()1339     Configuration[] getSizeConfigurations() {
1340         synchronized (this) {
1341             ensureValidLocked();
1342             return nativeGetSizeConfigurations(mObject);
1343         }
1344     }
1345 
1346     /**
1347      * Change the configuration used when retrieving resources.  Not for use by
1348      * applications.
1349      * @hide
1350      */
1351     @UnsupportedAppUsage
setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion)1352     public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation,
1353             int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
1354             int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
1355             int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) {
1356         synchronized (this) {
1357             ensureValidLocked();
1358             nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density,
1359                     keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
1360                     smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
1361                     colorMode, majorVersion);
1362         }
1363     }
1364 
1365     /**
1366      * @hide
1367      */
1368     @UnsupportedAppUsage
getAssignedPackageIdentifiers()1369     public SparseArray<String> getAssignedPackageIdentifiers() {
1370         synchronized (this) {
1371             ensureValidLocked();
1372             return nativeGetAssignedPackageIdentifiers(mObject);
1373         }
1374     }
1375 
1376     /**
1377      * @hide
1378      */
1379     @TestApi
1380     @GuardedBy("this")
getOverlayableMap(String packageName)1381     public @Nullable Map<String, String> getOverlayableMap(String packageName) {
1382         synchronized (this) {
1383             ensureValidLocked();
1384             return nativeGetOverlayableMap(mObject, packageName);
1385         }
1386     }
1387 
1388     @GuardedBy("this")
incRefsLocked(long id)1389     private void incRefsLocked(long id) {
1390         if (DEBUG_REFS) {
1391             if (mRefStacks == null) {
1392                 mRefStacks = new HashMap<>();
1393             }
1394             RuntimeException ex = new RuntimeException();
1395             ex.fillInStackTrace();
1396             mRefStacks.put(id, ex);
1397         }
1398         mNumRefs++;
1399     }
1400 
1401     @GuardedBy("this")
decRefsLocked(long id)1402     private void decRefsLocked(long id) {
1403         if (DEBUG_REFS && mRefStacks != null) {
1404             mRefStacks.remove(id);
1405         }
1406         mNumRefs--;
1407         if (mNumRefs == 0 && mObject != 0) {
1408             nativeDestroy(mObject);
1409             mObject = 0;
1410             mApkAssets = sEmptyApkAssets;
1411         }
1412     }
1413 
1414     // AssetManager setup native methods.
nativeCreate()1415     private static native long nativeCreate();
nativeDestroy(long ptr)1416     private static native void nativeDestroy(long ptr);
nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, boolean invalidateCaches)1417     private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets,
1418             boolean invalidateCaches);
nativeSetConfiguration(long ptr, int mcc, int mnc, @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion)1419     private static native void nativeSetConfiguration(long ptr, int mcc, int mnc,
1420             @Nullable String locale, int orientation, int touchscreen, int density, int keyboard,
1421             int keyboardHidden, int navigation, int screenWidth, int screenHeight,
1422             int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout,
1423             int uiMode, int colorMode, int majorVersion);
nativeGetAssignedPackageIdentifiers( long ptr)1424     private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
1425             long ptr);
1426 
1427     // File native methods.
nativeList(long ptr, @NonNull String path)1428     private static native @Nullable String[] nativeList(long ptr, @NonNull String path)
1429             throws IOException;
nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode)1430     private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode);
nativeOpenAssetFd(long ptr, @NonNull String fileName, long[] outOffsets)1431     private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr,
1432             @NonNull String fileName, long[] outOffsets) throws IOException;
nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, int accessMode)1433     private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName,
1434             int accessMode);
nativeOpenNonAssetFd(long ptr, int cookie, @NonNull String fileName, @NonNull long[] outOffsets)1435     private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie,
1436             @NonNull String fileName, @NonNull long[] outOffsets) throws IOException;
nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName)1437     private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName);
1438 
1439     // Primitive resource native methods.
nativeGetResourceValue(long ptr, @AnyRes int resId, short density, @NonNull TypedValue outValue, boolean resolveReferences)1440     private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density,
1441             @NonNull TypedValue outValue, boolean resolveReferences);
nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, @NonNull TypedValue outValue)1442     private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId,
1443             @NonNull TypedValue outValue);
1444 
nativeGetStyleAttributes(long ptr, @StyleRes int resId)1445     private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr,
1446             @StyleRes int resId);
nativeGetResourceStringArray(long ptr, @ArrayRes int resId)1447     private static native @Nullable String[] nativeGetResourceStringArray(long ptr,
1448             @ArrayRes int resId);
nativeGetResourceStringArrayInfo(long ptr, @ArrayRes int resId)1449     private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr,
1450             @ArrayRes int resId);
nativeGetResourceIntArray(long ptr, @ArrayRes int resId)1451     private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId);
nativeGetResourceArraySize(long ptr, @ArrayRes int resId)1452     private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId);
nativeGetResourceArray(long ptr, @ArrayRes int resId, @NonNull int[] outValues)1453     private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId,
1454             @NonNull int[] outValues);
1455 
1456     // Resource name/ID native methods.
nativeGetResourceIdentifier(long ptr, @NonNull String name, @Nullable String defType, @Nullable String defPackage)1457     private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name,
1458             @Nullable String defType, @Nullable String defPackage);
nativeGetResourceName(long ptr, @AnyRes int resid)1459     private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid);
nativeGetResourcePackageName(long ptr, @AnyRes int resid)1460     private static native @Nullable String nativeGetResourcePackageName(long ptr,
1461             @AnyRes int resid);
nativeGetResourceTypeName(long ptr, @AnyRes int resid)1462     private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid);
nativeGetResourceEntryName(long ptr, @AnyRes int resid)1463     private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid);
nativeGetLocales(long ptr, boolean excludeSystem)1464     private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem);
nativeGetSizeConfigurations(long ptr)1465     private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr);
nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled)1466     private static native void nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled);
nativeGetLastResourceResolution(long ptr)1467     private static native @Nullable String nativeGetLastResourceResolution(long ptr);
1468 
1469     // Style attribute retrieval native methods.
nativeAttributeResolutionStack(long ptr, long themePtr, @StyleRes int xmlStyleRes, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)1470     private static native int[] nativeAttributeResolutionStack(long ptr, long themePtr,
1471             @StyleRes int xmlStyleRes, @AttrRes int defStyleAttr, @StyleRes int defStyleRes);
nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, long outValuesAddress, long outIndicesAddress)1472     private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr,
1473             @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs,
1474             long outValuesAddress, long outIndicesAddress);
nativeResolveAttrs(long ptr, long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices)1475     private static native boolean nativeResolveAttrs(long ptr, long themePtr,
1476             @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues,
1477             @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
nativeRetrieveAttributes(long ptr, long xmlParserPtr, @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices)1478     private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr,
1479             @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
1480 
1481     // Theme related native methods
nativeThemeCreate(long ptr)1482     private static native long nativeThemeCreate(long ptr);
nativeThemeDestroy(long themePtr)1483     private static native void nativeThemeDestroy(long themePtr);
nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, boolean force)1484     private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId,
1485             boolean force);
nativeThemeCopy(long dstAssetManagerPtr, long dstThemePtr, long srcAssetManagerPtr, long srcThemePtr)1486     private static native void nativeThemeCopy(long dstAssetManagerPtr, long dstThemePtr,
1487             long srcAssetManagerPtr, long srcThemePtr);
nativeThemeClear(long themePtr)1488     static native void nativeThemeClear(long themePtr);
nativeThemeGetAttributeValue(long ptr, long themePtr, @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve)1489     private static native int nativeThemeGetAttributeValue(long ptr, long themePtr,
1490             @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve);
nativeThemeDump(long ptr, long themePtr, int priority, String tag, String prefix)1491     private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag,
1492             String prefix);
nativeThemeGetChangingConfigurations(long themePtr)1493     static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr);
1494 
1495     // AssetInputStream related native methods.
nativeAssetDestroy(long assetPtr)1496     private static native void nativeAssetDestroy(long assetPtr);
nativeAssetReadChar(long assetPtr)1497     private static native int nativeAssetReadChar(long assetPtr);
nativeAssetRead(long assetPtr, byte[] b, int off, int len)1498     private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len);
nativeAssetSeek(long assetPtr, long offset, int whence)1499     private static native long nativeAssetSeek(long assetPtr, long offset, int whence);
nativeAssetGetLength(long assetPtr)1500     private static native long nativeAssetGetLength(long assetPtr);
nativeAssetGetRemainingLength(long assetPtr)1501     private static native long nativeAssetGetRemainingLength(long assetPtr);
1502 
nativeVerifySystemIdmaps()1503     private static native void nativeVerifySystemIdmaps();
nativeCreateIdmapsForStaticOverlaysTargetingAndroid()1504     private static native String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid();
nativeGetOverlayableMap(long ptr, @NonNull String packageName)1505     private static native @Nullable Map nativeGetOverlayableMap(long ptr,
1506             @NonNull String packageName);
1507 
1508     // Global debug native methods.
1509     /**
1510      * @hide
1511      */
1512     @UnsupportedAppUsage
getGlobalAssetCount()1513     public static native int getGlobalAssetCount();
1514 
1515     /**
1516      * @hide
1517      */
getAssetAllocations()1518     public static native String getAssetAllocations();
1519 
1520     /**
1521      * @hide
1522      */
1523     @UnsupportedAppUsage
getGlobalAssetManagerCount()1524     public static native int getGlobalAssetManagerCount();
1525 }
1526