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