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         // Let's still check if the native object exists, given all the memory corruptions.
532         if (mObject == 0) {
533             throw new RuntimeException("AssetManager is open but the native object is gone");
534         }
535     }
536 
537     /**
538      * Populates {@code outValue} with the data associated a particular
539      * resource identifier for the current configuration.
540      *
541      * @param resId the resource identifier to load
542      * @param densityDpi the density bucket for which to load the resource
543      * @param outValue the typed value in which to put the data
544      * @param resolveRefs {@code true} to resolve references, {@code false}
545      *                    to leave them unresolved
546      * @return {@code true} if the data was loaded into {@code outValue},
547      *         {@code false} otherwise
548      */
549     @UnsupportedAppUsage
getResourceValue(@nyRes int resId, int densityDpi, @NonNull TypedValue outValue, boolean resolveRefs)550     boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
551             boolean resolveRefs) {
552         Objects.requireNonNull(outValue, "outValue");
553         synchronized (this) {
554             ensureValidLocked();
555             final int cookie = nativeGetResourceValue(
556                     mObject, resId, (short) densityDpi, outValue, resolveRefs);
557             if (cookie <= 0) {
558                 return false;
559             }
560 
561             // Convert the changing configurations flags populated by native code.
562             outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
563                     outValue.changingConfigurations);
564 
565             if (outValue.type == TypedValue.TYPE_STRING) {
566                 if ((outValue.string = getPooledStringForCookie(cookie, outValue.data)) == null) {
567                     return false;
568                 }
569             }
570             return true;
571         }
572     }
573 
574     /**
575      * Retrieves the string value associated with a particular resource
576      * identifier for the current configuration.
577      *
578      * @param resId the resource identifier to load
579      * @return the string value, or {@code null}
580      */
581     @UnsupportedAppUsage
getResourceText(@tringRes int resId)582     @Nullable CharSequence getResourceText(@StringRes int resId) {
583         synchronized (this) {
584             final TypedValue outValue = mValue;
585             if (getResourceValue(resId, 0, outValue, true)) {
586                 return outValue.coerceToString();
587             }
588             return null;
589         }
590     }
591 
592     /**
593      * Retrieves the string value associated with a particular resource
594      * identifier for the current configuration.
595      *
596      * @param resId the resource identifier to load
597      * @param bagEntryId the index into the bag to load
598      * @return the string value, or {@code null}
599      */
600     @UnsupportedAppUsage
getResourceBagText(@tringRes int resId, int bagEntryId)601     @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
602         synchronized (this) {
603             ensureValidLocked();
604             final TypedValue outValue = mValue;
605             final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue);
606             if (cookie <= 0) {
607                 return null;
608             }
609 
610             // Convert the changing configurations flags populated by native code.
611             outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
612                     outValue.changingConfigurations);
613 
614             if (outValue.type == TypedValue.TYPE_STRING) {
615                 return getPooledStringForCookie(cookie, outValue.data);
616             }
617             return outValue.coerceToString();
618         }
619     }
620 
getResourceArraySize(@rrayRes int resId)621     int getResourceArraySize(@ArrayRes int resId) {
622         synchronized (this) {
623             ensureValidLocked();
624             return nativeGetResourceArraySize(mObject, resId);
625         }
626     }
627 
628     /**
629      * Populates `outData` with array elements of `resId`. `outData` is normally
630      * used with
631      * {@link TypedArray}.
632      *
633      * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES}
634      * long,
635      * with the indices of the data representing the type, value, asset cookie,
636      * resource ID,
637      * configuration change mask, and density of the element.
638      *
639      * @param resId The resource ID of an array resource.
640      * @param outData The array to populate with data.
641      * @return The length of the array.
642      *
643      * @see TypedArray#STYLE_TYPE
644      * @see TypedArray#STYLE_DATA
645      * @see TypedArray#STYLE_ASSET_COOKIE
646      * @see TypedArray#STYLE_RESOURCE_ID
647      * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS
648      * @see TypedArray#STYLE_DENSITY
649      */
getResourceArray(@rrayRes int resId, @NonNull int[] outData)650     int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) {
651         Objects.requireNonNull(outData, "outData");
652         synchronized (this) {
653             ensureValidLocked();
654             return nativeGetResourceArray(mObject, resId, outData);
655         }
656     }
657 
658     /**
659      * Retrieves the string array associated with a particular resource
660      * identifier for the current configuration.
661      *
662      * @param resId the resource identifier of the string array
663      * @return the string array, or {@code null}
664      */
getResourceStringArray(@rrayRes int resId)665     @Nullable String[] getResourceStringArray(@ArrayRes int resId) {
666         synchronized (this) {
667             ensureValidLocked();
668             return nativeGetResourceStringArray(mObject, resId);
669         }
670     }
671 
672     /**
673      * Retrieve the text array associated with a particular resource
674      * identifier.
675      *
676      * @param resId the resource id of the string array
677      */
getResourceTextArray(@rrayRes int resId)678     @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) {
679         synchronized (this) {
680             ensureValidLocked();
681             final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId);
682             if (rawInfoArray == null) {
683                 return null;
684             }
685 
686             final int rawInfoArrayLen = rawInfoArray.length;
687             final int infoArrayLen = rawInfoArrayLen / 2;
688             final CharSequence[] retArray = new CharSequence[infoArrayLen];
689             for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) {
690                 int cookie = rawInfoArray[i];
691                 int index = rawInfoArray[i + 1];
692                 retArray[j] = (index >= 0 && cookie > 0)
693                         ? getPooledStringForCookie(cookie, index) : null;
694             }
695             return retArray;
696         }
697     }
698 
getResourceIntArray(@rrayRes int resId)699     @Nullable int[] getResourceIntArray(@ArrayRes int resId) {
700         synchronized (this) {
701             ensureValidLocked();
702             return nativeGetResourceIntArray(mObject, resId);
703         }
704     }
705 
706     /**
707      * Get the attributes for a style resource. These are the &lt;item&gt;
708      * elements in
709      * a &lt;style&gt; resource.
710      * @param resId The resource ID of the style
711      * @return An array of attribute IDs.
712      */
getStyleAttributes(@tyleRes int resId)713     @AttrRes int[] getStyleAttributes(@StyleRes int resId) {
714         synchronized (this) {
715             ensureValidLocked();
716             return nativeGetStyleAttributes(mObject, resId);
717         }
718     }
719 
720     /**
721      * Populates {@code outValue} with the data associated with a particular
722      * resource identifier for the current configuration. Resolves theme
723      * attributes against the specified theme.
724      *
725      * @param theme the native pointer of the theme
726      * @param resId the resource identifier to load
727      * @param outValue the typed value in which to put the data
728      * @param resolveRefs {@code true} to resolve references, {@code false}
729      *                    to leave them unresolved
730      * @return {@code true} if the data was loaded into {@code outValue},
731      *         {@code false} otherwise
732      */
getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs)733     boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue,
734             boolean resolveRefs) {
735         Objects.requireNonNull(outValue, "outValue");
736         synchronized (this) {
737             ensureValidLocked();
738             final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue,
739                     resolveRefs);
740             if (cookie <= 0) {
741                 return false;
742             }
743 
744             // Convert the changing configurations flags populated by native code.
745             outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
746                     outValue.changingConfigurations);
747 
748             if (outValue.type == TypedValue.TYPE_STRING) {
749                 if ((outValue.string = getPooledStringForCookie(cookie, outValue.data)) == null) {
750                     return false;
751                 }
752             }
753             return true;
754         }
755     }
756 
dumpTheme(long theme, int priority, String tag, String prefix)757     void dumpTheme(long theme, int priority, String tag, String prefix) {
758         synchronized (this) {
759             ensureValidLocked();
760             nativeThemeDump(mObject, theme, priority, tag, prefix);
761         }
762     }
763 
764     @UnsupportedAppUsage
getResourceName(@nyRes int resId)765     @Nullable String getResourceName(@AnyRes int resId) {
766         synchronized (this) {
767             ensureValidLocked();
768             return nativeGetResourceName(mObject, resId);
769         }
770     }
771 
772     @UnsupportedAppUsage
getResourcePackageName(@nyRes int resId)773     @Nullable String getResourcePackageName(@AnyRes int resId) {
774         synchronized (this) {
775             ensureValidLocked();
776             return nativeGetResourcePackageName(mObject, resId);
777         }
778     }
779 
780     @UnsupportedAppUsage
getResourceTypeName(@nyRes int resId)781     @Nullable String getResourceTypeName(@AnyRes int resId) {
782         synchronized (this) {
783             ensureValidLocked();
784             return nativeGetResourceTypeName(mObject, resId);
785         }
786     }
787 
788     @UnsupportedAppUsage
getResourceEntryName(@nyRes int resId)789     @Nullable String getResourceEntryName(@AnyRes int resId) {
790         synchronized (this) {
791             ensureValidLocked();
792             return nativeGetResourceEntryName(mObject, resId);
793         }
794     }
795 
796     @UnsupportedAppUsage
getResourceIdentifier(@onNull String name, @Nullable String defType, @Nullable String defPackage)797     @AnyRes int getResourceIdentifier(@NonNull String name, @Nullable String defType,
798             @Nullable String defPackage) {
799         synchronized (this) {
800             ensureValidLocked();
801             // name is checked in JNI.
802             return nativeGetResourceIdentifier(mObject, name, defType, defPackage);
803         }
804     }
805 
806     /**
807      * To get the parent theme resource id according to the parameter theme resource id.
808      * @param resId theme resource id.
809      * @return the parent theme resource id.
810      * @hide
811      */
812     @StyleRes
getParentThemeIdentifier(@tyleRes int resId)813     int getParentThemeIdentifier(@StyleRes int resId) {
814         synchronized (this) {
815             ensureValidLocked();
816             // name is checked in JNI.
817             return nativeGetParentThemeIdentifier(mObject, resId);
818         }
819     }
820 
821     /**
822      * Enable resource resolution logging to track the steps taken to resolve the last resource
823      * entry retrieved. Stores the configuration and package names for each step.
824      *
825      * Default disabled.
826      *
827      * @param enabled Boolean indicating whether to enable or disable logging.
828      *
829      * @hide
830      */
831     @TestApi
setResourceResolutionLoggingEnabled(boolean enabled)832     public void setResourceResolutionLoggingEnabled(boolean enabled) {
833         synchronized (this) {
834             ensureValidLocked();
835             nativeSetResourceResolutionLoggingEnabled(mObject, enabled);
836         }
837     }
838 
839     /**
840      * Retrieve the last resource resolution path logged.
841      *
842      * @return Formatted string containing last resource ID/name and steps taken to resolve final
843      * entry, including configuration and package names.
844      *
845      * @hide
846      */
847     @TestApi
getLastResourceResolution()848     public @Nullable String getLastResourceResolution() {
849         synchronized (this) {
850             ensureValidLocked();
851             return nativeGetLastResourceResolution(mObject);
852         }
853     }
854 
855     /**
856      * Returns whether the {@code resources.arsc} of any loaded apk assets is allocated in RAM
857      * (not mmapped).
858      *
859      * @hide
860      */
containsAllocatedTable()861     public boolean containsAllocatedTable() {
862         synchronized (this) {
863             ensureValidLocked();
864             return nativeContainsAllocatedTable(mObject);
865         }
866     }
867 
868     @Nullable
getPooledStringForCookie(int cookie, int id)869     CharSequence getPooledStringForCookie(int cookie, int id) {
870         // Cookies map to ApkAssets starting at 1.
871         return getApkAssets()[cookie - 1].getStringFromPool(id);
872     }
873 
874     /**
875      * Open an asset using ACCESS_STREAMING mode.  This provides access to
876      * files that have been bundled with an application as assets -- that is,
877      * files placed in to the "assets" directory.
878      *
879      * @param fileName The name of the asset to open.  This name can be hierarchical.
880      *
881      * @see #open(String, int)
882      * @see #list
883      */
open(@onNull String fileName)884     public @NonNull InputStream open(@NonNull String fileName) throws IOException {
885         return open(fileName, ACCESS_STREAMING);
886     }
887 
888     /**
889      * Open an asset using an explicit access mode, returning an InputStream to
890      * read its contents.  This provides access to files that have been bundled
891      * with an application as assets -- that is, files placed in to the
892      * "assets" directory.
893      *
894      * @param fileName The name of the asset to open.  This name can be hierarchical.
895      * @param accessMode Desired access mode for retrieving the data.
896      *
897      * @see #ACCESS_UNKNOWN
898      * @see #ACCESS_STREAMING
899      * @see #ACCESS_RANDOM
900      * @see #ACCESS_BUFFER
901      * @see #open(String)
902      * @see #list
903      */
open(@onNull String fileName, int accessMode)904     public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException {
905         Objects.requireNonNull(fileName, "fileName");
906         synchronized (this) {
907             ensureOpenLocked();
908             final long asset = nativeOpenAsset(mObject, fileName, accessMode);
909             if (asset == 0) {
910                 throw new FileNotFoundException("Asset file: " + fileName);
911             }
912             final AssetInputStream assetInputStream = new AssetInputStream(asset);
913             incRefsLocked(assetInputStream.hashCode());
914             return assetInputStream;
915         }
916     }
917 
918     /**
919      * Open an uncompressed asset by mmapping it and returning an {@link AssetFileDescriptor}.
920      * This provides access to files that have been bundled with an application as assets -- that
921      * is, files placed in to the "assets" directory.
922      *
923      * The asset must be uncompressed, or an exception will be thrown.
924      *
925      * @param fileName The name of the asset to open.  This name can be hierarchical.
926      * @return An open AssetFileDescriptor.
927      */
openFd(@onNull String fileName)928     public @NonNull AssetFileDescriptor openFd(@NonNull String fileName) throws IOException {
929         Objects.requireNonNull(fileName, "fileName");
930         synchronized (this) {
931             ensureOpenLocked();
932             final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets);
933             if (pfd == null) {
934                 throw new FileNotFoundException("Asset file: " + fileName);
935             }
936             return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
937         }
938     }
939 
940     /**
941      * Return a String array of all the assets at the given path.
942      *
943      * @param path A relative path within the assets, i.e., "docs/home.html".
944      *
945      * @return String[] Array of strings, one for each asset.  These file
946      *         names are relative to 'path'.  You can open the file by
947      *         concatenating 'path' and a name in the returned string (via
948      *         File) and passing that to open().
949      *
950      * @see #open
951      */
list(@onNull String path)952     public @Nullable String[] list(@NonNull String path) throws IOException {
953         Objects.requireNonNull(path, "path");
954         synchronized (this) {
955             ensureValidLocked();
956             return nativeList(mObject, path);
957         }
958     }
959 
960     /**
961      * Open a non-asset file as an asset using ACCESS_STREAMING mode.  This
962      * provides direct access to all of the files included in an application
963      * package (not only its assets).  Applications should not normally use
964      * this.
965      *
966      * @param fileName Name of the asset to retrieve.
967      *
968      * @see #open(String)
969      * @hide
970      */
971     @UnsupportedAppUsage
openNonAsset(@onNull String fileName)972     public @NonNull InputStream openNonAsset(@NonNull String fileName) throws IOException {
973         return openNonAsset(0, fileName, ACCESS_STREAMING);
974     }
975 
976     /**
977      * Open a non-asset file as an asset using a specific access mode.  This
978      * provides direct access to all of the files included in an application
979      * package (not only its assets).  Applications should not normally use
980      * this.
981      *
982      * @param fileName Name of the asset to retrieve.
983      * @param accessMode Desired access mode for retrieving the data.
984      *
985      * @see #ACCESS_UNKNOWN
986      * @see #ACCESS_STREAMING
987      * @see #ACCESS_RANDOM
988      * @see #ACCESS_BUFFER
989      * @see #open(String, int)
990      * @hide
991      */
992     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
openNonAsset(@onNull String fileName, int accessMode)993     public @NonNull InputStream openNonAsset(@NonNull String fileName, int accessMode)
994             throws IOException {
995         return openNonAsset(0, fileName, accessMode);
996     }
997 
998     /**
999      * Open a non-asset in a specified package.  Not for use by applications.
1000      *
1001      * @param cookie Identifier of the package to be opened.
1002      * @param fileName Name of the asset to retrieve.
1003      * @hide
1004      */
1005     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
openNonAsset(int cookie, @NonNull String fileName)1006     public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName)
1007             throws IOException {
1008         return openNonAsset(cookie, fileName, ACCESS_STREAMING);
1009     }
1010 
1011     /**
1012      * Open a non-asset in a specified package.  Not for use by applications.
1013      *
1014      * @param cookie Identifier of the package to be opened.
1015      * @param fileName Name of the asset to retrieve.
1016      * @param accessMode Desired access mode for retrieving the data.
1017      * @hide
1018      */
1019     @UnsupportedAppUsage
openNonAsset(int cookie, @NonNull String fileName, int accessMode)1020     public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName, int accessMode)
1021             throws IOException {
1022         Objects.requireNonNull(fileName, "fileName");
1023         synchronized (this) {
1024             ensureOpenLocked();
1025             final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode);
1026             if (asset == 0) {
1027                 throw new FileNotFoundException("Asset absolute file: " + fileName);
1028             }
1029             final AssetInputStream assetInputStream = new AssetInputStream(asset);
1030             incRefsLocked(assetInputStream.hashCode());
1031             return assetInputStream;
1032         }
1033     }
1034 
1035     /**
1036      * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
1037      * This provides direct access to all of the files included in an application
1038      * package (not only its assets).  Applications should not normally use this.
1039      *
1040      * The asset must not be compressed, or an exception will be thrown.
1041      *
1042      * @param fileName Name of the asset to retrieve.
1043      */
openNonAssetFd(@onNull String fileName)1044     public @NonNull AssetFileDescriptor openNonAssetFd(@NonNull String fileName)
1045             throws IOException {
1046         return openNonAssetFd(0, fileName);
1047     }
1048 
1049     /**
1050      * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
1051      * This provides direct access to all of the files included in an application
1052      * package (not only its assets).  Applications should not normally use this.
1053      *
1054      * The asset must not be compressed, or an exception will be thrown.
1055      *
1056      * @param cookie Identifier of the package to be opened.
1057      * @param fileName Name of the asset to retrieve.
1058      */
openNonAssetFd(int cookie, @NonNull String fileName)1059     public @NonNull AssetFileDescriptor openNonAssetFd(int cookie, @NonNull String fileName)
1060             throws IOException {
1061         Objects.requireNonNull(fileName, "fileName");
1062         synchronized (this) {
1063             ensureOpenLocked();
1064             final ParcelFileDescriptor pfd =
1065                     nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets);
1066             if (pfd == null) {
1067                 throw new FileNotFoundException("Asset absolute file: " + fileName);
1068             }
1069             return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
1070         }
1071     }
1072 
1073     /**
1074      * Retrieve a parser for a compiled XML file.
1075      *
1076      * @param fileName The name of the file to retrieve.
1077      */
openXmlResourceParser(@onNull String fileName)1078     public @NonNull XmlResourceParser openXmlResourceParser(@NonNull String fileName)
1079             throws IOException {
1080         return openXmlResourceParser(0, fileName);
1081     }
1082 
1083     /**
1084      * Retrieve a parser for a compiled XML file.
1085      *
1086      * @param cookie Identifier of the package to be opened.
1087      * @param fileName The name of the file to retrieve.
1088      */
openXmlResourceParser(int cookie, @NonNull String fileName)1089     public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName)
1090             throws IOException {
1091         try (XmlBlock block = openXmlBlockAsset(cookie, fileName)) {
1092             XmlResourceParser parser = block.newParser();
1093             // If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with
1094             // a valid native pointer, which makes newParser always return non-null. But let's
1095             // be careful.
1096             if (parser == null) {
1097                 throw new AssertionError("block.newParser() returned a null parser");
1098             }
1099             return parser;
1100         }
1101     }
1102 
1103     /**
1104      * Retrieve a non-asset as a compiled XML file.  Not for use by applications.
1105      *
1106      * @param fileName The name of the file to retrieve.
1107      * @hide
1108      */
openXmlBlockAsset(@onNull String fileName)1109     @NonNull XmlBlock openXmlBlockAsset(@NonNull String fileName) throws IOException {
1110         return openXmlBlockAsset(0, fileName);
1111     }
1112 
1113     /**
1114      * Retrieve a non-asset as a compiled XML file.  Not for use by
1115      * applications.
1116      *
1117      * @param cookie Identifier of the package to be opened.
1118      * @param fileName Name of the asset to retrieve.
1119      * @hide
1120      */
openXmlBlockAsset(int cookie, @NonNull String fileName)1121     @NonNull XmlBlock openXmlBlockAsset(int cookie, @NonNull String fileName) throws IOException {
1122         Objects.requireNonNull(fileName, "fileName");
1123         synchronized (this) {
1124             ensureOpenLocked();
1125 
1126             final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
1127             if (xmlBlock == 0) {
1128                 throw new FileNotFoundException("Asset XML file: " + fileName);
1129             }
1130             final XmlBlock block = new XmlBlock(this, xmlBlock);
1131             incRefsLocked(block.hashCode());
1132             return block;
1133         }
1134     }
1135 
xmlBlockGone(int id)1136     void xmlBlockGone(int id) {
1137         synchronized (this) {
1138             decRefsLocked(id);
1139         }
1140     }
1141 
1142     @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)1143     void applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
1144             @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress,
1145             long outIndicesAddress) {
1146         Objects.requireNonNull(inAttrs, "inAttrs");
1147         synchronized (this) {
1148             // Need to synchronize on AssetManager because we will be accessing
1149             // the native implementation of AssetManager.
1150             ensureValidLocked();
1151             nativeApplyStyle(mObject, themePtr, defStyleAttr, defStyleRes,
1152                     parser != null ? parser.mParseState : 0, inAttrs, outValuesAddress,
1153                     outIndicesAddress);
1154         }
1155     }
1156 
getAttributeResolutionStack(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @StyleRes int xmlStyle)1157     int[] getAttributeResolutionStack(long themePtr, @AttrRes int defStyleAttr,
1158             @StyleRes int defStyleRes, @StyleRes int xmlStyle) {
1159         synchronized (this) {
1160             ensureValidLocked();
1161             return nativeAttributeResolutionStack(
1162                     mObject, themePtr, xmlStyle, defStyleAttr, defStyleRes);
1163         }
1164     }
1165 
1166     @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)1167     boolean resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
1168             @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues,
1169             @NonNull int[] outIndices) {
1170         Objects.requireNonNull(inAttrs, "inAttrs");
1171         Objects.requireNonNull(outValues, "outValues");
1172         Objects.requireNonNull(outIndices, "outIndices");
1173         synchronized (this) {
1174             // Need to synchronize on AssetManager because we will be accessing
1175             // the native implementation of AssetManager.
1176             ensureValidLocked();
1177             return nativeResolveAttrs(mObject,
1178                     themePtr, defStyleAttr, defStyleRes, inValues, inAttrs, outValues, outIndices);
1179         }
1180     }
1181 
1182     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
retrieveAttributes(@onNull XmlBlock.Parser parser, @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices)1183     boolean retrieveAttributes(@NonNull XmlBlock.Parser parser, @NonNull int[] inAttrs,
1184             @NonNull int[] outValues, @NonNull int[] outIndices) {
1185         Objects.requireNonNull(parser, "parser");
1186         Objects.requireNonNull(inAttrs, "inAttrs");
1187         Objects.requireNonNull(outValues, "outValues");
1188         Objects.requireNonNull(outIndices, "outIndices");
1189         synchronized (this) {
1190             // Need to synchronize on AssetManager because we will be accessing
1191             // the native implementation of AssetManager.
1192             ensureValidLocked();
1193             return nativeRetrieveAttributes(
1194                     mObject, parser.mParseState, inAttrs, outValues, outIndices);
1195         }
1196     }
1197 
1198     @UnsupportedAppUsage
createTheme()1199     long createTheme() {
1200         synchronized (this) {
1201             ensureValidLocked();
1202             long themePtr = nativeThemeCreate(mObject);
1203             incRefsLocked(themePtr);
1204             return themePtr;
1205         }
1206     }
1207 
releaseTheme(long themePtr)1208     void releaseTheme(long themePtr) {
1209         synchronized (this) {
1210             decRefsLocked(themePtr);
1211         }
1212     }
1213 
getThemeFreeFunction()1214     static long getThemeFreeFunction() {
1215         return nativeGetThemeFreeFunction();
1216     }
1217 
applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force)1218     void applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force) {
1219         synchronized (this) {
1220             // Need to synchronize on AssetManager because we will be accessing
1221             // the native implementation of AssetManager.
1222             ensureValidLocked();
1223             nativeThemeApplyStyle(mObject, themePtr, resId, force);
1224         }
1225     }
1226 
rebaseTheme(long themePtr, @NonNull AssetManager newAssetManager, @StyleRes int[] styleIds, @StyleRes boolean[] force, int count)1227     AssetManager rebaseTheme(long themePtr, @NonNull AssetManager newAssetManager,
1228             @StyleRes int[] styleIds, @StyleRes boolean[] force, int count) {
1229         // Exchange ownership of the theme with the new asset manager.
1230         if (this != newAssetManager) {
1231             synchronized (this) {
1232                 ensureValidLocked();
1233                 decRefsLocked(themePtr);
1234             }
1235             synchronized (newAssetManager) {
1236                 newAssetManager.ensureValidLocked();
1237                 newAssetManager.incRefsLocked(themePtr);
1238             }
1239         }
1240 
1241         try {
1242             synchronized (newAssetManager) {
1243                 newAssetManager.ensureValidLocked();
1244                 nativeThemeRebase(newAssetManager.mObject, themePtr, styleIds, force, count);
1245             }
1246         } finally {
1247             Reference.reachabilityFence(newAssetManager);
1248         }
1249         return newAssetManager;
1250     }
1251 
1252     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
setThemeTo(long dstThemePtr, @NonNull AssetManager srcAssetManager, long srcThemePtr)1253     void setThemeTo(long dstThemePtr, @NonNull AssetManager srcAssetManager, long srcThemePtr) {
1254         synchronized (this) {
1255             ensureValidLocked();
1256             synchronized (srcAssetManager) {
1257                 srcAssetManager.ensureValidLocked();
1258                 nativeThemeCopy(mObject, dstThemePtr, srcAssetManager.mObject, srcThemePtr);
1259             }
1260         }
1261     }
1262 
1263     @Override
finalize()1264     protected void finalize() throws Throwable {
1265         if (DEBUG_REFS && mNumRefs != 0) {
1266             Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs);
1267             if (mRefStacks != null) {
1268                 for (RuntimeException e : mRefStacks.values()) {
1269                     Log.w(TAG, "Reference from here", e);
1270                 }
1271             }
1272         }
1273 
1274         synchronized (this) {
1275             if (mObject != 0) {
1276                 nativeDestroy(mObject);
1277                 mObject = 0;
1278             }
1279         }
1280     }
1281 
1282     /* No Locking is needed for AssetInputStream because an AssetInputStream is not-thread
1283     safe and it does not rely on AssetManager once it has been created. It completely owns the
1284     underlying Asset. */
1285     public final class AssetInputStream extends InputStream {
1286         private long mAssetNativePtr;
1287         private long mLength;
1288         private long mMarkPos;
1289 
1290         /**
1291          * @hide
1292          */
1293         @UnsupportedAppUsage
getAssetInt()1294         public final int getAssetInt() {
1295             throw new UnsupportedOperationException();
1296         }
1297 
1298         /**
1299          * @hide
1300          */
1301         @UnsupportedAppUsage
getNativeAsset()1302         public final long getNativeAsset() {
1303             return mAssetNativePtr;
1304         }
1305 
AssetInputStream(long assetNativePtr)1306         private AssetInputStream(long assetNativePtr) {
1307             mAssetNativePtr = assetNativePtr;
1308             mLength = nativeAssetGetLength(assetNativePtr);
1309         }
1310 
1311         @Override
read()1312         public final int read() throws IOException {
1313             ensureOpen();
1314             return nativeAssetReadChar(mAssetNativePtr);
1315         }
1316 
1317         @Override
read(@onNull byte[] b)1318         public final int read(@NonNull byte[] b) throws IOException {
1319             ensureOpen();
1320             Objects.requireNonNull(b, "b");
1321             return nativeAssetRead(mAssetNativePtr, b, 0, b.length);
1322         }
1323 
1324         @Override
read(@onNull byte[] b, int off, int len)1325         public final int read(@NonNull byte[] b, int off, int len) throws IOException {
1326             ensureOpen();
1327             Objects.requireNonNull(b, "b");
1328             return nativeAssetRead(mAssetNativePtr, b, off, len);
1329         }
1330 
1331         @Override
skip(long n)1332         public final long skip(long n) throws IOException {
1333             ensureOpen();
1334             long pos = nativeAssetSeek(mAssetNativePtr, 0, 0);
1335             if ((pos + n) > mLength) {
1336                 n = mLength - pos;
1337             }
1338             if (n > 0) {
1339                 nativeAssetSeek(mAssetNativePtr, n, 0);
1340             }
1341             return n;
1342         }
1343 
1344         @Override
available()1345         public final int available() throws IOException {
1346             ensureOpen();
1347             final long len = nativeAssetGetRemainingLength(mAssetNativePtr);
1348             return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len;
1349         }
1350 
1351         @Override
markSupported()1352         public final boolean markSupported() {
1353             return true;
1354         }
1355 
1356         @Override
mark(int readlimit)1357         public final void mark(int readlimit) {
1358             ensureOpen();
1359             mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0);
1360         }
1361 
1362         @Override
reset()1363         public final void reset() throws IOException {
1364             ensureOpen();
1365             nativeAssetSeek(mAssetNativePtr, mMarkPos, -1);
1366         }
1367 
1368         @Override
close()1369         public final void close() throws IOException {
1370             if (mAssetNativePtr != 0) {
1371                 nativeAssetDestroy(mAssetNativePtr);
1372                 mAssetNativePtr = 0;
1373 
1374                 synchronized (AssetManager.this) {
1375                     decRefsLocked(hashCode());
1376                 }
1377             }
1378         }
1379 
1380         @Override
finalize()1381         protected void finalize() throws Throwable {
1382             close();
1383         }
1384 
ensureOpen()1385         private void ensureOpen() {
1386             if (mAssetNativePtr == 0) {
1387                 throw new IllegalStateException("AssetInputStream is closed");
1388             }
1389         }
1390     }
1391 
1392     /**
1393      * Determine whether the state in this asset manager is up-to-date with
1394      * the files on the filesystem.  If false is returned, you need to
1395      * instantiate a new AssetManager class to see the new data.
1396      * @hide
1397      */
1398     @UnsupportedAppUsage
isUpToDate()1399     public boolean isUpToDate() {
1400         synchronized (this) {
1401             if (!mOpen) {
1402                 return false;
1403             }
1404 
1405             for (ApkAssets apkAssets : mApkAssets) {
1406                 if (!apkAssets.isUpToDate()) {
1407                     return false;
1408                 }
1409             }
1410 
1411             return true;
1412         }
1413     }
1414 
1415     /**
1416      * Get the locales that this asset manager contains data for.
1417      *
1418      * <p>On SDK 21 (Android 5.0: Lollipop) and above, Locale strings are valid
1419      * <a href="https://tools.ietf.org/html/bcp47">BCP-47</a> language tags and can be
1420      * parsed using {@link Locale#forLanguageTag(String)}.
1421      *
1422      * <p>On SDK 20 (Android 4.4W: Kitkat for watches) and below, locale strings
1423      * are of the form {@code ll_CC} where {@code ll} is a two letter language code,
1424      * and {@code CC} is a two letter country code.
1425      */
getLocales()1426     public String[] getLocales() {
1427         synchronized (this) {
1428             ensureValidLocked();
1429             return nativeGetLocales(mObject, false /*excludeSystem*/);
1430         }
1431     }
1432 
1433     /**
1434      * Same as getLocales(), except that locales that are only provided by the system (i.e. those
1435      * present in framework-res.apk or its overlays) will not be listed.
1436      *
1437      * For example, if the "system" assets support English, French, and German, and the additional
1438      * assets support Cherokee and French, getLocales() would return
1439      * [Cherokee, English, French, German], while getNonSystemLocales() would return
1440      * [Cherokee, French].
1441      * @hide
1442      */
getNonSystemLocales()1443     public String[] getNonSystemLocales() {
1444         synchronized (this) {
1445             ensureValidLocked();
1446             return nativeGetLocales(mObject, true /*excludeSystem*/);
1447         }
1448     }
1449 
1450     /**
1451      * @hide
1452      */
getSizeConfigurations()1453     Configuration[] getSizeConfigurations() {
1454         synchronized (this) {
1455             ensureValidLocked();
1456             return nativeGetSizeConfigurations(mObject);
1457         }
1458     }
1459 
1460     /**
1461      * @hide
1462      */
getSizeAndUiModeConfigurations()1463     Configuration[] getSizeAndUiModeConfigurations() {
1464         synchronized (this) {
1465             ensureValidLocked();
1466             return nativeGetSizeAndUiModeConfigurations(mObject);
1467         }
1468     }
1469 
1470     /**
1471      * Change the configuration used when retrieving resources.  Not for use by
1472      * applications.
1473      * @hide
1474      */
1475     @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 grammaticalGender, int majorVersion)1476     public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation,
1477             int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
1478             int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
1479             int screenHeightDp, int screenLayout, int uiMode, int colorMode, int grammaticalGender,
1480             int majorVersion) {
1481         synchronized (this) {
1482             ensureValidLocked();
1483             nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density,
1484                     keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
1485                     smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
1486                     colorMode, grammaticalGender, majorVersion);
1487         }
1488     }
1489 
1490     /**
1491      * @hide
1492      */
1493     @UnsupportedAppUsage
getAssignedPackageIdentifiers()1494     public SparseArray<String> getAssignedPackageIdentifiers() {
1495         return getAssignedPackageIdentifiers(true, true);
1496     }
1497 
1498     /**
1499      * @hide
1500      */
getAssignedPackageIdentifiers(boolean includeOverlays, boolean includeLoaders)1501     public SparseArray<String> getAssignedPackageIdentifiers(boolean includeOverlays,
1502             boolean includeLoaders) {
1503         synchronized (this) {
1504             ensureValidLocked();
1505             return nativeGetAssignedPackageIdentifiers(mObject, includeOverlays, includeLoaders);
1506         }
1507     }
1508 
1509     /**
1510      * @hide
1511      */
1512     @GuardedBy("this")
getOverlayableMap(String packageName)1513     public @Nullable Map<String, String> getOverlayableMap(String packageName) {
1514         synchronized (this) {
1515             ensureValidLocked();
1516             return nativeGetOverlayableMap(mObject, packageName);
1517         }
1518     }
1519 
1520     /**
1521      * @hide
1522      */
1523     @TestApi
1524     @GuardedBy("this")
getOverlayablesToString(String packageName)1525     public @Nullable String getOverlayablesToString(String packageName) {
1526         synchronized (this) {
1527             ensureValidLocked();
1528             return nativeGetOverlayablesToString(mObject, packageName);
1529         }
1530     }
1531 
1532     @GuardedBy("this")
incRefsLocked(long id)1533     private void incRefsLocked(long id) {
1534         if (DEBUG_REFS) {
1535             if (mRefStacks == null) {
1536                 mRefStacks = new HashMap<>();
1537             }
1538             RuntimeException ex = new RuntimeException();
1539             ex.fillInStackTrace();
1540             mRefStacks.put(id, ex);
1541         }
1542         mNumRefs++;
1543     }
1544 
1545     @GuardedBy("this")
decRefsLocked(long id)1546     private void decRefsLocked(long id) {
1547         if (DEBUG_REFS && mRefStacks != null) {
1548             mRefStacks.remove(id);
1549         }
1550         mNumRefs--;
1551         if (mNumRefs == 0 && mObject != 0) {
1552             nativeDestroy(mObject);
1553             mObject = 0;
1554             mApkAssets = sEmptyApkAssets;
1555         }
1556     }
1557 
dump(PrintWriter pw, String prefix)1558     synchronized void dump(PrintWriter pw, String prefix) {
1559         pw.println(prefix + "class=" + getClass());
1560         pw.println(prefix + "apkAssets=");
1561         for (int i = 0; i < mApkAssets.length; i++) {
1562             pw.println(prefix + i);
1563             mApkAssets[i].dump(pw, prefix + "  ");
1564         }
1565     }
1566 
1567     // AssetManager setup native methods.
nativeCreate()1568     private static native long nativeCreate();
nativeDestroy(long ptr)1569     private static native void nativeDestroy(long ptr);
nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, boolean invalidateCaches)1570     private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets,
1571             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 grammaticalGender, int majorVersion)1572     private static native void nativeSetConfiguration(long ptr, int mcc, int mnc,
1573             @Nullable String locale, int orientation, int touchscreen, int density, int keyboard,
1574             int keyboardHidden, int navigation, int screenWidth, int screenHeight,
1575             int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout,
1576             int uiMode, int colorMode, int grammaticalGender, int majorVersion);
nativeGetAssignedPackageIdentifiers( long ptr, boolean includeOverlays, boolean includeLoaders)1577     private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
1578             long ptr, boolean includeOverlays, boolean includeLoaders);
1579 
1580     // File native methods.
nativeContainsAllocatedTable(long ptr)1581     private static native boolean nativeContainsAllocatedTable(long ptr);
nativeList(long ptr, @NonNull String path)1582     private static native @Nullable String[] nativeList(long ptr, @NonNull String path)
1583             throws IOException;
nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode)1584     private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode);
nativeOpenAssetFd(long ptr, @NonNull String fileName, long[] outOffsets)1585     private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr,
1586             @NonNull String fileName, long[] outOffsets) throws IOException;
nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, int accessMode)1587     private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName,
1588             int accessMode);
nativeOpenNonAssetFd(long ptr, int cookie, @NonNull String fileName, @NonNull long[] outOffsets)1589     private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie,
1590             @NonNull String fileName, @NonNull long[] outOffsets) throws IOException;
nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName)1591     private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName);
nativeOpenXmlAssetFd(long ptr, int cookie, @NonNull FileDescriptor fileDescriptor)1592     private static native long nativeOpenXmlAssetFd(long ptr, int cookie,
1593             @NonNull FileDescriptor fileDescriptor);
1594 
1595     // Primitive resource native methods.
nativeGetResourceValue(long ptr, @AnyRes int resId, short density, @NonNull TypedValue outValue, boolean resolveReferences)1596     private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density,
1597             @NonNull TypedValue outValue, boolean resolveReferences);
nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, @NonNull TypedValue outValue)1598     private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId,
1599             @NonNull TypedValue outValue);
1600 
nativeGetStyleAttributes(long ptr, @StyleRes int resId)1601     private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr,
1602             @StyleRes int resId);
nativeGetResourceStringArray(long ptr, @ArrayRes int resId)1603     private static native @Nullable String[] nativeGetResourceStringArray(long ptr,
1604             @ArrayRes int resId);
nativeGetResourceStringArrayInfo(long ptr, @ArrayRes int resId)1605     private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr,
1606             @ArrayRes int resId);
nativeGetResourceIntArray(long ptr, @ArrayRes int resId)1607     private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId);
nativeGetResourceArraySize(long ptr, @ArrayRes int resId)1608     private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId);
nativeGetResourceArray(long ptr, @ArrayRes int resId, @NonNull int[] outValues)1609     private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId,
1610             @NonNull int[] outValues);
1611 
1612     // Resource name/ID native methods.
nativeGetResourceIdentifier(long ptr, @NonNull String name, @Nullable String defType, @Nullable String defPackage)1613     private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name,
1614             @Nullable String defType, @Nullable String defPackage);
nativeGetResourceName(long ptr, @AnyRes int resid)1615     private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid);
nativeGetResourcePackageName(long ptr, @AnyRes int resid)1616     private static native @Nullable String nativeGetResourcePackageName(long ptr,
1617             @AnyRes int resid);
nativeGetResourceTypeName(long ptr, @AnyRes int resid)1618     private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid);
nativeGetResourceEntryName(long ptr, @AnyRes int resid)1619     private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid);
nativeGetLocales(long ptr, boolean excludeSystem)1620     private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem);
nativeGetSizeConfigurations(long ptr)1621     private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr);
nativeGetSizeAndUiModeConfigurations(long ptr)1622     private static native @Nullable Configuration[] nativeGetSizeAndUiModeConfigurations(long ptr);
nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled)1623     private static native void nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled);
nativeGetLastResourceResolution(long ptr)1624     private static native @Nullable String nativeGetLastResourceResolution(long ptr);
1625 
1626     // Style attribute retrieval native methods.
nativeAttributeResolutionStack(long ptr, long themePtr, @StyleRes int xmlStyleRes, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)1627     private static native int[] nativeAttributeResolutionStack(long ptr, long themePtr,
1628             @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)1629     private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr,
1630             @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs,
1631             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)1632     private static native boolean nativeResolveAttrs(long ptr, long themePtr,
1633             @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues,
1634             @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
nativeRetrieveAttributes(long ptr, long xmlParserPtr, @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices)1635     private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr,
1636             @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
1637 
1638     // Theme related native methods
nativeThemeCreate(long ptr)1639     private static native long nativeThemeCreate(long ptr);
nativeGetThemeFreeFunction()1640     private static native long nativeGetThemeFreeFunction();
nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, boolean force)1641     private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId,
1642             boolean force);
nativeThemeRebase(long ptr, long themePtr, @NonNull int[] styleIds, @NonNull boolean[] force, int styleSize)1643     private static native void nativeThemeRebase(long ptr, long themePtr, @NonNull int[] styleIds,
1644             @NonNull boolean[] force, int styleSize);
nativeThemeCopy(long dstAssetManagerPtr, long dstThemePtr, long srcAssetManagerPtr, long srcThemePtr)1645     private static native void nativeThemeCopy(long dstAssetManagerPtr, long dstThemePtr,
1646             long srcAssetManagerPtr, long srcThemePtr);
nativeThemeGetAttributeValue(long ptr, long themePtr, @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve)1647     private static native int nativeThemeGetAttributeValue(long ptr, long themePtr,
1648             @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve);
nativeThemeDump(long ptr, long themePtr, int priority, String tag, String prefix)1649     private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag,
1650             String prefix);
nativeThemeGetChangingConfigurations(long themePtr)1651     static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr);
1652     @StyleRes
nativeGetParentThemeIdentifier(long ptr, @StyleRes int styleId)1653     private static native int nativeGetParentThemeIdentifier(long ptr, @StyleRes int styleId);
1654 
1655     // AssetInputStream related native methods.
nativeAssetDestroy(long assetPtr)1656     private static native void nativeAssetDestroy(long assetPtr);
nativeAssetReadChar(long assetPtr)1657     private static native int nativeAssetReadChar(long assetPtr);
nativeAssetRead(long assetPtr, byte[] b, int off, int len)1658     private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len);
nativeAssetSeek(long assetPtr, long offset, int whence)1659     private static native long nativeAssetSeek(long assetPtr, long offset, int whence);
nativeAssetGetLength(long assetPtr)1660     private static native long nativeAssetGetLength(long assetPtr);
nativeAssetGetRemainingLength(long assetPtr)1661     private static native long nativeAssetGetRemainingLength(long assetPtr);
1662 
nativeGetOverlayableMap(long ptr, @NonNull String packageName)1663     private static native @Nullable Map nativeGetOverlayableMap(long ptr,
1664             @NonNull String packageName);
nativeGetOverlayablesToString(long ptr, @NonNull String packageName)1665     private static native @Nullable String nativeGetOverlayablesToString(long ptr,
1666             @NonNull String packageName);
1667 
1668     // Global debug native methods.
1669     /**
1670      * @hide
1671      */
1672     @UnsupportedAppUsage
getGlobalAssetCount()1673     public static native int getGlobalAssetCount();
1674 
1675     /**
1676      * @hide
1677      */
getAssetAllocations()1678     public static native String getAssetAllocations();
1679 
1680     /**
1681      * @hide
1682      */
1683     @UnsupportedAppUsage
getGlobalAssetManagerCount()1684     public static native int getGlobalAssetManagerCount();
1685 }
1686