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