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