/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.content.res; import android.os.ParcelFileDescriptor; import android.util.Config; import android.util.Log; import android.util.TypedValue; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Locale; /** * Provides access to an application's raw asset files; see {@link Resources} * for the way most applications will want to retrieve their resource data. * This class presents a lower-level API that allows you to open and read raw * files that have been bundled with the application as a simple stream of * bytes. */ public final class AssetManager { /* modes used when opening an asset */ /** * Mode for {@link #open(String, int)}: no specific information about how * data will be accessed. */ public static final int ACCESS_UNKNOWN = 0; /** * Mode for {@link #open(String, int)}: Read chunks, and seek forward and * backward. */ public static final int ACCESS_RANDOM = 1; /** * Mode for {@link #open(String, int)}: Read sequentially, with an * occasional forward seek. */ public static final int ACCESS_STREAMING = 2; /** * Mode for {@link #open(String, int)}: Attempt to load contents into * memory, for fast small reads. */ public static final int ACCESS_BUFFER = 3; private static final String TAG = "AssetManager"; private static final boolean localLOGV = Config.LOGV || false; private static final Object mSync = new Object(); private static final TypedValue mValue = new TypedValue(); private static final long[] mOffsets = new long[2]; private static AssetManager mSystem = null; // For communication with native code. private int mObject; private StringBlock mStringBlocks[] = null; private int mNumRefs = 1; private boolean mOpen = true; private String mAssetDir; private String mAppName; /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the * appropriate asset manager with {@link Resources#getAssets}. Not for * use by applications. * {@hide} */ public AssetManager() { synchronized (mSync) { init(); if (localLOGV) Log.v(TAG, "New asset manager: " + this); ensureSystemAssets(); } } private static void ensureSystemAssets() { synchronized (mSync) { if (mSystem == null) { AssetManager system = new AssetManager(true); system.makeStringBlocks(false); mSystem = system; } } } private AssetManager(boolean isSystem) { init(); if (localLOGV) Log.v(TAG, "New asset manager: " + this); } /** * Return a global shared asset manager that provides access to only * system assets (no application assets). * {@hide} */ public static AssetManager getSystem() { ensureSystemAssets(); return mSystem; } /** * Close this asset manager. */ public void close() { synchronized(mSync) { //System.out.println("Release: num=" + mNumRefs // + ", released=" + mReleased); if (mOpen) { mOpen = false; decRefsLocked(); } } } /** * Retrieve the string value associated with a particular resource * identifier for the current configuration / skin. */ /*package*/ final CharSequence getResourceText(int ident) { synchronized (mSync) { TypedValue tmpValue = mValue; int block = loadResourceValue(ident, tmpValue, true); if (block >= 0) { if (tmpValue.type == TypedValue.TYPE_STRING) { return mStringBlocks[block].get(tmpValue.data); } return tmpValue.coerceToString(); } } return null; } /** * Retrieve the string value associated with a particular resource * identifier for the current configuration / skin. */ /*package*/ final CharSequence getResourceBagText(int ident, int bagEntryId) { synchronized (mSync) { TypedValue tmpValue = mValue; int block = loadResourceBagValue(ident, bagEntryId, tmpValue, true); if (block >= 0) { if (tmpValue.type == TypedValue.TYPE_STRING) { return mStringBlocks[block].get(tmpValue.data); } return tmpValue.coerceToString(); } } return null; } /** * Retrieve the string array associated with a particular resource * identifier. * @param id Resource id of the string array */ /*package*/ final String[] getResourceStringArray(final int id) { String[] retArray = getArrayStringResource(id); return retArray; } /*package*/ final boolean getResourceValue(int ident, TypedValue outValue, boolean resolveRefs) { int block = loadResourceValue(ident, outValue, resolveRefs); if (block >= 0) { if (outValue.type != TypedValue.TYPE_STRING) { return true; } outValue.string = mStringBlocks[block].get(outValue.data); return true; } return false; } /** * Retrieve the text array associated with a particular resource * identifier. * @param id Resource id of the string array */ /*package*/ final CharSequence[] getResourceTextArray(final int id) { int[] rawInfoArray = getArrayStringInfo(id); int rawInfoArrayLen = rawInfoArray.length; final int infoArrayLen = rawInfoArrayLen / 2; int block; int index; CharSequence[] retArray = new CharSequence[infoArrayLen]; for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { block = rawInfoArray[i]; index = rawInfoArray[i + 1]; retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; } return retArray; } /*package*/ final boolean getThemeValue(int theme, int ident, TypedValue outValue, boolean resolveRefs) { int block = loadThemeAttributeValue(theme, ident, outValue, resolveRefs); if (block >= 0) { if (outValue.type != TypedValue.TYPE_STRING) { return true; } StringBlock[] blocks = mStringBlocks; if (blocks == null) { ensureStringBlocks(); } outValue.string = blocks[block].get(outValue.data); return true; } return false; } /*package*/ final void ensureStringBlocks() { if (mStringBlocks == null) { synchronized (mSync) { if (mStringBlocks == null) { makeStringBlocks(true); } } } } private final void makeStringBlocks(boolean copyFromSystem) { final int sysNum = copyFromSystem ? mSystem.mStringBlocks.length : 0; final int num = getStringBlockCount(); mStringBlocks = new StringBlock[num]; if (localLOGV) Log.v(TAG, "Making string blocks for " + this + ": " + num); for (int i=0; i<num; i++) { if (i < sysNum) { mStringBlocks[i] = mSystem.mStringBlocks[i]; } else { mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true); } } } /*package*/ final CharSequence getPooledString(int block, int id) { //System.out.println("Get pooled: block=" + block // + ", id=#" + Integer.toHexString(id) // + ", blocks=" + mStringBlocks); return mStringBlocks[block-1].get(id); } /** * Open an asset using ACCESS_STREAMING mode. This provides access to * files that have been bundled with an application as assets -- that is, * files placed in to the "assets" directory. * * @param fileName The name of the asset to open. This name can be * hierarchical. * * @see #open(String, int) * @see #list */ public final InputStream open(String fileName) throws IOException { return open(fileName, ACCESS_STREAMING); } /** * Open an asset using an explicit access mode, returning an InputStream to * read its contents. This provides access to files that have been bundled * with an application as assets -- that is, files placed in to the * "assets" directory. * * @param fileName The name of the asset to open. This name can be * hierarchical. * @param accessMode Desired access mode for retrieving the data. * * @see #ACCESS_UNKNOWN * @see #ACCESS_STREAMING * @see #ACCESS_RANDOM * @see #ACCESS_BUFFER * @see #open(String) * @see #list */ public final InputStream open(String fileName, int accessMode) throws IOException { synchronized (mSync) { if (!mOpen) { throw new RuntimeException("Assetmanager has been closed"); } int asset = openAsset(fileName, accessMode); if (asset != 0) { mNumRefs++; return new AssetInputStream(asset); } } throw new FileNotFoundException("Asset file: " + fileName); } public final AssetFileDescriptor openFd(String fileName) throws IOException { synchronized (mSync) { if (!mOpen) { throw new RuntimeException("Assetmanager has been closed"); } ParcelFileDescriptor pfd = openAssetFd(fileName, mOffsets); if (pfd != null) { return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); } } throw new FileNotFoundException("Asset file: " + fileName); } /** * Return a String array of all the assets at the given path. * * @param path A relative path within the assets, i.e., "docs/home.html". * * @return String[] Array of strings, one for each asset. These file * names are relative to 'path'. You can open the file by * concatenating 'path' and a name in the returned string (via * File) and passing that to open(). * * @see #open */ public native final String[] list(String path) throws IOException; /** * {@hide} * Open a non-asset file as an asset using ACCESS_STREAMING mode. This * provides direct access to all of the files included in an application * package (not only its assets). Applications should not normally use * this. * * @see #open(String) */ public final InputStream openNonAsset(String fileName) throws IOException { return openNonAsset(0, fileName, ACCESS_STREAMING); } /** * {@hide} * Open a non-asset file as an asset using a specific access mode. This * provides direct access to all of the files included in an application * package (not only its assets). Applications should not normally use * this. * * @see #open(String, int) */ public final InputStream openNonAsset(String fileName, int accessMode) throws IOException { return openNonAsset(0, fileName, accessMode); } /** * {@hide} * Open a non-asset in a specified package. Not for use by applications. * * @param cookie Identifier of the package to be opened. * @param fileName Name of the asset to retrieve. */ public final InputStream openNonAsset(int cookie, String fileName) throws IOException { return openNonAsset(cookie, fileName, ACCESS_STREAMING); } /** * {@hide} * Open a non-asset in a specified package. Not for use by applications. * * @param cookie Identifier of the package to be opened. * @param fileName Name of the asset to retrieve. * @param accessMode Desired access mode for retrieving the data. */ public final InputStream openNonAsset(int cookie, String fileName, int accessMode) throws IOException { synchronized (mSync) { if (!mOpen) { throw new RuntimeException("Assetmanager has been closed"); } int asset = openNonAssetNative(cookie, fileName, accessMode); if (asset != 0) { mNumRefs++; return new AssetInputStream(asset); } } throw new FileNotFoundException("Asset absolute file: " + fileName); } public final AssetFileDescriptor openNonAssetFd(String fileName) throws IOException { return openNonAssetFd(0, fileName); } public final AssetFileDescriptor openNonAssetFd(int cookie, String fileName) throws IOException { synchronized (mSync) { if (!mOpen) { throw new RuntimeException("Assetmanager has been closed"); } ParcelFileDescriptor pfd = openNonAssetFdNative(cookie, fileName, mOffsets); if (pfd != null) { return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); } } throw new FileNotFoundException("Asset absolute file: " + fileName); } /** * Retrieve a parser for a compiled XML file. * * @param fileName The name of the file to retrieve. */ public final XmlResourceParser openXmlResourceParser(String fileName) throws IOException { return openXmlResourceParser(0, fileName); } /** * Retrieve a parser for a compiled XML file. * * @param cookie Identifier of the package to be opened. * @param fileName The name of the file to retrieve. */ public final XmlResourceParser openXmlResourceParser(int cookie, String fileName) throws IOException { XmlBlock block = openXmlBlockAsset(cookie, fileName); XmlResourceParser rp = block.newParser(); block.close(); return rp; } /** * {@hide} * Retrieve a non-asset as a compiled XML file. Not for use by * applications. * * @param fileName The name of the file to retrieve. */ /*package*/ final XmlBlock openXmlBlockAsset(String fileName) throws IOException { return openXmlBlockAsset(0, fileName); } /** * {@hide} * Retrieve a non-asset as a compiled XML file. Not for use by * applications. * * @param cookie Identifier of the package to be opened. * @param fileName Name of the asset to retrieve. */ /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName) throws IOException { synchronized (mSync) { if (!mOpen) { throw new RuntimeException("Assetmanager has been closed"); } int xmlBlock = openXmlAssetNative(cookie, fileName); if (xmlBlock != 0) { mNumRefs++; return new XmlBlock(this, xmlBlock); } } throw new FileNotFoundException("Asset XML file: " + fileName); } /*package*/ void xmlBlockGone() { synchronized (mSync) { decRefsLocked(); } } /*package*/ final int createTheme() { synchronized (mSync) { if (!mOpen) { throw new RuntimeException("Assetmanager has been closed"); } mNumRefs++; return newTheme(); } } /*package*/ final void releaseTheme(int theme) { synchronized (mSync) { deleteTheme(theme); decRefsLocked(); } } protected void finalize() throws Throwable { destroy(); } public final class AssetInputStream extends InputStream { public final int getAssetInt() { return mAsset; } private AssetInputStream(int asset) { mAsset = asset; mLength = getAssetLength(asset); } public final int read() throws IOException { return readAssetChar(mAsset); } public final boolean markSupported() { return true; } public final int available() throws IOException { long len = getAssetRemainingLength(mAsset); return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; } public final void close() throws IOException { synchronized (AssetManager.mSync) { if (mAsset != 0) { destroyAsset(mAsset); mAsset = 0; decRefsLocked(); } } } public final void mark(int readlimit) { mMarkPos = seekAsset(mAsset, 0, 0); } public final void reset() throws IOException { seekAsset(mAsset, mMarkPos, -1); } public final int read(byte[] b) throws IOException { return readAsset(mAsset, b, 0, b.length); } public final int read(byte[] b, int off, int len) throws IOException { return readAsset(mAsset, b, off, len); } public final long skip(long n) throws IOException { long pos = seekAsset(mAsset, 0, 0); if ((pos+n) > mLength) { n = mLength-pos; } if (n > 0) { seekAsset(mAsset, n, 0); } return n; } protected void finalize() throws Throwable { close(); } private int mAsset; private long mLength; private long mMarkPos; } /** * Add an additional set of assets to the asset manager. This can be * either a directory or ZIP file. Not for use by applications. Returns * the cookie of the added asset, or 0 on failure. * {@hide} */ public native final int addAssetPath(String path); /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. * {@hide} */ public native final boolean isUpToDate(); /** * Change the locale being used by this asset manager. Not for use by * applications. * {@hide} */ public native final void setLocale(String locale); /** * Get the locales that this asset manager contains data for. */ public native final String[] getLocales(); /** * Change the configuation used when retrieving resources. Not for use by * applications. * {@hide} */ public native final void setConfiguration(int mcc, int mnc, String locale, int orientation, int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, int screenWidth, int screenHeight, int majorVersion); /** * Retrieve the resource identifier for the given resource name. */ /*package*/ native final int getResourceIdentifier(String type, String name, String defPackage); /*package*/ native final String getResourceName(int resid); /*package*/ native final String getResourcePackageName(int resid); /*package*/ native final String getResourceTypeName(int resid); /*package*/ native final String getResourceEntryName(int resid); private native final int openAsset(String fileName, int accessMode); private final native ParcelFileDescriptor openAssetFd(String fileName, long[] outOffsets) throws IOException; private native final int openNonAssetNative(int cookie, String fileName, int accessMode); private native ParcelFileDescriptor openNonAssetFdNative(int cookie, String fileName, long[] outOffsets) throws IOException; private native final void destroyAsset(int asset); private native final int readAssetChar(int asset); private native final int readAsset(int asset, byte[] b, int off, int len); private native final long seekAsset(int asset, long offset, int whence); private native final long getAssetLength(int asset); private native final long getAssetRemainingLength(int asset); /** Returns true if the resource was found, filling in mRetStringBlock and * mRetData. */ private native final int loadResourceValue(int ident, TypedValue outValue, boolean resolve); /** Returns true if the resource was found, filling in mRetStringBlock and * mRetData. */ private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, boolean resolve); /*package*/ static final int STYLE_NUM_ENTRIES = 5; /*package*/ static final int STYLE_TYPE = 0; /*package*/ static final int STYLE_DATA = 1; /*package*/ static final int STYLE_ASSET_COOKIE = 2; /*package*/ static final int STYLE_RESOURCE_ID = 3; /*package*/ static final int STYLE_CHANGING_CONFIGURATIONS = 4; /*package*/ native static final boolean applyStyle(int theme, int defStyleAttr, int defStyleRes, int xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); /*package*/ native final boolean retrieveAttributes( int xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); /*package*/ native final int getArraySize(int resource); /*package*/ native final int retrieveArray(int resource, int[] outValues); private native final int getStringBlockCount(); private native final int getNativeStringBlock(int block); /** * {@hide} */ public native final String getCookieName(int cookie); /** * {@hide} */ public native static final int getGlobalAssetCount(); /** * {@hide} */ public native static final int getGlobalAssetManagerCount(); private native final int newTheme(); private native final void deleteTheme(int theme); /*package*/ native static final void applyThemeStyle(int theme, int styleRes, boolean force); /*package*/ native static final void copyTheme(int dest, int source); /*package*/ native static final int loadThemeAttributeValue(int theme, int ident, TypedValue outValue, boolean resolve); /*package*/ native static final void dumpTheme(int theme, int priority, String tag, String prefix); private native final int openXmlAssetNative(int cookie, String fileName); private native final String[] getArrayStringResource(int arrayRes); private native final int[] getArrayStringInfo(int arrayRes); /*package*/ native final int[] getArrayIntResource(int arrayRes); private native final void init(); private native final void destroy(); private final void decRefsLocked() { mNumRefs--; //System.out.println("Dec streams: mNumRefs=" + mNumRefs // + " mReleased=" + mReleased); if (mNumRefs == 0) { destroy(); } } }