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