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