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