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