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