AssetManager.java revision 2ba4efff8bece69f95401768e48db1b20afd077d
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.os.ParcelFileDescriptor;
20import android.util.Log;
21import android.util.TypedValue;
22
23import java.io.FileNotFoundException;
24import java.io.IOException;
25import java.io.InputStream;
26import java.util.HashMap;
27
28/**
29 * Provides access to an application's raw asset files; see {@link Resources}
30 * for the way most applications will want to retrieve their resource data.
31 * This class presents a lower-level API that allows you to open and read raw
32 * files that have been bundled with the application as a simple stream of
33 * bytes.
34 */
35public final class AssetManager {
36    /* modes used when opening an asset */
37
38    /**
39     * Mode for {@link #open(String, int)}: no specific information about how
40     * data will be accessed.
41     */
42    public static final int ACCESS_UNKNOWN = 0;
43    /**
44     * Mode for {@link #open(String, int)}: Read chunks, and seek forward and
45     * backward.
46     */
47    public static final int ACCESS_RANDOM = 1;
48    /**
49     * Mode for {@link #open(String, int)}: Read sequentially, with an
50     * occasional forward seek.
51     */
52    public static final int ACCESS_STREAMING = 2;
53    /**
54     * Mode for {@link #open(String, int)}: Attempt to load contents into
55     * memory, for fast small reads.
56     */
57    public static final int ACCESS_BUFFER = 3;
58
59    private static final String TAG = "AssetManager";
60    private static final boolean localLOGV = false || false;
61
62    private static final boolean DEBUG_REFS = false;
63
64    private static final Object sSync = new Object();
65    /*package*/ static AssetManager sSystem = null;
66
67    private final TypedValue mValue = new TypedValue();
68    private final long[] mOffsets = new long[2];
69
70    // For communication with native code.
71    private int mObject;
72    private int mNObject;  // used by the NDK
73
74    private StringBlock mStringBlocks[] = null;
75
76    private int mNumRefs = 1;
77    private boolean mOpen = true;
78    private HashMap<Integer, RuntimeException> mRefStacks;
79
80    /**
81     * Create a new AssetManager containing only the basic system assets.
82     * Applications will not generally use this method, instead retrieving the
83     * appropriate asset manager with {@link Resources#getAssets}.    Not for
84     * use by applications.
85     * {@hide}
86     */
87    public AssetManager() {
88        synchronized (this) {
89            if (DEBUG_REFS) {
90                mNumRefs = 0;
91                incRefsLocked(this.hashCode());
92            }
93            init();
94            if (localLOGV) Log.v(TAG, "New asset manager: " + this);
95            ensureSystemAssets();
96        }
97    }
98
99    private static void ensureSystemAssets() {
100        synchronized (sSync) {
101            if (sSystem == null) {
102                AssetManager system = new AssetManager(true);
103                system.makeStringBlocks(false);
104                sSystem = system;
105            }
106        }
107    }
108
109    private AssetManager(boolean isSystem) {
110        if (DEBUG_REFS) {
111            synchronized (this) {
112                mNumRefs = 0;
113                incRefsLocked(this.hashCode());
114            }
115        }
116        init();
117        if (localLOGV) Log.v(TAG, "New asset manager: " + this);
118    }
119
120    /**
121     * Return a global shared asset manager that provides access to only
122     * system assets (no application assets).
123     * {@hide}
124     */
125    public static AssetManager getSystem() {
126        ensureSystemAssets();
127        return sSystem;
128    }
129
130    /**
131     * Close this asset manager.
132     */
133    public void close() {
134        synchronized(this) {
135            //System.out.println("Release: num=" + mNumRefs
136            //                   + ", released=" + mReleased);
137            if (mOpen) {
138                mOpen = false;
139                decRefsLocked(this.hashCode());
140            }
141        }
142    }
143
144    /**
145     * Retrieve the string value associated with a particular resource
146     * identifier for the current configuration / skin.
147     */
148    /*package*/ final CharSequence getResourceText(int ident) {
149        synchronized (this) {
150            TypedValue tmpValue = mValue;
151            int block = loadResourceValue(ident, (short) 0, tmpValue, true);
152            if (block >= 0) {
153                if (tmpValue.type == TypedValue.TYPE_STRING) {
154                    return mStringBlocks[block].get(tmpValue.data);
155                }
156                return tmpValue.coerceToString();
157            }
158        }
159        return null;
160    }
161
162    /**
163     * Retrieve the string value associated with a particular resource
164     * identifier for the current configuration / skin.
165     */
166    /*package*/ final CharSequence getResourceBagText(int ident, int bagEntryId) {
167        synchronized (this) {
168            TypedValue tmpValue = mValue;
169            int block = loadResourceBagValue(ident, bagEntryId, tmpValue, true);
170            if (block >= 0) {
171                if (tmpValue.type == TypedValue.TYPE_STRING) {
172                    return mStringBlocks[block].get(tmpValue.data);
173                }
174                return tmpValue.coerceToString();
175            }
176        }
177        return null;
178    }
179
180    /**
181     * Retrieve the string array associated with a particular resource
182     * identifier.
183     * @param id Resource id of the string array
184     */
185    /*package*/ final String[] getResourceStringArray(final int id) {
186        String[] retArray = getArrayStringResource(id);
187        return retArray;
188    }
189
190
191    /*package*/ final boolean getResourceValue(int ident,
192                                               int density,
193                                               TypedValue outValue,
194                                               boolean resolveRefs)
195    {
196        int block = loadResourceValue(ident, (short) density, outValue, resolveRefs);
197        if (block >= 0) {
198            if (outValue.type != TypedValue.TYPE_STRING) {
199                return true;
200            }
201            outValue.string = mStringBlocks[block].get(outValue.data);
202            return true;
203        }
204        return false;
205    }
206
207    /**
208     * Retrieve the text array associated with a particular resource
209     * identifier.
210     * @param id Resource id of the string array
211     */
212    /*package*/ final CharSequence[] getResourceTextArray(final int id) {
213        int[] rawInfoArray = getArrayStringInfo(id);
214        int rawInfoArrayLen = rawInfoArray.length;
215        final int infoArrayLen = rawInfoArrayLen / 2;
216        int block;
217        int index;
218        CharSequence[] retArray = new CharSequence[infoArrayLen];
219        for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) {
220            block = rawInfoArray[i];
221            index = rawInfoArray[i + 1];
222            retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null;
223        }
224        return retArray;
225    }
226
227    /*package*/ final boolean getThemeValue(int theme, int ident,
228            TypedValue outValue, boolean resolveRefs) {
229        int block = loadThemeAttributeValue(theme, ident, outValue, resolveRefs);
230        if (block >= 0) {
231            if (outValue.type != TypedValue.TYPE_STRING) {
232                return true;
233            }
234            StringBlock[] blocks = mStringBlocks;
235            if (blocks == null) {
236                ensureStringBlocks();
237                blocks = mStringBlocks;
238            }
239            outValue.string = blocks[block].get(outValue.data);
240            return true;
241        }
242        return false;
243    }
244
245    /*package*/ final void ensureStringBlocks() {
246        if (mStringBlocks == null) {
247            synchronized (this) {
248                if (mStringBlocks == null) {
249                    makeStringBlocks(true);
250                }
251            }
252        }
253    }
254
255    /*package*/ final void makeStringBlocks(boolean copyFromSystem) {
256        final int sysNum = copyFromSystem ? sSystem.mStringBlocks.length : 0;
257        final int num = getStringBlockCount();
258        mStringBlocks = new StringBlock[num];
259        if (localLOGV) Log.v(TAG, "Making string blocks for " + this
260                + ": " + num);
261        for (int i=0; i<num; i++) {
262            if (i < sysNum) {
263                mStringBlocks[i] = sSystem.mStringBlocks[i];
264            } else {
265                mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true);
266            }
267        }
268    }
269
270    /*package*/ final CharSequence getPooledString(int block, int id) {
271        //System.out.println("Get pooled: block=" + block
272        //                   + ", id=#" + Integer.toHexString(id)
273        //                   + ", blocks=" + mStringBlocks);
274        return mStringBlocks[block-1].get(id);
275    }
276
277    /**
278     * Open an asset using ACCESS_STREAMING mode.  This provides access to
279     * files that have been bundled with an application as assets -- that is,
280     * files placed in to the "assets" directory.
281     *
282     * @param fileName The name of the asset to open.  This name can be
283     *                 hierarchical.
284     *
285     * @see #open(String, int)
286     * @see #list
287     */
288    public final InputStream open(String fileName) throws IOException {
289        return open(fileName, ACCESS_STREAMING);
290    }
291
292    /**
293     * Open an asset using an explicit access mode, returning an InputStream to
294     * read its contents.  This provides access to files that have been bundled
295     * with an application as assets -- that is, files placed in to the
296     * "assets" directory.
297     *
298     * @param fileName The name of the asset to open.  This name can be
299     *                 hierarchical.
300     * @param accessMode Desired access mode for retrieving the data.
301     *
302     * @see #ACCESS_UNKNOWN
303     * @see #ACCESS_STREAMING
304     * @see #ACCESS_RANDOM
305     * @see #ACCESS_BUFFER
306     * @see #open(String)
307     * @see #list
308     */
309    public final InputStream open(String fileName, int accessMode)
310        throws IOException {
311        synchronized (this) {
312            if (!mOpen) {
313                throw new RuntimeException("Assetmanager has been closed");
314            }
315            int asset = openAsset(fileName, accessMode);
316            if (asset != 0) {
317                AssetInputStream res = new AssetInputStream(asset);
318                incRefsLocked(res.hashCode());
319                return res;
320            }
321        }
322        throw new FileNotFoundException("Asset file: " + fileName);
323    }
324
325    public final AssetFileDescriptor openFd(String fileName)
326            throws IOException {
327        synchronized (this) {
328            if (!mOpen) {
329                throw new RuntimeException("Assetmanager has been closed");
330            }
331            ParcelFileDescriptor pfd = openAssetFd(fileName, mOffsets);
332            if (pfd != null) {
333                return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
334            }
335        }
336        throw new FileNotFoundException("Asset file: " + fileName);
337    }
338
339    /**
340     * Return a String array of all the assets at the given path.
341     *
342     * @param path A relative path within the assets, i.e., "docs/home.html".
343     *
344     * @return String[] Array of strings, one for each asset.  These file
345     *         names are relative to 'path'.  You can open the file by
346     *         concatenating 'path' and a name in the returned string (via
347     *         File) and passing that to open().
348     *
349     * @see #open
350     */
351    public native final String[] list(String path)
352        throws IOException;
353
354    /**
355     * {@hide}
356     * Open a non-asset file as an asset using ACCESS_STREAMING mode.  This
357     * provides direct access to all of the files included in an application
358     * package (not only its assets).  Applications should not normally use
359     * this.
360     *
361     * @see #open(String)
362     */
363    public final InputStream openNonAsset(String fileName) throws IOException {
364        return openNonAsset(0, fileName, ACCESS_STREAMING);
365    }
366
367    /**
368     * {@hide}
369     * Open a non-asset file as an asset using a specific access mode.  This
370     * provides direct access to all of the files included in an application
371     * package (not only its assets).  Applications should not normally use
372     * this.
373     *
374     * @see #open(String, int)
375     */
376    public final InputStream openNonAsset(String fileName, int accessMode)
377        throws IOException {
378        return openNonAsset(0, fileName, accessMode);
379    }
380
381    /**
382     * {@hide}
383     * Open a non-asset in a specified package.  Not for use by applications.
384     *
385     * @param cookie Identifier of the package to be opened.
386     * @param fileName Name of the asset to retrieve.
387     */
388    public final InputStream openNonAsset(int cookie, String fileName)
389        throws IOException {
390        return openNonAsset(cookie, fileName, ACCESS_STREAMING);
391    }
392
393    /**
394     * {@hide}
395     * Open a non-asset in a specified package.  Not for use by applications.
396     *
397     * @param cookie Identifier of the package to be opened.
398     * @param fileName Name of the asset to retrieve.
399     * @param accessMode Desired access mode for retrieving the data.
400     */
401    public final InputStream openNonAsset(int cookie, String fileName, int accessMode)
402        throws IOException {
403        synchronized (this) {
404            if (!mOpen) {
405                throw new RuntimeException("Assetmanager has been closed");
406            }
407            int asset = openNonAssetNative(cookie, fileName, accessMode);
408            if (asset != 0) {
409                AssetInputStream res = new AssetInputStream(asset);
410                incRefsLocked(res.hashCode());
411                return res;
412            }
413        }
414        throw new FileNotFoundException("Asset absolute file: " + fileName);
415    }
416
417    public final AssetFileDescriptor openNonAssetFd(String fileName)
418            throws IOException {
419        return openNonAssetFd(0, fileName);
420    }
421
422    public final AssetFileDescriptor openNonAssetFd(int cookie,
423            String fileName) throws IOException {
424        synchronized (this) {
425            if (!mOpen) {
426                throw new RuntimeException("Assetmanager has been closed");
427            }
428            ParcelFileDescriptor pfd = openNonAssetFdNative(cookie,
429                    fileName, mOffsets);
430            if (pfd != null) {
431                return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
432            }
433        }
434        throw new FileNotFoundException("Asset absolute file: " + fileName);
435    }
436
437    /**
438     * Retrieve a parser for a compiled XML file.
439     *
440     * @param fileName The name of the file to retrieve.
441     */
442    public final XmlResourceParser openXmlResourceParser(String fileName)
443            throws IOException {
444        return openXmlResourceParser(0, fileName);
445    }
446
447    /**
448     * Retrieve a parser for a compiled XML file.
449     *
450     * @param cookie Identifier of the package to be opened.
451     * @param fileName The name of the file to retrieve.
452     */
453    public final XmlResourceParser openXmlResourceParser(int cookie,
454            String fileName) throws IOException {
455        XmlBlock block = openXmlBlockAsset(cookie, fileName);
456        XmlResourceParser rp = block.newParser();
457        block.close();
458        return rp;
459    }
460
461    /**
462     * {@hide}
463     * Retrieve a non-asset as a compiled XML file.  Not for use by
464     * applications.
465     *
466     * @param fileName The name of the file to retrieve.
467     */
468    /*package*/ final XmlBlock openXmlBlockAsset(String fileName)
469            throws IOException {
470        return openXmlBlockAsset(0, fileName);
471    }
472
473    /**
474     * {@hide}
475     * Retrieve a non-asset as a compiled XML file.  Not for use by
476     * applications.
477     *
478     * @param cookie Identifier of the package to be opened.
479     * @param fileName Name of the asset to retrieve.
480     */
481    /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName)
482        throws IOException {
483        synchronized (this) {
484            if (!mOpen) {
485                throw new RuntimeException("Assetmanager has been closed");
486            }
487            int xmlBlock = openXmlAssetNative(cookie, fileName);
488            if (xmlBlock != 0) {
489                XmlBlock res = new XmlBlock(this, xmlBlock);
490                incRefsLocked(res.hashCode());
491                return res;
492            }
493        }
494        throw new FileNotFoundException("Asset XML file: " + fileName);
495    }
496
497    /*package*/ void xmlBlockGone(int id) {
498        synchronized (this) {
499            decRefsLocked(id);
500        }
501    }
502
503    /*package*/ final int createTheme() {
504        synchronized (this) {
505            if (!mOpen) {
506                throw new RuntimeException("Assetmanager has been closed");
507            }
508            int res = newTheme();
509            incRefsLocked(res);
510            return res;
511        }
512    }
513
514    /*package*/ final void releaseTheme(int theme) {
515        synchronized (this) {
516            deleteTheme(theme);
517            decRefsLocked(theme);
518        }
519    }
520
521    protected void finalize() throws Throwable {
522        try {
523            if (DEBUG_REFS && mNumRefs != 0) {
524                Log.w(TAG, "AssetManager " + this
525                        + " finalized with non-zero refs: " + mNumRefs);
526                if (mRefStacks != null) {
527                    for (RuntimeException e : mRefStacks.values()) {
528                        Log.w(TAG, "Reference from here", e);
529                    }
530                }
531            }
532            destroy();
533        } finally {
534            super.finalize();
535        }
536    }
537
538    public final class AssetInputStream extends InputStream {
539        /**
540         * @hide
541         */
542        public final int getAssetInt() {
543            return mAsset;
544        }
545        /**
546         * @hide
547         */
548        public final long getNativeAsset() {
549            return mAsset;
550        }
551        private AssetInputStream(int asset)
552        {
553            mAsset = asset;
554            mLength = getAssetLength(asset);
555        }
556        public final int read() throws IOException {
557            return readAssetChar(mAsset);
558        }
559        public final boolean markSupported() {
560            return true;
561        }
562        public final int available() throws IOException {
563            long len = getAssetRemainingLength(mAsset);
564            return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len;
565        }
566        public final void close() throws IOException {
567            synchronized (AssetManager.this) {
568                if (mAsset != 0) {
569                    destroyAsset(mAsset);
570                    mAsset = 0;
571                    decRefsLocked(hashCode());
572                }
573            }
574        }
575        public final void mark(int readlimit) {
576            mMarkPos = seekAsset(mAsset, 0, 0);
577        }
578        public final void reset() throws IOException {
579            seekAsset(mAsset, mMarkPos, -1);
580        }
581        public final int read(byte[] b) throws IOException {
582            return readAsset(mAsset, b, 0, b.length);
583        }
584        public final int read(byte[] b, int off, int len) throws IOException {
585            return readAsset(mAsset, b, off, len);
586        }
587        public final long skip(long n) throws IOException {
588            long pos = seekAsset(mAsset, 0, 0);
589            if ((pos+n) > mLength) {
590                n = mLength-pos;
591            }
592            if (n > 0) {
593                seekAsset(mAsset, n, 0);
594            }
595            return n;
596        }
597
598        protected void finalize() throws Throwable
599        {
600            close();
601        }
602
603        private int mAsset;
604        private long mLength;
605        private long mMarkPos;
606    }
607
608    /**
609     * Add an additional set of assets to the asset manager.  This can be
610     * either a directory or ZIP file.  Not for use by applications.  Returns
611     * the cookie of the added asset, or 0 on failure.
612     * {@hide}
613     */
614    public final int addAssetPath(String path) {
615        int res = addAssetPathNative(path);
616        return res;
617    }
618
619    private native final int addAssetPathNative(String path);
620
621    /**
622     * Add multiple sets of assets to the asset manager at once.  See
623     * {@link #addAssetPath(String)} for more information.  Returns array of
624     * cookies for each added asset with 0 indicating failure, or null if
625     * the input array of paths is null.
626     * {@hide}
627     */
628    public final int[] addAssetPaths(String[] paths) {
629        if (paths == null) {
630            return null;
631        }
632
633        int[] cookies = new int[paths.length];
634        for (int i = 0; i < paths.length; i++) {
635            cookies[i] = addAssetPath(paths[i]);
636        }
637
638        return cookies;
639    }
640
641    /**
642     * Determine whether the state in this asset manager is up-to-date with
643     * the files on the filesystem.  If false is returned, you need to
644     * instantiate a new AssetManager class to see the new data.
645     * {@hide}
646     */
647    public native final boolean isUpToDate();
648
649    /**
650     * Change the locale being used by this asset manager.  Not for use by
651     * applications.
652     * {@hide}
653     */
654    public native final void setLocale(String locale);
655
656    /**
657     * Get the locales that this asset manager contains data for.
658     */
659    public native final String[] getLocales();
660
661    /**
662     * Change the configuation used when retrieving resources.  Not for use by
663     * applications.
664     * {@hide}
665     */
666    public native final void setConfiguration(int mcc, int mnc, String locale,
667            int orientation, int touchscreen, int density, int keyboard,
668            int keyboardHidden, int navigation, int screenWidth, int screenHeight,
669            int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp,
670            int screenLayout, int uiMode, int majorVersion);
671
672    /**
673     * Retrieve the resource identifier for the given resource name.
674     */
675    /*package*/ native final int getResourceIdentifier(String type,
676                                                       String name,
677                                                       String defPackage);
678
679    /*package*/ native final String getResourceName(int resid);
680    /*package*/ native final String getResourcePackageName(int resid);
681    /*package*/ native final String getResourceTypeName(int resid);
682    /*package*/ native final String getResourceEntryName(int resid);
683
684    private native final int openAsset(String fileName, int accessMode);
685    private final native ParcelFileDescriptor openAssetFd(String fileName,
686            long[] outOffsets) throws IOException;
687    private native final int openNonAssetNative(int cookie, String fileName,
688            int accessMode);
689    private native ParcelFileDescriptor openNonAssetFdNative(int cookie,
690            String fileName, long[] outOffsets) throws IOException;
691    private native final void destroyAsset(int asset);
692    private native final int readAssetChar(int asset);
693    private native final int readAsset(int asset, byte[] b, int off, int len);
694    private native final long seekAsset(int asset, long offset, int whence);
695    private native final long getAssetLength(int asset);
696    private native final long getAssetRemainingLength(int asset);
697
698    /** Returns true if the resource was found, filling in mRetStringBlock and
699     *  mRetData. */
700    private native final int loadResourceValue(int ident, short density, TypedValue outValue,
701            boolean resolve);
702    /** Returns true if the resource was found, filling in mRetStringBlock and
703     *  mRetData. */
704    private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue,
705                                               boolean resolve);
706    /*package*/ static final int STYLE_NUM_ENTRIES = 6;
707    /*package*/ static final int STYLE_TYPE = 0;
708    /*package*/ static final int STYLE_DATA = 1;
709    /*package*/ static final int STYLE_ASSET_COOKIE = 2;
710    /*package*/ static final int STYLE_RESOURCE_ID = 3;
711    /*package*/ static final int STYLE_CHANGING_CONFIGURATIONS = 4;
712    /*package*/ static final int STYLE_DENSITY = 5;
713    /*package*/ native static final boolean applyStyle(int theme,
714            int defStyleAttr, int defStyleRes, int xmlParser,
715            int[] inAttrs, int[] outValues, int[] outIndices);
716    /*package*/ native final boolean retrieveAttributes(
717            int xmlParser, int[] inAttrs, int[] outValues, int[] outIndices);
718    /*package*/ native final int getArraySize(int resource);
719    /*package*/ native final int retrieveArray(int resource, int[] outValues);
720    private native final int getStringBlockCount();
721    private native final int getNativeStringBlock(int block);
722
723    /**
724     * {@hide}
725     */
726    public native final String getCookieName(int cookie);
727
728    /**
729     * {@hide}
730     */
731    public native static final int getGlobalAssetCount();
732
733    /**
734     * {@hide}
735     */
736    public native static final String getAssetAllocations();
737
738    /**
739     * {@hide}
740     */
741    public native static final int getGlobalAssetManagerCount();
742
743    private native final int newTheme();
744    private native final void deleteTheme(int theme);
745    /*package*/ native static final void applyThemeStyle(int theme, int styleRes, boolean force);
746    /*package*/ native static final void copyTheme(int dest, int source);
747    /*package*/ native static final int loadThemeAttributeValue(int theme, int ident,
748                                                                TypedValue outValue,
749                                                                boolean resolve);
750    /*package*/ native static final void dumpTheme(int theme, int priority, String tag, String prefix);
751
752    private native final int openXmlAssetNative(int cookie, String fileName);
753
754    private native final String[] getArrayStringResource(int arrayRes);
755    private native final int[] getArrayStringInfo(int arrayRes);
756    /*package*/ native final int[] getArrayIntResource(int arrayRes);
757
758    private native final void init();
759    private native final void destroy();
760
761    private final void incRefsLocked(int id) {
762        if (DEBUG_REFS) {
763            if (mRefStacks == null) {
764                mRefStacks = new HashMap<Integer, RuntimeException>();
765                RuntimeException ex = new RuntimeException();
766                ex.fillInStackTrace();
767                mRefStacks.put(this.hashCode(), ex);
768            }
769        }
770        mNumRefs++;
771    }
772
773    private final void decRefsLocked(int id) {
774        if (DEBUG_REFS && mRefStacks != null) {
775            mRefStacks.remove(id);
776        }
777        mNumRefs--;
778        //System.out.println("Dec streams: mNumRefs=" + mNumRefs
779        //                   + " mReleased=" + mReleased);
780        if (mNumRefs == 0) {
781            destroy();
782        }
783    }
784}
785