auto import from //depot/cupcake/@135843
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
new file mode 100644
index 0000000..231e3e2
--- /dev/null
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -0,0 +1,348 @@
+/*
+ * 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.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * File descriptor of an entry in the AssetManager.  This provides your own
+ * opened FileDescriptor that can be used to read the data, as well as the
+ * offset and length of that entry's data in the file.
+ */
+public class AssetFileDescriptor implements Parcelable {
+    /**
+     * Length used with {@link #AssetFileDescriptor(ParcelFileDescriptor, long, long)}
+     * and {@link #getDeclaredLength} when a length has not been declared.  This means
+     * the data extends to the end of the file.
+     */
+    public static final long UNKNOWN_LENGTH = -1;
+    
+    private final ParcelFileDescriptor mFd;
+    private final long mStartOffset;
+    private final long mLength;
+    
+    /**
+     * Create a new AssetFileDescriptor from the given values.
+     * @param fd The underlying file descriptor.
+     * @param startOffset The location within the file that the asset starts.
+     * This must be 0 if length is UNKNOWN_LENGTH.
+     * @param length The number of bytes of the asset, or
+     * {@link #UNKNOWN_LENGTH if it extends to the end of the file.
+     */
+    public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
+            long length) {
+        if (length < 0 && startOffset != 0) {
+            throw new IllegalArgumentException(
+                    "startOffset must be 0 when using UNKNOWN_LENGTH");
+        }
+        mFd = fd;
+        mStartOffset = startOffset;
+        mLength = length;
+    }
+    
+    /**
+     * The AssetFileDescriptor contains its own ParcelFileDescriptor, which
+     * in addition to the normal FileDescriptor object also allows you to close
+     * the descriptor when you are done with it.
+     */
+    public ParcelFileDescriptor getParcelFileDescriptor() {
+        return mFd;
+    }
+    
+    /**
+     * Returns the FileDescriptor that can be used to read the data in the
+     * file.
+     */
+    public FileDescriptor getFileDescriptor() {
+        return mFd.getFileDescriptor();
+    }
+    
+    /**
+     * Returns the byte offset where this asset entry's data starts.
+     */
+    public long getStartOffset() {
+        return mStartOffset;
+    }
+    
+    /**
+     * Returns the total number of bytes of this asset entry's data.  May be
+     * {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file.
+     * If the AssetFileDescriptor was constructed with {@link #UNKNOWN_LENGTH},
+     * this will use {@link ParcelFileDescriptor#getStatSize()
+     * ParcelFileDescriptor.getStatSize()} to find the total size of the file,
+     * returning that number if found or {@link #UNKNOWN_LENGTH} if it could
+     * not be determined.
+     * 
+     * @see #getDeclaredLength()
+     */
+    public long getLength() {
+        if (mLength >= 0) {
+            return mLength;
+        }
+        long len = mFd.getStatSize();
+        return len >= 0 ? len : UNKNOWN_LENGTH;
+    }
+    
+    /**
+     * Return the actual number of bytes that were declared when the
+     * AssetFileDescriptor was constructed.  Will be
+     * {@link #UNKNOWN_LENGTH} if the length was not declared, meaning data
+     * should be read to the end of the file.
+     * 
+     * @see #getDeclaredLength()
+     */
+    public long getDeclaredLength() {
+        return mLength;
+    }
+    
+    /**
+     * Convenience for calling <code>getParcelFileDescriptor().close()</code>.
+     */
+    public void close() throws IOException {
+        mFd.close();
+    }
+    
+    /**
+     * Create and return a new auto-close input stream for this asset.  This
+     * will either return a full asset {@link AutoCloseInputStream}, or
+     * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream
+     * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the
+     * the object represents a complete file or sub-section of a file.  You
+     * should only call this once for a particular asset.
+     */
+    public FileInputStream createInputStream() throws IOException {
+        if (mLength < 0) {
+            return new ParcelFileDescriptor.AutoCloseInputStream(mFd);
+        }
+        return new AutoCloseInputStream(this);
+    }
+    
+    /**
+     * Create and return a new auto-close output stream for this asset.  This
+     * will either return a full asset {@link AutoCloseOutputStream}, or
+     * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream
+     * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the
+     * the object represents a complete file or sub-section of a file.  You
+     * should only call this once for a particular asset.
+     */
+    public FileOutputStream createOutputStream() throws IOException {
+        if (mLength < 0) {
+            return new ParcelFileDescriptor.AutoCloseOutputStream(mFd);
+        }
+        return new AutoCloseOutputStream(this);
+    }
+    
+    @Override
+    public String toString() {
+        return "{AssetFileDescriptor: " + mFd
+                + " start=" + mStartOffset + " len=" + mLength + "}";
+    }
+    
+    /**
+     * An InputStream you can create on a ParcelFileDescriptor, which will
+     * take care of calling {@link ParcelFileDescriptor#close
+     * ParcelFileDescritor.close()} for you when the stream is closed.
+     */
+    public static class AutoCloseInputStream
+            extends ParcelFileDescriptor.AutoCloseInputStream {
+        private long mRemaining;
+        
+        public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
+            super(fd.getParcelFileDescriptor());
+            super.skip(fd.getStartOffset());
+            mRemaining = (int)fd.getLength();
+        }
+
+        @Override
+        public int available() throws IOException {
+            return mRemaining >= 0
+                    ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff)
+                    : super.available();
+        }
+
+        @Override
+        public int read() throws IOException {
+            if (mRemaining >= 0) {
+                if (mRemaining == 0) return -1;
+                int res = super.read();
+                if (res >= 0) mRemaining--;
+                return res;
+            }
+            
+            return super.read();
+        }
+
+        @Override
+        public int read(byte[] buffer, int offset, int count) throws IOException {
+            if (mRemaining >= 0) {
+                if (mRemaining == 0) return -1;
+                if (count > mRemaining) count = (int)mRemaining;
+                int res = super.read(buffer, offset, count);
+                if (res >= 0) mRemaining -= res;
+                return res;
+            }
+            
+            return super.read(buffer, offset, count);
+        }
+
+        @Override
+        public int read(byte[] buffer) throws IOException {
+            if (mRemaining >= 0) {
+                if (mRemaining == 0) return -1;
+                int count = buffer.length;
+                if (count > mRemaining) count = (int)mRemaining;
+                int res = super.read(buffer, 0, count);
+                if (res >= 0) mRemaining -= res;
+                return res;
+            }
+            
+            return super.read(buffer);
+        }
+
+        @Override
+        public long skip(long count) throws IOException {
+            if (mRemaining >= 0) {
+                if (mRemaining == 0) return -1;
+                if (count > mRemaining) count = mRemaining;
+                long res = super.skip(count);
+                if (res >= 0) mRemaining -= res;
+                return res;
+            }
+            
+            // TODO Auto-generated method stub
+            return super.skip(count);
+        }
+
+        @Override
+        public void mark(int readlimit) {
+            if (mRemaining >= 0) {
+                // Not supported.
+                return;
+            }
+            super.mark(readlimit);
+        }
+
+        @Override
+        public boolean markSupported() {
+            if (mRemaining >= 0) {
+                return false;
+            }
+            return super.markSupported();
+        }
+
+        @Override
+        public synchronized void reset() throws IOException {
+            if (mRemaining >= 0) {
+                // Not supported.
+                return;
+            }
+            super.reset();
+        }
+    }
+    
+    /**
+     * An OutputStream you can create on a ParcelFileDescriptor, which will
+     * take care of calling {@link ParcelFileDescriptor#close
+     * ParcelFileDescritor.close()} for you when the stream is closed.
+     */
+    public static class AutoCloseOutputStream
+            extends ParcelFileDescriptor.AutoCloseOutputStream {
+        private long mRemaining;
+        
+        public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException {
+            super(fd.getParcelFileDescriptor());
+            if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) {
+                throw new IOException("Unable to seek");
+            }
+            mRemaining = (int)fd.getLength();
+        }
+
+        @Override
+        public void write(byte[] buffer, int offset, int count) throws IOException {
+            if (mRemaining >= 0) {
+                if (mRemaining == 0) return;
+                if (count > mRemaining) count = (int)mRemaining;
+                super.write(buffer, offset, count);
+                mRemaining -= count;
+                return;
+            }
+            
+            super.write(buffer, offset, count);
+        }
+
+        @Override
+        public void write(byte[] buffer) throws IOException {
+            if (mRemaining >= 0) {
+                if (mRemaining == 0) return;
+                int count = buffer.length;
+                if (count > mRemaining) count = (int)mRemaining;
+                super.write(buffer);
+                mRemaining -= count;
+                return;
+            }
+            
+            super.write(buffer);
+        }
+
+        @Override
+        public void write(int oneByte) throws IOException {
+            if (mRemaining >= 0) {
+                if (mRemaining == 0) return;
+                super.write(oneByte);
+                mRemaining--;
+                return;
+            }
+            
+            super.write(oneByte);
+        }
+    }
+    
+    
+    /* Parcelable interface */
+    public int describeContents() {
+        return mFd.describeContents();
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        mFd.writeToParcel(out, flags);
+        out.writeLong(mStartOffset);
+        out.writeLong(mLength);
+    }
+
+    AssetFileDescriptor(Parcel src) {
+        mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src);
+        mStartOffset = src.readLong();
+        mLength = src.readLong();
+    }
+    
+    public static final Parcelable.Creator<AssetFileDescriptor> CREATOR
+            = new Parcelable.Creator<AssetFileDescriptor>() {
+        public AssetFileDescriptor createFromParcel(Parcel in) {
+            return new AssetFileDescriptor(in);
+        }
+        public AssetFileDescriptor[] newArray(int size) {
+            return new AssetFileDescriptor[size];
+        }
+    };
+}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
new file mode 100644
index 0000000..fadcb35
--- /dev/null
+++ b/core/java/android/content/res/AssetManager.java
@@ -0,0 +1,697 @@
+/*
+ * 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.  A
+     * zero return value indicates 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();
+        }
+    }
+}
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
new file mode 100644
index 0000000..0f3f270
--- /dev/null
+++ b/core/java/android/content/res/ColorStateList.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2007 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 com.android.internal.util.ArrayUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.util.StateSet;
+import android.util.Xml;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+
+/**
+ *
+ * Lets you map {@link android.view.View} state sets to colors.
+ *
+ * {@link android.content.res.ColorStateList}s are created from XML resource files defined in the
+ * "color" subdirectory directory of an application's resource directory.  The XML file contains
+ * a single "selector" element with a number of "item" elements inside.  For example:
+ *
+ * <pre>
+ * &lt;selector xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ *   &lt;item android:state_focused="true" android:color="@color/testcolor1"/&gt;
+ *   &lt;item android:state_pressed="true" android:state_enabled="false" android:color="@color/testcolor2" /&gt;
+ *   &lt;item android:state_enabled="false" android:colore="@color/testcolor3" /&gt;
+ *   &lt;item android:state_active="true" android:color="@color/testcolor4" /&gt;
+ *   &lt;item android:color="@color/testcolor5"/&gt;
+ * &lt;/selector&gt;
+ * </pre>
+ *
+ * This defines a set of state spec / color pairs where each state spec specifies a set of
+ * states that a view must either be in or not be in and the color specifies the color associated
+ * with that spec.  The list of state specs will be processed in order of the items in the XML file.
+ * An item with no state spec is considered to match any set of states and is generally useful as
+ * a final item to be used as a default.  Note that if you have such an item before any other items
+ * in the list then any subsequent items will end up being ignored.
+ */
+public class ColorStateList implements Parcelable {
+
+    private int[][] mStateSpecs; // must be parallel to mColors
+    private int[] mColors;      // must be parallel to mStateSpecs
+    private int mDefaultColor = 0xffff0000;
+
+    private static final int[][] EMPTY = new int[][] { new int[0] };
+    private static final SparseArray<WeakReference<ColorStateList>> sCache =
+                            new SparseArray<WeakReference<ColorStateList>>();
+
+    private ColorStateList() { }
+
+    /**
+     * Creates a ColorStateList that returns the specified mapping from
+     * states to colors.
+     */
+    public ColorStateList(int[][] states, int[] colors) {
+        mStateSpecs = states;
+        mColors = colors;
+
+        if (states.length > 0) {
+            mDefaultColor = colors[0];
+
+            for (int i = 0; i < states.length; i++) {
+                if (states[i].length == 0) {
+                    mDefaultColor = colors[i];
+                }
+            }
+        }
+    }
+
+    /**
+     * Creates or retrieves a ColorStateList that always returns a single color.
+     */
+    public static ColorStateList valueOf(int color) {
+        // TODO: should we collect these eventually?
+        synchronized (sCache) {
+            WeakReference<ColorStateList> ref = sCache.get(color);
+            ColorStateList csl = ref != null ? ref.get() : null;
+
+            if (csl != null) {
+                return csl;
+            }
+
+            csl = new ColorStateList(EMPTY, new int[] { color });
+            sCache.put(color, new WeakReference<ColorStateList>(csl));
+            return csl;
+        }
+    }
+
+    /**
+     * Create a ColorStateList from an XML document, given a set of {@link Resources}.
+     */
+    public static ColorStateList createFromXml(Resources r, XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+
+        AttributeSet attrs = Xml.asAttributeSet(parser);
+
+        int type;
+        while ((type=parser.next()) != XmlPullParser.START_TAG
+                   && type != XmlPullParser.END_DOCUMENT) {
+        }
+
+        if (type != XmlPullParser.START_TAG) {
+            throw new XmlPullParserException("No start tag found");
+        }
+
+        return createFromXmlInner(r, parser, attrs);
+    }
+
+    /* Create from inside an XML document.  Called on a parser positioned at
+     * a tag in an XML document, tries to create a ColorStateList from that tag.
+     * Returns null if the tag is not a valid ColorStateList.
+     */
+    private static ColorStateList createFromXmlInner(Resources r, XmlPullParser parser,
+            AttributeSet attrs) throws XmlPullParserException, IOException {
+
+        ColorStateList colorStateList;
+
+        final String name = parser.getName();
+
+        if (name.equals("selector")) {
+            colorStateList = new ColorStateList();
+        } else {
+            throw new XmlPullParserException(
+                parser.getPositionDescription() + ": invalid drawable tag " + name);
+        }
+
+        colorStateList.inflate(r, parser, attrs);
+        return colorStateList;
+    }
+
+    /**
+     * Creates a new ColorStateList that has the same states and
+     * colors as this one but where each color has the specified alpha value
+     * (0-255).
+     */
+    public ColorStateList withAlpha(int alpha) {
+        int[] colors = new int[mColors.length];
+
+        int len = colors.length;
+        for (int i = 0; i < len; i++) {
+            colors[i] = (mColors[i] & 0xFFFFFF) | (alpha << 24);
+        }
+
+        return new ColorStateList(mStateSpecs, colors);
+    }
+
+    /**
+     * Fill in this object based on the contents of an XML "selector" element.
+     */
+    private void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+        throws XmlPullParserException, IOException {
+
+        int type;
+
+        final int innerDepth = parser.getDepth()+1;
+        int depth;
+
+        int listAllocated = 20;
+        int listSize = 0;
+        int[] colorList = new int[listAllocated];
+        int[][] stateSpecList = new int[listAllocated][];
+
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+               && ((depth=parser.getDepth()) >= innerDepth
+                   || type != XmlPullParser.END_TAG)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            if (depth > innerDepth || !parser.getName().equals("item")) {
+                continue;
+            }
+
+            int colorRes = 0;
+            int color = 0xffff0000;
+            boolean haveColor = false;
+
+            int i;
+            int j = 0;
+            final int numAttrs = attrs.getAttributeCount();
+            int[] stateSpec = new int[numAttrs];
+            for (i = 0; i < numAttrs; i++) {
+                final int stateResId = attrs.getAttributeNameResource(i);
+                if (stateResId == 0) break;
+                if (stateResId == com.android.internal.R.attr.color) {
+                    colorRes = attrs.getAttributeResourceValue(i, 0);
+
+                    if (colorRes == 0) {
+                        color = attrs.getAttributeIntValue(i, color);
+                        haveColor = true;
+                    }
+                } else {
+                    stateSpec[j++] = attrs.getAttributeBooleanValue(i, false)
+                                  ? stateResId
+                                  : -stateResId;
+                }
+            }
+            stateSpec = StateSet.trimStateSet(stateSpec, j);
+
+            if (colorRes != 0) {
+                color = r.getColor(colorRes);
+            } else if (!haveColor) {
+                throw new XmlPullParserException(
+                        parser.getPositionDescription()
+                        + ": <item> tag requires a 'android:color' attribute.");
+            }
+
+            if (listSize == 0 || stateSpec.length == 0) {
+                mDefaultColor = color;
+            }
+            
+            if (listSize + 1 >= listAllocated) {
+                listAllocated = ArrayUtils.idealIntArraySize(listSize + 1);
+
+                int[] ncolor = new int[listAllocated];
+                System.arraycopy(colorList, 0, ncolor, 0, listSize);
+
+                int[][] nstate = new int[listAllocated][];
+                System.arraycopy(stateSpecList, 0, nstate, 0, listSize);
+
+                colorList = ncolor;
+                stateSpecList = nstate;
+            }
+
+            colorList[listSize] = color;
+            stateSpecList[listSize] = stateSpec;
+            listSize++;
+        }
+
+        mColors = new int[listSize];
+        mStateSpecs = new int[listSize][];
+        System.arraycopy(colorList, 0, mColors, 0, listSize);
+        System.arraycopy(stateSpecList, 0, mStateSpecs, 0, listSize);
+    }
+
+    public boolean isStateful() {
+        return mStateSpecs.length > 1;
+    }
+    
+    /**
+     * Return the color associated with the given set of {@link android.view.View} states.
+     *
+     * @param stateSet an array of {@link android.view.View} states
+     * @param defaultColor the color to return if there's not state spec in this
+     * {@link ColorStateList} that matches the stateSet.
+     *
+     * @return the color associated with that set of states in this {@link ColorStateList}.
+     */
+    public int getColorForState(int[] stateSet, int defaultColor) {
+        final int setLength = mStateSpecs.length;
+        for (int i = 0; i < setLength; i++) {
+            int[] stateSpec = mStateSpecs[i];
+            if (StateSet.stateSetMatches(stateSpec, stateSet)) {
+                return mColors[i];
+            }
+        }
+        return defaultColor;
+    }
+
+    /**
+     * Return the default color in this {@link ColorStateList}.
+     *
+     * @return the default color in this {@link ColorStateList}.
+     */
+    public int getDefaultColor() {
+        return mDefaultColor;
+    }
+
+    public String toString() {
+        return "ColorStateList{" +
+               "mStateSpecs=" + Arrays.deepToString(mStateSpecs) +
+               "mColors=" + Arrays.toString(mColors) +
+               "mDefaultColor=" + mDefaultColor + '}';
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        final int N = mStateSpecs.length;
+        dest.writeInt(N);
+        for (int i=0; i<N; i++) {
+            dest.writeIntArray(mStateSpecs[i]);
+        }
+        dest.writeArray(mStateSpecs);
+        dest.writeIntArray(mColors);
+    }
+
+    public static final Parcelable.Creator<ColorStateList> CREATOR =
+            new Parcelable.Creator<ColorStateList>() {
+        public ColorStateList[] newArray(int size) {
+            return new ColorStateList[size];
+        }
+
+        public ColorStateList createFromParcel(Parcel source) {
+            final int N = source.readInt();
+            int[][] stateSpecs = new int[N][];
+            for (int i=0; i<N; i++) {
+                stateSpecs[i] = source.createIntArray();
+            }
+            int[] colors = source.createIntArray();
+            return new ColorStateList(stateSpecs, colors);
+        }
+    };
+}
diff --git a/core/java/android/content/res/Configuration.aidl b/core/java/android/content/res/Configuration.aidl
new file mode 100755
index 0000000..bb7f2dd
--- /dev/null
+++ b/core/java/android/content/res/Configuration.aidl
@@ -0,0 +1,21 @@
+/* //device/java/android/android/view/WindowManager.aidl
+**
+** Copyright 2007, 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;
+
+parcelable Configuration;
+
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
new file mode 100644
index 0000000..7e4b7ac
--- /dev/null
+++ b/core/java/android/content/res/Configuration.java
@@ -0,0 +1,436 @@
+package android.content.res;
+
+import android.content.pm.ActivityInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Locale;
+
+/**
+ * This class describes all device configuration information that can
+ * impact the resources the application retrieves.  This includes both
+ * user-specified configuration options (locale and scaling) as well
+ * as dynamic device configuration (various types of input devices).
+ */
+public final class Configuration implements Parcelable, Comparable<Configuration> {
+    /**
+     * Current user preference for the scaling factor for fonts, relative
+     * to the base density scaling.
+     */
+    public float fontScale;
+
+    /**
+     * IMSI MCC (Mobile Country Code).  0 if undefined.
+     */
+    public int mcc;
+    
+    /**
+     * IMSI MNC (Mobile Network Code).  0 if undefined.
+     */
+    public int mnc;
+    
+    /**
+     * Current user preference for the locale.
+     */
+    public Locale locale;
+
+    /**
+     * Locale should persist on setting
+     * @hide pending API council approval
+     */
+    public boolean userSetLocale;
+
+    public static final int TOUCHSCREEN_UNDEFINED = 0;
+    public static final int TOUCHSCREEN_NOTOUCH = 1;
+    public static final int TOUCHSCREEN_STYLUS = 2;
+    public static final int TOUCHSCREEN_FINGER = 3;
+    
+    /**
+     * The kind of touch screen attached to the device.
+     * One of: {@link #TOUCHSCREEN_NOTOUCH}, {@link #TOUCHSCREEN_STYLUS}, 
+     * {@link #TOUCHSCREEN_FINGER}. 
+     */
+    public int touchscreen;
+    
+    public static final int KEYBOARD_UNDEFINED = 0;
+    public static final int KEYBOARD_NOKEYS = 1;
+    public static final int KEYBOARD_QWERTY = 2;
+    public static final int KEYBOARD_12KEY = 3;
+    
+    /**
+     * The kind of keyboard attached to the device.
+     * One of: {@link #KEYBOARD_QWERTY}, {@link #KEYBOARD_12KEY}.
+     */
+    public int keyboard;
+    
+    public static final int KEYBOARDHIDDEN_UNDEFINED = 0;
+    public static final int KEYBOARDHIDDEN_NO = 1;
+    public static final int KEYBOARDHIDDEN_YES = 2;
+    /** Constant matching actual resource implementation. {@hide} */
+    public static final int KEYBOARDHIDDEN_SOFT = 3;
+    
+    /**
+     * A flag indicating whether any keyboard is available.  Unlike
+     * {@link #hardKeyboardHidden}, this also takes into account a soft
+     * keyboard, so if the hard keyboard is hidden but there is soft
+     * keyboard available, it will be set to NO.  Value is one of:
+     * {@link #KEYBOARDHIDDEN_NO}, {@link #KEYBOARDHIDDEN_YES}.
+     */
+    public int keyboardHidden;
+    
+    public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0;
+    public static final int HARDKEYBOARDHIDDEN_NO = 1;
+    public static final int HARDKEYBOARDHIDDEN_YES = 2;
+    
+    /**
+     * A flag indicating whether the hard keyboard has been hidden.  This will
+     * be set on a device with a mechanism to hide the keyboard from the
+     * user, when that mechanism is closed.  One of:
+     * {@link #HARDKEYBOARDHIDDEN_NO}, {@link #HARDKEYBOARDHIDDEN_YES}.
+     */
+    public int hardKeyboardHidden;
+    
+    public static final int NAVIGATION_UNDEFINED = 0;
+    public static final int NAVIGATION_NONAV = 1;
+    public static final int NAVIGATION_DPAD = 2;
+    public static final int NAVIGATION_TRACKBALL = 3;
+    public static final int NAVIGATION_WHEEL = 4;
+    
+    /**
+     * The kind of navigation method available on the device.
+     * One of: {@link #NAVIGATION_DPAD}, {@link #NAVIGATION_TRACKBALL}, 
+     * {@link #NAVIGATION_WHEEL}. 
+     */
+    public int navigation;
+    
+    public static final int ORIENTATION_UNDEFINED = 0;
+    public static final int ORIENTATION_PORTRAIT = 1;
+    public static final int ORIENTATION_LANDSCAPE = 2;
+    public static final int ORIENTATION_SQUARE = 3;
+    
+    /**
+     * Overall orientation of the screen.  May be one of
+     * {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT},
+     * or {@link #ORIENTATION_SQUARE}.
+     */
+    public int orientation;
+    
+    /**
+     * Construct an invalid Configuration.  You must call {@link #setToDefaults}
+     * for this object to be valid.  {@more}
+     */
+    public Configuration() {
+        setToDefaults();
+    }
+
+    /**
+     * Makes a deep copy suitable for modification.
+     */
+    public Configuration(Configuration o) {
+        fontScale = o.fontScale;
+        mcc = o.mcc;
+        mnc = o.mnc;
+        if (o.locale != null) {
+            locale = (Locale) o.locale.clone();
+        }
+        userSetLocale = o.userSetLocale;
+        touchscreen = o.touchscreen;
+        keyboard = o.keyboard;
+        keyboardHidden = o.keyboardHidden;
+        hardKeyboardHidden = o.hardKeyboardHidden;
+        navigation = o.navigation;
+        orientation = o.orientation;
+    }
+
+    public String toString() {
+        return "{ scale=" + fontScale + " imsi=" + mcc + "/" + mnc
+                + " locale=" + locale
+                + " touch=" + touchscreen + " key=" + keyboard + "/"
+                + keyboardHidden + "/" + hardKeyboardHidden
+                + " nav=" + navigation + " orien=" + orientation + " }";
+    }
+
+    /**
+     * Set this object to the system defaults.
+     */
+    public void setToDefaults() {
+        fontScale = 1;
+        mcc = mnc = 0;
+        locale = Locale.getDefault();
+        userSetLocale = false;
+        touchscreen = TOUCHSCREEN_UNDEFINED;
+        keyboard = KEYBOARD_UNDEFINED;
+        keyboardHidden = KEYBOARDHIDDEN_UNDEFINED;
+        hardKeyboardHidden = HARDKEYBOARDHIDDEN_UNDEFINED;
+        navigation = NAVIGATION_UNDEFINED;
+        orientation = ORIENTATION_UNDEFINED;
+    }
+
+    /** {@hide} */
+    @Deprecated public void makeDefault() {
+        setToDefaults();
+    }
+    
+    /**
+     * Copy the fields from delta into this Configuration object, keeping
+     * track of which ones have changed.  Any undefined fields in
+     * <var>delta</var> are ignored and not copied in to the current
+     * Configuration.
+     * @return Returns a bit mask of the changed fields, as per
+     * {@link #diff}.
+     */
+    public int updateFrom(Configuration delta) {
+        int changed = 0;
+        if (delta.fontScale > 0 && fontScale != delta.fontScale) {
+            changed |= ActivityInfo.CONFIG_FONT_SCALE;
+            fontScale = delta.fontScale;
+        }
+        if (delta.mcc != 0 && mcc != delta.mcc) {
+            changed |= ActivityInfo.CONFIG_MCC;
+            mcc = delta.mcc;
+        }
+        if (delta.mnc != 0 && mnc != delta.mnc) {
+            changed |= ActivityInfo.CONFIG_MNC;
+            mnc = delta.mnc;
+        }
+        if (delta.locale != null
+                && (locale == null || !locale.equals(delta.locale))) {
+            changed |= ActivityInfo.CONFIG_LOCALE;
+            locale = delta.locale != null
+                    ? (Locale) delta.locale.clone() : null;
+        }
+        if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
+        {
+            userSetLocale = true;
+            changed |= ActivityInfo.CONFIG_LOCALE;
+        }
+        if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
+                && touchscreen != delta.touchscreen) {
+            changed |= ActivityInfo.CONFIG_TOUCHSCREEN;
+            touchscreen = delta.touchscreen;
+        }
+        if (delta.keyboard != KEYBOARD_UNDEFINED
+                && keyboard != delta.keyboard) {
+            changed |= ActivityInfo.CONFIG_KEYBOARD;
+            keyboard = delta.keyboard;
+        }
+        if (delta.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED
+                && keyboardHidden != delta.keyboardHidden) {
+            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+            keyboardHidden = delta.keyboardHidden;
+        }
+        if (delta.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED
+                && hardKeyboardHidden != delta.hardKeyboardHidden) {
+            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+            hardKeyboardHidden = delta.hardKeyboardHidden;
+        }
+        if (delta.navigation != NAVIGATION_UNDEFINED
+                && navigation != delta.navigation) {
+            changed |= ActivityInfo.CONFIG_NAVIGATION;
+            navigation = delta.navigation;
+        }
+        if (delta.orientation != ORIENTATION_UNDEFINED
+                && orientation != delta.orientation) {
+            changed |= ActivityInfo.CONFIG_ORIENTATION;
+            orientation = delta.orientation;
+        }
+        
+        return changed;
+    }
+
+    /**
+     * Return a bit mask of the differences between this Configuration
+     * object and the given one.  Does not change the values of either.  Any
+     * undefined fields in <var>delta</var> are ignored.
+     * @return Returns a bit mask indicating which configuration
+     * values has changed, containing any combination of
+     * {@link android.content.pm.ActivityInfo#CONFIG_FONT_SCALE
+     * PackageManager.ActivityInfo.CONFIG_FONT_SCALE},
+     * {@link android.content.pm.ActivityInfo#CONFIG_MCC
+     * PackageManager.ActivityInfo.CONFIG_MCC},
+     * {@link android.content.pm.ActivityInfo#CONFIG_MNC
+     * PackageManager.ActivityInfo.CONFIG_MNC},
+     * {@link android.content.pm.ActivityInfo#CONFIG_LOCALE
+     * PackageManager.ActivityInfo.CONFIG_LOCALE},
+     * {@link android.content.pm.ActivityInfo#CONFIG_TOUCHSCREEN
+     * PackageManager.ActivityInfo.CONFIG_TOUCHSCREEN},
+     * {@link android.content.pm.ActivityInfo#CONFIG_KEYBOARD
+     * PackageManager.ActivityInfo.CONFIG_KEYBOARD},
+     * {@link android.content.pm.ActivityInfo#CONFIG_NAVIGATION
+     * PackageManager.ActivityInfo.CONFIG_NAVIGATION}, or
+     * {@link android.content.pm.ActivityInfo#CONFIG_ORIENTATION
+     * PackageManager.ActivityInfo.CONFIG_ORIENTATION}.
+     */
+    public int diff(Configuration delta) {
+        int changed = 0;
+        if (delta.fontScale > 0 && fontScale != delta.fontScale) {
+            changed |= ActivityInfo.CONFIG_FONT_SCALE;
+        }
+        if (delta.mcc != 0 && mcc != delta.mcc) {
+            changed |= ActivityInfo.CONFIG_MCC;
+        }
+        if (delta.mnc != 0 && mnc != delta.mnc) {
+            changed |= ActivityInfo.CONFIG_MNC;
+        }
+        if (delta.locale != null
+                && (locale == null || !locale.equals(delta.locale))) {
+            changed |= ActivityInfo.CONFIG_LOCALE;
+        }
+        if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
+                && touchscreen != delta.touchscreen) {
+            changed |= ActivityInfo.CONFIG_TOUCHSCREEN;
+        }
+        if (delta.keyboard != KEYBOARD_UNDEFINED
+                && keyboard != delta.keyboard) {
+            changed |= ActivityInfo.CONFIG_KEYBOARD;
+        }
+        if (delta.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED
+                && keyboardHidden != delta.keyboardHidden) {
+            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+        }
+        if (delta.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED
+                && hardKeyboardHidden != delta.hardKeyboardHidden) {
+            changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+        }
+        if (delta.navigation != NAVIGATION_UNDEFINED
+                && navigation != delta.navigation) {
+            changed |= ActivityInfo.CONFIG_NAVIGATION;
+        }
+        if (delta.orientation != ORIENTATION_UNDEFINED
+                && orientation != delta.orientation) {
+            changed |= ActivityInfo.CONFIG_ORIENTATION;
+        }
+        
+        return changed;
+    }
+
+    /**
+     * Determine if a new resource needs to be loaded from the bit set of
+     * configuration changes returned by {@link #updateFrom(Configuration)}.
+     * 
+     * @param configChanges The mask of changes configurations as returned by
+     * {@link #updateFrom(Configuration)}.
+     * @param interestingChanges The configuration changes that the resource
+     * can handled, as given in {@link android.util.TypedValue#changingConfigurations}.
+     * 
+     * @return Return true if the resource needs to be loaded, else false.
+     */
+    public static boolean needNewResources(int configChanges, int interestingChanges) {
+        return (configChanges & (interestingChanges|ActivityInfo.CONFIG_FONT_SCALE)) != 0;
+    }
+    
+    /**
+     * Parcelable methods
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeFloat(fontScale);
+        dest.writeInt(mcc);
+        dest.writeInt(mnc);
+        if (locale == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+            dest.writeString(locale.getLanguage());
+            dest.writeString(locale.getCountry());
+            dest.writeString(locale.getVariant());
+        }
+        if(userSetLocale) {
+            dest.writeInt(1);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeInt(touchscreen);
+        dest.writeInt(keyboard);
+        dest.writeInt(keyboardHidden);
+        dest.writeInt(hardKeyboardHidden);
+        dest.writeInt(navigation);
+        dest.writeInt(orientation);
+    }
+
+    public static final Parcelable.Creator<Configuration> CREATOR
+            = new Parcelable.Creator<Configuration>() {
+        public Configuration createFromParcel(Parcel source) {
+            return new Configuration(source);
+        }
+
+        public Configuration[] newArray(int size) {
+            return new Configuration[size];
+        }
+    };
+
+    /**
+     * Construct this Configuration object, reading from the Parcel.
+     */
+    private Configuration(Parcel source) {
+        fontScale = source.readFloat();
+        mcc = source.readInt();
+        mnc = source.readInt();
+        if (source.readInt() != 0) {
+            locale = new Locale(source.readString(), source.readString(),
+                    source.readString());
+        }
+        userSetLocale = (source.readInt()==1);
+        touchscreen = source.readInt();
+        keyboard = source.readInt();
+        keyboardHidden = source.readInt();
+        hardKeyboardHidden = source.readInt();
+        navigation = source.readInt();
+        orientation = source.readInt();
+    }
+
+    public int compareTo(Configuration that) {
+        int n;
+        float a = this.fontScale;
+        float b = that.fontScale;
+        if (a < b) return -1;
+        if (a > b) return 1;
+        n = this.mcc - that.mcc;
+        if (n != 0) return n;
+        n = this.mnc - that.mnc;
+        if (n != 0) return n;
+        n = this.locale.getLanguage().compareTo(that.locale.getLanguage());
+        if (n != 0) return n;
+        n = this.locale.getCountry().compareTo(that.locale.getCountry());
+        if (n != 0) return n;
+        n = this.locale.getVariant().compareTo(that.locale.getVariant());
+        if (n != 0) return n;
+        n = this.touchscreen - that.touchscreen;
+        if (n != 0) return n;
+        n = this.keyboard - that.keyboard;
+        if (n != 0) return n;
+        n = this.keyboardHidden - that.keyboardHidden;
+        if (n != 0) return n;
+        n = this.hardKeyboardHidden - that.hardKeyboardHidden;
+        if (n != 0) return n;
+        n = this.navigation - that.navigation;
+        if (n != 0) return n;
+        n = this.orientation - that.orientation;
+        //if (n != 0) return n;
+        return n;
+    }
+
+    public boolean equals(Configuration that) {
+        if (that == null) return false;
+        if (that == this) return true;
+        return this.compareTo(that) == 0;
+    }
+
+    public boolean equals(Object that) {
+        try {
+            return equals((Configuration)that);
+        } catch (ClassCastException e) {
+        }
+        return false;
+    }
+    
+    public int hashCode() {
+        return ((int)this.fontScale) + this.mcc + this.mnc
+                + this.locale.hashCode() + this.touchscreen
+                + this.keyboard + this.keyboardHidden + this.hardKeyboardHidden
+                + this.navigation + this.orientation;
+    }
+}
diff --git a/core/java/android/content/res/PluralRules.java b/core/java/android/content/res/PluralRules.java
new file mode 100644
index 0000000..2dce3c1
--- /dev/null
+++ b/core/java/android/content/res/PluralRules.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2007 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 java.util.Locale;
+
+/*
+ * Yuck-o.  This is not the right way to implement this.  When the ICU PluralRules
+ * object has been integrated to android, we should switch to that.  For now, yuck-o.
+ */
+
+abstract class PluralRules {
+
+    static final int QUANTITY_OTHER = 0x0000;
+    static final int QUANTITY_ZERO  = 0x0001;
+    static final int QUANTITY_ONE   = 0x0002;
+    static final int QUANTITY_TWO   = 0x0004;
+    static final int QUANTITY_FEW   = 0x0008;
+    static final int QUANTITY_MANY  = 0x0010;
+
+    static final int ID_OTHER = 0x01000004;
+
+    abstract int quantityForNumber(int n);
+
+    final int attrForNumber(int n) {
+        return PluralRules.attrForQuantity(quantityForNumber(n));
+    }
+
+    static final int attrForQuantity(int quantity) {
+        // see include/utils/ResourceTypes.h
+        switch (quantity) {
+            case QUANTITY_ZERO: return 0x01000005;
+            case QUANTITY_ONE:  return 0x01000006;
+            case QUANTITY_TWO:  return 0x01000007;
+            case QUANTITY_FEW:  return 0x01000008;
+            case QUANTITY_MANY: return 0x01000009;
+            default:            return ID_OTHER;
+        }
+    }
+
+    static final String stringForQuantity(int quantity) {
+        switch (quantity) {
+            case QUANTITY_ZERO:
+                return "zero";
+            case QUANTITY_ONE:
+                return "one";
+            case QUANTITY_TWO:
+                return "two";
+            case QUANTITY_FEW:
+                return "few";
+            case QUANTITY_MANY:
+                return "many";
+            default:
+                return "other";
+        }
+    }
+
+    static final PluralRules ruleForLocale(Locale locale) {
+        String lang = locale.getLanguage();
+        if ("cs".equals(lang)) {
+            if (cs == null) cs = new cs();
+            return cs;
+        }
+        else {
+            if (en == null) en = new en();
+            return en;
+        }
+    }
+
+    private static PluralRules cs;
+    private static class cs extends PluralRules {
+        int quantityForNumber(int n) {
+            if (n == 1) {
+                return QUANTITY_ONE;
+            }
+            else if (n >= 2 && n <= 4) {
+                return QUANTITY_FEW;
+            }
+            else {
+                return QUANTITY_OTHER;
+            }
+        }
+    }
+
+    private static PluralRules en;
+    private static class en extends PluralRules {
+        int quantityForNumber(int n) {
+            if (n == 1) {
+                return QUANTITY_ONE;
+            }
+            else {
+                return QUANTITY_OTHER;
+            }
+        }
+    }
+}
+
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
new file mode 100644
index 0000000..1a963f6
--- /dev/null
+++ b/core/java/android/content/res/Resources.java
@@ -0,0 +1,1890 @@
+/*
+ * 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 com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.graphics.Movie;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.TypedValue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+
+/**
+ * Class for accessing an application's resources.  This sits on top of the
+ * asset manager of the application (accessible through getAssets()) and
+ * provides a higher-level API for getting typed data from the assets.
+ */
+public class Resources {
+    static final String TAG = "Resources";
+    private static final boolean DEBUG_LOAD = false;
+    private static final boolean DEBUG_CONFIG = false;
+    private static final boolean TRACE_FOR_PRELOAD = false;
+
+    private static final int sSdkVersion = SystemProperties.getInt(
+            "ro.build.version.sdk", 0);
+    private static final Object mSync = new Object();
+    private static Resources mSystem = null;
+    
+    // Information about preloaded resources.  Note that they are not
+    // protected by a lock, because while preloading in zygote we are all
+    // single-threaded, and after that these are immutable.
+    private static final SparseArray<Drawable.ConstantState> mPreloadedDrawables
+            = new SparseArray<Drawable.ConstantState>();
+    private static final SparseArray<ColorStateList> mPreloadedColorStateLists
+            = new SparseArray<ColorStateList>();
+    private static boolean mPreloaded;
+
+    /*package*/ final TypedValue mTmpValue = new TypedValue();
+
+    // These are protected by the mTmpValue lock.
+    private final SparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache
+            = new SparseArray<WeakReference<Drawable.ConstantState> >();
+    private final SparseArray<WeakReference<ColorStateList> > mColorStateListCache
+            = new SparseArray<WeakReference<ColorStateList> >();
+    private boolean mPreloading;
+
+    /*package*/ TypedArray mCachedStyledAttributes = null;
+
+    private int mLastCachedXmlBlockIndex = -1;
+    private final int[] mCachedXmlBlockIds = { 0, 0, 0, 0 };
+    private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[4];
+
+    /*package*/ final AssetManager mAssets;
+    private final Configuration mConfiguration = new Configuration();
+    /*package*/ final DisplayMetrics mMetrics = new DisplayMetrics();
+    PluralRules mPluralRule;
+
+    /**
+     * This exception is thrown by the resource APIs when a requested resource
+     * can not be found.
+     */
+    public static class NotFoundException extends RuntimeException {
+        public NotFoundException() {
+        }
+
+        public NotFoundException(String name) {
+            super(name);
+        }
+    }
+
+    /**
+     * Create a new Resources object on top of an existing set of assets in an
+     * AssetManager.
+     * 
+     * @param assets Previously created AssetManager. 
+     * @param metrics Current display metrics to consider when 
+     *                selecting/computing resource values.
+     * @param config Desired device configuration to consider when 
+     *               selecting/computing resource values (optional).
+     */
+    public Resources(AssetManager assets, DisplayMetrics metrics,
+            Configuration config) {
+        mAssets = assets;
+        mConfiguration.setToDefaults();
+        mMetrics.setToDefaults();
+        updateConfiguration(config, metrics);
+        assets.ensureStringBlocks();
+    }
+
+    /**
+     * Return a global shared Resources object that provides access to only
+     * system resources (no application resources), and is not configured for 
+     * the current screen (can not use dimension units, does not change based 
+     * on orientation, etc). 
+     */
+    public static Resources getSystem() {
+        synchronized (mSync) {
+            Resources ret = mSystem;
+            if (ret == null) {
+                ret = new Resources();
+                mSystem = ret;
+            }
+
+            return ret;
+        }
+    }
+
+    /**
+     * Return the string value associated with a particular resource ID.  The
+     * returned object will be a String if this is a plain string; it will be
+     * some other type of CharSequence if it is styled.
+     * {@more}
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return CharSequence The string data associated with the resource, plus
+     *         possibly styled text information.
+     */
+    public CharSequence getText(int id) throws NotFoundException {
+        CharSequence res = mAssets.getResourceText(id);
+        if (res != null) {
+            return res;
+        }
+        throw new NotFoundException("String resource ID #0x"
+                                    + Integer.toHexString(id));
+    }
+
+    /**
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return CharSequence The string data associated with the resource, plus
+     *         possibly styled text information.
+     */
+    public CharSequence getQuantityText(int id, int quantity) throws NotFoundException {
+        PluralRules rule = getPluralRule();
+        CharSequence res = mAssets.getResourceBagText(id, rule.attrForNumber(quantity));
+        if (res != null) {
+            return res;
+        }
+        res = mAssets.getResourceBagText(id, PluralRules.ID_OTHER);
+        if (res != null) {
+            return res;
+        }
+        throw new NotFoundException("Plural resource ID #0x" + Integer.toHexString(id)
+                + " quantity=" + quantity
+                + " item=" + PluralRules.stringForQuantity(rule.quantityForNumber(quantity)));
+    }
+
+    private PluralRules getPluralRule() {
+        synchronized (mSync) {
+            if (mPluralRule == null) {
+                mPluralRule = PluralRules.ruleForLocale(mConfiguration.locale);
+            }
+            return mPluralRule;
+        }
+    }
+
+    /**
+     * Return the string value associated with a particular resource ID.  It
+     * will be stripped of any styled text information.
+     * {@more}
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return String The string data associated with the resource,
+     * stripped of styled text information.
+     */
+    public String getString(int id) throws NotFoundException {
+        CharSequence res = getText(id);
+        if (res != null) {
+            return res.toString();
+        }
+        throw new NotFoundException("String resource ID #0x"
+                                    + Integer.toHexString(id));
+    }
+
+
+    /**
+     * Return the string value associated with a particular resource ID,
+     * substituting the format arguments as defined in {@link java.util.Formatter}
+     * and {@link java.lang.String#format}. It will be stripped of any styled text
+     * information.
+     * {@more}
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *           
+     * @param formatArgs The format arguments that will be used for substitution.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return String The string data associated with the resource,
+     * stripped of styled text information.
+     */
+    public String getString(int id, Object... formatArgs) throws NotFoundException {
+        String raw = getString(id);
+        return String.format(mConfiguration.locale, raw, formatArgs);
+    }
+
+    /**
+     * Return the string value associated with a particular resource ID for a particular
+     * numerical quantity, substituting the format arguments as defined in
+     * {@link java.util.Formatter} and {@link java.lang.String#format}. It will be
+     * stripped of any styled text information.
+     * {@more}
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @param quantity The number used to get the correct string for the current language's
+     *           plural rules.
+     * @param formatArgs The format arguments that will be used for substitution.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return String The string data associated with the resource,
+     * stripped of styled text information.
+     */
+    public String getQuantityString(int id, int quantity, Object... formatArgs)
+            throws NotFoundException {
+        String raw = getQuantityText(id, quantity).toString();
+        return String.format(mConfiguration.locale, raw, formatArgs);
+    }
+
+    /**
+     * Return the string value associated with a particular resource ID for a particular
+     * numerical quantity.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @param quantity The number used to get the correct string for the current language's
+     *           plural rules.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return String The string data associated with the resource,
+     * stripped of styled text information.
+     */
+    public String getQuantityString(int id, int quantity) throws NotFoundException {
+        return getQuantityText(id, quantity).toString();
+    }
+
+    /**
+     * Return the string value associated with a particular resource ID.  The
+     * returned object will be a String if this is a plain string; it will be
+     * some other type of CharSequence if it is styled.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * 
+     * @param def The default CharSequence to return.
+     *
+     * @return CharSequence The string data associated with the resource, plus
+     *         possibly styled text information, or def if id is 0 or not found.
+     */
+    public CharSequence getText(int id, CharSequence def) {
+        CharSequence res = id != 0 ? mAssets.getResourceText(id) : null;
+        return res != null ? res : def;
+    }
+
+    /**
+     * Return the styled text array associated with a particular resource ID.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return The styled text array associated with the resource.
+     */
+    public CharSequence[] getTextArray(int id) throws NotFoundException {
+        CharSequence[] res = mAssets.getResourceTextArray(id);
+        if (res != null) {
+            return res;
+        }
+        throw new NotFoundException("Text array resource ID #0x"
+                                    + Integer.toHexString(id));
+    }
+
+    /**
+     * Return the string array associated with a particular resource ID.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return The string array associated with the resource.
+     */
+    public String[] getStringArray(int id) throws NotFoundException {
+        String[] res = mAssets.getResourceStringArray(id);
+        if (res != null) {
+            return res;
+        }
+        throw new NotFoundException("String array resource ID #0x"
+                                    + Integer.toHexString(id));
+    }
+
+    /**
+     * Return the int array associated with a particular resource ID.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return The int array associated with the resource.
+     */
+    public int[] getIntArray(int id) throws NotFoundException {
+        int[] res = mAssets.getArrayIntResource(id);
+        if (res != null) {
+            return res;
+        }
+        throw new NotFoundException("Int array resource ID #0x"
+                                    + Integer.toHexString(id));
+    }
+
+    /**
+     * Return an array of heterogeneous values.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return Returns a TypedArray holding an array of the array values.
+     * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+     * when done with it.
+     */
+    public TypedArray obtainTypedArray(int id) throws NotFoundException {
+        int len = mAssets.getArraySize(id);
+        if (len < 0) {
+            throw new NotFoundException("Array resource ID #0x"
+                                        + Integer.toHexString(id));
+        }
+        
+        TypedArray array = getCachedStyledAttributes(len);
+        array.mLength = mAssets.retrieveArray(id, array.mData);
+        array.mIndices[0] = 0;
+        
+        return array;
+    }
+
+    /**
+     * Retrieve a dimensional for a particular resource ID.  Unit 
+     * conversions are based on the current {@link DisplayMetrics} associated
+     * with the resources.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * 
+     * @return Resource dimension value multiplied by the appropriate 
+     * metric.
+     * 
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @see #getDimensionPixelOffset
+     * @see #getDimensionPixelSize
+     */
+    public float getDimension(int id) throws NotFoundException {
+        synchronized (mTmpValue) {
+            TypedValue value = mTmpValue;
+            getValue(id, value, true);
+            if (value.type == TypedValue.TYPE_DIMENSION) {
+                return TypedValue.complexToDimension(value.data, mMetrics);
+            }
+            throw new NotFoundException(
+                    "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+                    + Integer.toHexString(value.type) + " is not valid");
+        }
+    }
+
+    /**
+     * Retrieve a dimensional for a particular resource ID for use
+     * as an offset in raw pixels.  This is the same as
+     * {@link #getDimension}, except the returned value is converted to
+     * integer pixels for you.  An offset conversion involves simply
+     * truncating the base value to an integer.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * 
+     * @return Resource dimension value multiplied by the appropriate 
+     * metric and truncated to integer pixels.
+     * 
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @see #getDimension
+     * @see #getDimensionPixelSize
+     */
+    public int getDimensionPixelOffset(int id) throws NotFoundException {
+        synchronized (mTmpValue) {
+            TypedValue value = mTmpValue;
+            getValue(id, value, true);
+            if (value.type == TypedValue.TYPE_DIMENSION) {
+                return TypedValue.complexToDimensionPixelOffset(
+                        value.data, mMetrics);
+            }
+            throw new NotFoundException(
+                    "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+                    + Integer.toHexString(value.type) + " is not valid");
+        }
+    }
+
+    /**
+     * Retrieve a dimensional for a particular resource ID for use
+     * as a size in raw pixels.  This is the same as
+     * {@link #getDimension}, except the returned value is converted to
+     * integer pixels for use as a size.  A size conversion involves
+     * rounding the base value, and ensuring that a non-zero base value
+     * is at least one pixel in size.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * 
+     * @return Resource dimension value multiplied by the appropriate 
+     * metric and truncated to integer pixels.
+     *  
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @see #getDimension
+     * @see #getDimensionPixelOffset
+     */
+    public int getDimensionPixelSize(int id) throws NotFoundException {
+        synchronized (mTmpValue) {
+            TypedValue value = mTmpValue;
+            getValue(id, value, true);
+            if (value.type == TypedValue.TYPE_DIMENSION) {
+                return TypedValue.complexToDimensionPixelSize(
+                        value.data, mMetrics);
+            }
+            throw new NotFoundException(
+                    "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+                    + Integer.toHexString(value.type) + " is not valid");
+        }
+    }
+
+    /**
+     * Retrieve a fractional unit for a particular resource ID.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @param base The base value of this fraction.  In other words, a 
+     *             standard fraction is multiplied by this value.
+     * @param pbase The parent base value of this fraction.  In other 
+     *             words, a parent fraction (nn%p) is multiplied by this
+     *             value.
+     * 
+     * @return Attribute fractional value multiplied by the appropriate 
+     * base value.
+     *  
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     */
+    public float getFraction(int id, int base, int pbase) {
+        synchronized (mTmpValue) {
+            TypedValue value = mTmpValue;
+            getValue(id, value, true);
+            if (value.type == TypedValue.TYPE_FRACTION) {
+                return TypedValue.complexToFraction(value.data, base, pbase);
+            }
+            throw new NotFoundException(
+                    "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+                    + Integer.toHexString(value.type) + " is not valid");
+        }
+    }
+    
+    /**
+     * Return a drawable object associated with a particular resource ID.
+     * Various types of objects will be returned depending on the underlying
+     * resource -- for example, a solid color, PNG image, scalable image, etc.
+     * The Drawable API hides these implementation details.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     * @return Drawable An object that can be used to draw this resource.
+     */
+    public Drawable getDrawable(int id) throws NotFoundException {
+        synchronized (mTmpValue) {
+            TypedValue value = mTmpValue;
+            getValue(id, value, true);
+            return loadDrawable(value, id);
+        }
+    }
+
+    /**
+     * Return a movie object associated with the particular resource ID.
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     */
+    public Movie getMovie(int id) throws NotFoundException {
+        InputStream is = openRawResource(id);
+        Movie movie = Movie.decodeStream(is);
+        try {
+            is.close();
+        }
+        catch (java.io.IOException e) {
+            // don't care, since the return value is valid
+        }
+        return movie;
+    }
+
+    /**
+     * Return a color integer associated with a particular resource ID.
+     * If the resource holds a complex
+     * {@link android.content.res.ColorStateList}, then the default color from
+     * the set is returned.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return Returns a single color value in the form 0xAARRGGBB.
+     */
+    public int getColor(int id) throws NotFoundException {
+        synchronized (mTmpValue) {
+            TypedValue value = mTmpValue;
+            getValue(id, value, true);
+            if (value.type >= TypedValue.TYPE_FIRST_INT
+                && value.type <= TypedValue.TYPE_LAST_INT) {
+                return value.data;
+            } else if (value.type == TypedValue.TYPE_STRING) {
+                ColorStateList csl = loadColorStateList(mTmpValue, id);
+                return csl.getDefaultColor();
+            }
+            throw new NotFoundException(
+                "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+                + Integer.toHexString(value.type) + " is not valid");
+        }
+    }
+
+    /**
+     * Return a color state list associated with a particular resource ID.  The
+     * resource may contain either a single raw color value, or a complex
+     * {@link android.content.res.ColorStateList} holding multiple possible colors.
+     *
+     * @param id The desired resource identifier of a {@link ColorStateList},
+     *        as generated by the aapt tool. This integer encodes the package, type, and resource
+     *        entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return Returns a ColorStateList object containing either a single
+     * solid color or multiple colors that can be selected based on a state.
+     */
+    public ColorStateList getColorStateList(int id) throws NotFoundException {
+        synchronized (mTmpValue) {
+            TypedValue value = mTmpValue;
+            getValue(id, value, true);
+            return loadColorStateList(value, id);
+        }
+    }
+
+    /**
+     * Return a boolean associated with a particular resource ID.  This can be
+     * used with any integral resource value, and will return true if it is
+     * non-zero.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return Returns the boolean value contained in the resource.
+     */
+    public boolean getBoolean(int id) throws NotFoundException {
+        synchronized (mTmpValue) {
+            TypedValue value = mTmpValue;
+            getValue(id, value, true);
+            if (value.type >= TypedValue.TYPE_FIRST_INT
+                && value.type <= TypedValue.TYPE_LAST_INT) {
+                return value.data != 0;
+            }
+            throw new NotFoundException(
+                "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+                + Integer.toHexString(value.type) + " is not valid");
+        }
+    }
+
+    /**
+     * Return an integer associated with a particular resource ID.
+     *
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @return Returns the integer value contained in the resource.
+     */
+    public int getInteger(int id) throws NotFoundException {
+        synchronized (mTmpValue) {
+            TypedValue value = mTmpValue;
+            getValue(id, value, true);
+            if (value.type >= TypedValue.TYPE_FIRST_INT
+                && value.type <= TypedValue.TYPE_LAST_INT) {
+                return value.data;
+            }
+            throw new NotFoundException(
+                "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+                + Integer.toHexString(value.type) + " is not valid");
+        }
+    }
+
+    /**
+     * Return an XmlResourceParser through which you can read a view layout
+     * description for the given resource ID.  This parser has limited
+     * functionality -- in particular, you can't change its input, and only
+     * the high-level events are available.
+     * 
+     * <p>This function is really a simple wrapper for calling
+     * {@link #getXml} with a layout resource.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     * @return A new parser object through which you can read
+     *         the XML data.
+     *         
+     * @see #getXml
+     */
+    public XmlResourceParser getLayout(int id) throws NotFoundException {
+        return loadXmlResourceParser(id, "layout");
+    }
+
+    /**
+     * Return an XmlResourceParser through which you can read an animation
+     * description for the given resource ID.  This parser has limited
+     * functionality -- in particular, you can't change its input, and only
+     * the high-level events are available.
+     * 
+     * <p>This function is really a simple wrapper for calling
+     * {@link #getXml} with an animation resource.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     * @return A new parser object through which you can read
+     *         the XML data.
+     *         
+     * @see #getXml
+     */
+    public XmlResourceParser getAnimation(int id) throws NotFoundException {
+        return loadXmlResourceParser(id, "anim");
+    }
+
+    /**
+     * Return an XmlResourceParser through which you can read a generic XML
+     * resource for the given resource ID.
+     * 
+     * <p>The XmlPullParser implementation returned here has some limited
+     * functionality.  In particular, you can't change its input, and only
+     * high-level parsing events are available (since the document was
+     * pre-parsed for you at build time, which involved merging text and
+     * stripping comments).
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     * @return A new parser object through which you can read
+     *         the XML data.
+     *         
+     * @see android.util.AttributeSet
+     */
+    public XmlResourceParser getXml(int id) throws NotFoundException {
+        return loadXmlResourceParser(id, "xml");
+    }
+
+    /**
+     * Open a data stream for reading a raw resource.  This can only be used
+     * with resources whose value is the name of an asset files -- that is, it can be
+     * used to open drawable, sound, and raw resources; it will fail on string
+     * and color resources.
+     * 
+     * @param id The resource identifier to open, as generated by the appt
+     *           tool.
+     * 
+     * @return InputStream Access to the resource data.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     */
+    public InputStream openRawResource(int id) throws NotFoundException {
+        synchronized (mTmpValue) {
+            return openRawResource(id, mTmpValue);
+        }
+    }
+
+    /**
+     * Open a data stream for reading a raw resource.  This can only be used
+     * with resources whose value is the name of an asset files -- that is, it can be
+     * used to open drawable, sound, and raw resources; it will fail on string
+     * and color resources.
+     *
+     * @param id The resource identifier to open, as generated by the appt tool.
+     * @param value The TypedValue object to hold the resource information.
+     *
+     * @return InputStream Access to the resource data.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     * @hide Pending API council approval
+     */
+    public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
+        getValue(id, value, true);
+
+        try {
+            return mAssets.openNonAsset(value.assetCookie, value.string.toString(),
+                    AssetManager.ACCESS_STREAMING);
+        } catch (Exception e) {
+            NotFoundException rnf = new NotFoundException("File " + value.string.toString() +
+                    " from drawable resource ID #0x" + Integer.toHexString(id));
+            rnf.initCause(e);
+            throw rnf;
+        }
+    }
+
+    /**
+     * Open a file descriptor for reading a raw resource.  This can only be used
+     * with resources whose value is the name of an asset files -- that is, it can be
+     * used to open drawable, sound, and raw resources; it will fail on string
+     * and color resources.
+     * 
+     * <p>This function only works for resources that are stored in the package
+     * as uncompressed data, which typically includes things like mp3 files
+     * and png images.
+     * 
+     * @param id The resource identifier to open, as generated by the appt
+     *           tool.
+     * 
+     * @return AssetFileDescriptor A new file descriptor you can use to read
+     * the resource.  This includes the file descriptor itself, as well as the
+     * offset and length of data where the resource appears in the file.  A
+     * null is returned if the file exists but is compressed.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     */
+    public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
+        synchronized (mTmpValue) {
+            TypedValue value = mTmpValue;
+            getValue(id, value, true);
+
+            try {
+                return mAssets.openNonAssetFd(
+                    value.assetCookie, value.string.toString());
+            } catch (Exception e) {
+                NotFoundException rnf = new NotFoundException(
+                    "File " + value.string.toString()
+                    + " from drawable resource ID #0x"
+                    + Integer.toHexString(id));
+                rnf.initCause(e);
+                throw rnf;
+            }
+
+        }
+    }
+
+    /**
+     * Return the raw data associated with a particular resource ID.
+     * 
+     * @param id The desired resource identifier, as generated by the aapt
+     *           tool. This integer encodes the package, type, and resource
+     *           entry. The value 0 is an invalid identifier.
+     * @param outValue Object in which to place the resource data.
+     * @param resolveRefs If true, a resource that is a reference to another
+     *                    resource will be followed so that you receive the
+     *                    actual final resource data.  If false, the TypedValue
+     *                    will be filled in with the reference itself.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     */
+    public void getValue(int id, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        boolean found = mAssets.getResourceValue(id, outValue, resolveRefs);
+        if (found) {
+            return;
+        }
+        throw new NotFoundException("Resource ID #0x"
+                                    + Integer.toHexString(id));
+    }
+
+    /**
+     * Return the raw data associated with a particular resource ID.
+     * See getIdentifier() for information on how names are mapped to resource
+     * IDs, and getString(int) for information on how string resources are
+     * retrieved.
+     * 
+     * <p>Note: use of this function is discouraged.  It is much more
+     * efficient to retrieve resources by identifier than by name.
+     * 
+     * @param name The name of the desired resource.  This is passed to
+     *             getIdentifier() with a default type of "string".
+     * @param outValue Object in which to place the resource data.
+     * @param resolveRefs If true, a resource that is a reference to another
+     *                    resource will be followed so that you receive the
+     *                    actual final resource data.  If false, the TypedValue
+     *                    will be filled in with the reference itself.
+     *
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     *
+     */
+    public void getValue(String name, TypedValue outValue, boolean resolveRefs)
+            throws NotFoundException {
+        int id = getIdentifier(name, "string", null);
+        if (id != 0) {
+            getValue(id, outValue, resolveRefs);
+            return;
+        }
+        throw new NotFoundException("String resource name " + name);
+    }
+
+    /**
+     * This class holds the current attribute values for a particular theme.
+     * In other words, a Theme is a set of values for resource attributes;
+     * these are used in conjunction with {@link TypedArray}
+     * to resolve the final value for an attribute.
+     * 
+     * <p>The Theme's attributes come into play in two ways: (1) a styled
+     * attribute can explicit reference a value in the theme through the
+     * "?themeAttribute" syntax; (2) if no value has been defined for a
+     * particular styled attribute, as a last resort we will try to find that
+     * attribute's value in the Theme.
+     * 
+     * <p>You will normally use the {@link #obtainStyledAttributes} APIs to
+     * retrieve XML attributes with style and theme information applied.
+     */
+    public final class Theme {
+        /**
+         * Place new attribute values into the theme.  The style resource
+         * specified by <var>resid</var> will be retrieved from this Theme's
+         * resources, its values placed into the Theme object.
+         * 
+         * <p>The semantics of this function depends on the <var>force</var>
+         * argument:  If false, only values that are not already defined in
+         * the theme will be copied from the system resource; otherwise, if
+         * any of the style's attributes are already defined in the theme, the
+         * current values in the theme will be overwritten.
+         * 
+         * @param resid The resource ID of a style resource from which to
+         *              obtain attribute values.
+         * @param force If true, values in the style resource will always be
+         *              used in the theme; otherwise, they will only be used
+         *              if not already defined in the theme.
+         */
+        public void applyStyle(int resid, boolean force) {
+            AssetManager.applyThemeStyle(mTheme, resid, force);
+        }
+
+        /**
+         * Set this theme to hold the same contents as the theme
+         * <var>other</var>.  If both of these themes are from the same
+         * Resources object, they will be identical after this function
+         * returns.  If they are from different Resources, only the resources
+         * they have in common will be set in this theme.
+         * 
+         * @param other The existing Theme to copy from.
+         */
+        public void setTo(Theme other) {
+            AssetManager.copyTheme(mTheme, other.mTheme);
+        }
+
+        /**
+         * Return a StyledAttributes holding the values defined by
+         * <var>Theme</var> which are listed in <var>attrs</var>.
+         * 
+         * <p>Be sure to call StyledAttributes.recycle() when you are done with
+         * the array.
+         * 
+         * @param attrs The desired attributes.
+         *
+         * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+         * 
+         * @return Returns a TypedArray holding an array of the attribute values.
+         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+         * when done with it.
+         * 
+         * @see Resources#obtainAttributes
+         * @see #obtainStyledAttributes(int, int[])
+         * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
+         */
+        public TypedArray obtainStyledAttributes(int[] attrs) {
+            int len = attrs.length;
+            TypedArray array = getCachedStyledAttributes(len);
+            array.mRsrcs = attrs;
+            AssetManager.applyStyle(mTheme, 0, 0, 0, attrs,
+                    array.mData, array.mIndices);
+            return array;
+        }
+
+        /**
+         * Return a StyledAttributes holding the values defined by the style
+         * resource <var>resid</var> which are listed in <var>attrs</var>.
+         * 
+         * <p>Be sure to call StyledAttributes.recycle() when you are done with
+         * the array.
+         * 
+         * @param resid The desired style resource.
+         * @param attrs The desired attributes in the style.
+         * 
+         * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+         * 
+         * @return Returns a TypedArray holding an array of the attribute values.
+         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+         * when done with it.
+         * 
+         * @see Resources#obtainAttributes
+         * @see #obtainStyledAttributes(int[])
+         * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
+         */
+        public TypedArray obtainStyledAttributes(int resid, int[] attrs)
+                throws NotFoundException {
+            int len = attrs.length;
+            TypedArray array = getCachedStyledAttributes(len);
+            array.mRsrcs = attrs;
+
+            AssetManager.applyStyle(mTheme, 0, resid, 0, attrs,
+                    array.mData, array.mIndices);
+            if (false) {
+                int[] data = array.mData;
+                
+                System.out.println("**********************************************************");
+                System.out.println("**********************************************************");
+                System.out.println("**********************************************************");
+                System.out.println("Attributes:");
+                String s = "  Attrs:";
+                int i;
+                for (i=0; i<attrs.length; i++) {
+                    s = s + " 0x" + Integer.toHexString(attrs[i]);
+                }
+                System.out.println(s);
+                s = "  Found:";
+                TypedValue value = new TypedValue();
+                for (i=0; i<attrs.length; i++) {
+                    int d = i*AssetManager.STYLE_NUM_ENTRIES;
+                    value.type = data[d+AssetManager.STYLE_TYPE];
+                    value.data = data[d+AssetManager.STYLE_DATA];
+                    value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
+                    value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
+                    s = s + " 0x" + Integer.toHexString(attrs[i])
+                        + "=" + value;
+                }
+                System.out.println(s);
+            }
+            return array;
+        }
+
+        /**
+         * Return a StyledAttributes holding the attribute values in
+         * <var>set</var>
+         * that are listed in <var>attrs</var>.  In addition, if the given
+         * AttributeSet specifies a style class (through the "style" attribute),
+         * that style will be applied on top of the base attributes it defines.
+         * 
+         * <p>Be sure to call StyledAttributes.recycle() when you are done with
+         * the array.
+         * 
+         * <p>When determining the final value of a particular attribute, there
+         * are four inputs that come into play:</p>
+         * 
+         * <ol>
+         *     <li> Any attribute values in the given AttributeSet.
+         *     <li> The style resource specified in the AttributeSet (named
+         *     "style").
+         *     <li> The default style specified by <var>defStyleAttr</var> and
+         *     <var>defStyleRes</var>
+         *     <li> The base values in this theme.
+         * </ol>
+         * 
+         * <p>Each of these inputs is considered in-order, with the first listed
+         * taking precedence over the following ones.  In other words, if in the
+         * AttributeSet you have supplied <code>&lt;Button
+         * textColor="#ff000000"&gt;</code>, then the button's text will
+         * <em>always</em> be black, regardless of what is specified in any of
+         * the styles.
+         * 
+         * @param set The base set of attribute values.  May be null.
+         * @param attrs The desired attributes to be retrieved.
+         * @param defStyleAttr An attribute in the current theme that contains a
+         *                     reference to a style resource that supplies
+         *                     defaults values for the StyledAttributes.  Can be
+         *                     0 to not look for defaults.
+         * @param defStyleRes A resource identifier of a style resource that
+         *                    supplies default values for the StyledAttributes,
+         *                    used only if defStyleAttr is 0 or can not be found
+         *                    in the theme.  Can be 0 to not look for defaults.
+         * 
+         * @return Returns a TypedArray holding an array of the attribute values.
+         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+         * when done with it.
+         * 
+         * @see Resources#obtainAttributes
+         * @see #obtainStyledAttributes(int[])
+         * @see #obtainStyledAttributes(int, int[])
+         */
+        public TypedArray obtainStyledAttributes(AttributeSet set,
+                int[] attrs, int defStyleAttr, int defStyleRes) {
+            int len = attrs.length;
+            TypedArray array = getCachedStyledAttributes(len);
+
+            // XXX note that for now we only work with compiled XML files.
+            // To support generic XML files we will need to manually parse
+            // out the attributes from the XML file (applying type information
+            // contained in the resources and such).
+            XmlBlock.Parser parser = (XmlBlock.Parser)set;
+            AssetManager.applyStyle(
+                mTheme, defStyleAttr, defStyleRes,
+                parser != null ? parser.mParseState : 0, attrs,
+                        array.mData, array.mIndices);
+
+            array.mRsrcs = attrs;
+            array.mXml = parser;
+
+            if (false) {
+                int[] data = array.mData;
+                
+                System.out.println("Attributes:");
+                String s = "  Attrs:";
+                int i;
+                for (i=0; i<set.getAttributeCount(); i++) {
+                    s = s + " " + set.getAttributeName(i);
+                    int id = set.getAttributeNameResource(i);
+                    if (id != 0) {
+                        s = s + "(0x" + Integer.toHexString(id) + ")";
+                    }
+                    s = s + "=" + set.getAttributeValue(i);
+                }
+                System.out.println(s);
+                s = "  Found:";
+                TypedValue value = new TypedValue();
+                for (i=0; i<attrs.length; i++) {
+                    int d = i*AssetManager.STYLE_NUM_ENTRIES;
+                    value.type = data[d+AssetManager.STYLE_TYPE];
+                    value.data = data[d+AssetManager.STYLE_DATA];
+                    value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
+                    value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
+                    s = s + " 0x" + Integer.toHexString(attrs[i])
+                        + "=" + value;
+                }
+                System.out.println(s);
+            }
+
+            return array;
+        }
+
+        /**
+         * Retrieve the value of an attribute in the Theme.  The contents of
+         * <var>outValue</var> are ultimately filled in by
+         * {@link Resources#getValue}.
+         * 
+         * @param resid The resource identifier of the desired theme
+         *              attribute.
+         * @param outValue Filled in with the ultimate resource value supplied
+         *                 by the attribute.
+         * @param resolveRefs If true, resource references will be walked; if
+         *                    false, <var>outValue</var> may be a
+         *                    TYPE_REFERENCE.  In either case, it will never
+         *                    be a TYPE_ATTRIBUTE.
+         * 
+         * @return boolean Returns true if the attribute was found and
+         *         <var>outValue</var> is valid, else false.
+         */
+        public boolean resolveAttribute(int resid, TypedValue outValue,
+                boolean resolveRefs) {
+            boolean got = mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);
+            if (false) {
+                System.out.println(
+                    "resolveAttribute #" + Integer.toHexString(resid)
+                    + " got=" + got + ", type=0x" + Integer.toHexString(outValue.type)
+                    + ", data=0x" + Integer.toHexString(outValue.data));
+            }
+            return got;
+        }
+
+        /**
+         * Print contents of this theme out to the log.  For debugging only.
+         * 
+         * @param priority The log priority to use.
+         * @param tag The log tag to use.
+         * @param prefix Text to prefix each line printed.
+         */
+        public void dump(int priority, String tag, String prefix) {
+            AssetManager.dumpTheme(mTheme, priority, tag, prefix);
+        }
+        
+        protected void finalize() throws Throwable {
+            super.finalize();
+            mAssets.releaseTheme(mTheme);
+        }
+
+        /*package*/ Theme() {
+            mAssets = Resources.this.mAssets;
+            mTheme = mAssets.createTheme();
+        }
+
+        private final AssetManager mAssets;
+        private final int mTheme;
+    }
+
+    /**
+     * Generate a new Theme object for this set of Resources.  It initially
+     * starts out empty.
+     * 
+     * @return Theme The newly created Theme container.
+     */
+    public final Theme newTheme() {
+        return new Theme();
+    }
+
+    /**
+     * Retrieve a set of basic attribute values from an AttributeSet, not
+     * performing styling of them using a theme and/or style resources.
+     * 
+     * @param set The current attribute values to retrieve.
+     * @param attrs The specific attributes to be retrieved.
+     * @return Returns a TypedArray holding an array of the attribute values.
+     * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+     * when done with it.
+     * 
+     * @see Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
+     */
+    public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
+        int len = attrs.length;
+        TypedArray array = getCachedStyledAttributes(len);
+
+        // XXX note that for now we only work with compiled XML files.
+        // To support generic XML files we will need to manually parse
+        // out the attributes from the XML file (applying type information
+        // contained in the resources and such).
+        XmlBlock.Parser parser = (XmlBlock.Parser)set;
+        mAssets.retrieveAttributes(parser.mParseState, attrs,
+                array.mData, array.mIndices);
+
+        array.mRsrcs = attrs;
+        array.mXml = parser;
+
+        return array;
+    }
+    
+    /**
+     * Store the newly updated configuration.
+     */
+    public void updateConfiguration(Configuration config,
+            DisplayMetrics metrics) {
+        synchronized (mTmpValue) {
+            int configChanges = 0xfffffff;
+            if (config != null) {
+                configChanges = mConfiguration.updateFrom(config);
+            }
+            if (metrics != null) {
+                mMetrics.setTo(metrics);
+            }
+            mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
+            String locale = null;
+            if (mConfiguration.locale != null) {
+                locale = mConfiguration.locale.getLanguage();
+                if (mConfiguration.locale.getCountry() != null) {
+                    locale += "-" + mConfiguration.locale.getCountry();
+                }
+            }
+            int width, height;
+            if (mMetrics.widthPixels >= mMetrics.heightPixels) {
+                width = mMetrics.widthPixels;
+                height = mMetrics.heightPixels;
+            } else {
+                //noinspection SuspiciousNameCombination
+                width = mMetrics.heightPixels;
+                //noinspection SuspiciousNameCombination
+                height = mMetrics.widthPixels;
+            }
+            int keyboardHidden = mConfiguration.keyboardHidden;
+            if (keyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
+                    && mConfiguration.hardKeyboardHidden
+                            == Configuration.HARDKEYBOARDHIDDEN_YES) {
+                keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
+            }
+            mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
+                    locale, mConfiguration.orientation,
+                    mConfiguration.touchscreen,
+                    (int)(mMetrics.density*160), mConfiguration.keyboard,
+                    keyboardHidden, mConfiguration.navigation, width, height,
+                    sSdkVersion);
+            int N = mDrawableCache.size();
+            if (DEBUG_CONFIG) {
+                Log.d(TAG, "Cleaning up drawables config changes: 0x"
+                        + Integer.toHexString(configChanges));
+            }
+            for (int i=0; i<N; i++) {
+                WeakReference<Drawable.ConstantState> ref = mDrawableCache.valueAt(i);
+                if (ref != null) {
+                    Drawable.ConstantState cs = ref.get();
+                    if (cs != null) {
+                        if (Configuration.needNewResources(
+                                configChanges, cs.getChangingConfigurations())) {
+                            if (DEBUG_CONFIG) {
+                                Log.d(TAG, "FLUSHING #0x"
+                                        + Integer.toHexString(mDrawableCache.keyAt(i))
+                                        + " / " + cs + " with changes: 0x"
+                                        + Integer.toHexString(cs.getChangingConfigurations()));
+                            }
+                            mDrawableCache.setValueAt(i, null);
+                        } else if (DEBUG_CONFIG) {
+                            Log.d(TAG, "(Keeping #0x"
+                                    + Integer.toHexString(mDrawableCache.keyAt(i))
+                                    + " / " + cs + " with changes: 0x"
+                                    + Integer.toHexString(cs.getChangingConfigurations())
+                                    + ")");
+                        }
+                    }
+                }
+            }
+            mDrawableCache.clear();
+            mColorStateListCache.clear();
+            flushLayoutCache();
+        }
+        synchronized (mSync) {
+            if (mPluralRule != null) {
+                mPluralRule = PluralRules.ruleForLocale(config.locale);
+            }
+        }
+    }
+
+    /**
+     * Update the system resources configuration if they have previously
+     * been initialized.
+     *
+     * @hide
+     */
+    public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics) {
+        if (mSystem != null) {
+            mSystem.updateConfiguration(config, metrics);
+            //Log.i(TAG, "Updated system resources " + mSystem
+            //        + ": " + mSystem.getConfiguration());
+        }
+    }
+
+    /**
+     * Return the current display metrics that are in effect for this resource 
+     * object.  The returned object should be treated as read-only.
+     * 
+     * @return The resource's current display metrics. 
+     */
+    public DisplayMetrics getDisplayMetrics() {
+        return mMetrics;
+    }
+
+    /**
+     * Return the current configuration that is in effect for this resource 
+     * object.  The returned object should be treated as read-only.
+     * 
+     * @return The resource's current configuration. 
+     */
+    public Configuration getConfiguration() {
+        return mConfiguration;
+    }
+
+    /**
+     * Return a resource identifier for the given resource name.  A fully
+     * qualified resource name is of the form "package:type/entry".  The first
+     * two components (package and type) are optional if defType and
+     * defPackage, respectively, are specified here.
+     * 
+     * <p>Note: use of this function is discouraged.  It is much more
+     * efficient to retrieve resources by identifier than by name.
+     * 
+     * @param name The name of the desired resource.
+     * @param defType Optional default resource type to find, if "type/" is
+     *                not included in the name.  Can be null to require an
+     *                explicit type.
+     * @param defPackage Optional default package to find, if "package:" is
+     *                   not included in the name.  Can be null to require an
+     *                   explicit package.
+     * 
+     * @return int The associated resource identifier.  Returns 0 if no such
+     *         resource was found.  (0 is not a valid resource ID.)
+     */
+    public int getIdentifier(String name, String defType, String defPackage) {
+        try {
+            return Integer.parseInt(name);
+        } catch (Exception e) {
+            // Ignore
+        }
+        return mAssets.getResourceIdentifier(name, defType, defPackage);
+    }
+
+    /**
+     * Return the full name for a given resource identifier.  This name is
+     * a single string of the form "package:type/entry".
+     * 
+     * @param resid The resource identifier whose name is to be retrieved.
+     * 
+     * @return A string holding the name of the resource.
+     * 
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     * @see #getResourcePackageName
+     * @see #getResourceTypeName
+     * @see #getResourceEntryName
+     */
+    public String getResourceName(int resid) throws NotFoundException {
+        String str = mAssets.getResourceName(resid);
+        if (str != null) return str;
+        throw new NotFoundException("Unable to find resource ID #0x"
+                + Integer.toHexString(resid));
+    }
+    
+    /**
+     * Return the package name for a given resource identifier.
+     * 
+     * @param resid The resource identifier whose package name is to be
+     * retrieved.
+     * 
+     * @return A string holding the package name of the resource.
+     * 
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     * @see #getResourceName
+     */
+    public String getResourcePackageName(int resid) throws NotFoundException {
+        String str = mAssets.getResourcePackageName(resid);
+        if (str != null) return str;
+        throw new NotFoundException("Unable to find resource ID #0x"
+                + Integer.toHexString(resid));
+    }
+    
+    /**
+     * Return the type name for a given resource identifier.
+     * 
+     * @param resid The resource identifier whose type name is to be
+     * retrieved.
+     * 
+     * @return A string holding the type name of the resource.
+     * 
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     * @see #getResourceName
+     */
+    public String getResourceTypeName(int resid) throws NotFoundException {
+        String str = mAssets.getResourceTypeName(resid);
+        if (str != null) return str;
+        throw new NotFoundException("Unable to find resource ID #0x"
+                + Integer.toHexString(resid));
+    }
+    
+    /**
+     * Return the entry name for a given resource identifier.
+     * 
+     * @param resid The resource identifier whose entry name is to be
+     * retrieved.
+     * 
+     * @return A string holding the entry name of the resource.
+     * 
+     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+     * 
+     * @see #getResourceName
+     */
+    public String getResourceEntryName(int resid) throws NotFoundException {
+        String str = mAssets.getResourceEntryName(resid);
+        if (str != null) return str;
+        throw new NotFoundException("Unable to find resource ID #0x"
+                + Integer.toHexString(resid));
+    }
+    
+    /**
+     * Parse a series of {@link android.R.styleable#Extra &lt;extra&gt;} tags from
+     * an XML file.  You call this when you are at the parent tag of the
+     * extra tags, and it return once all of the child tags have been parsed.
+     * This will call {@link #parseBundleExtra} for each extra tag encountered.
+     * 
+     * @param parser The parser from which to retrieve the extras.
+     * @param outBundle A Bundle in which to place all parsed extras.
+     * @throws XmlPullParserException
+     * @throws IOException
+     */
+    public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle)
+            throws XmlPullParserException, IOException {
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+               && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            
+            String nodeName = parser.getName();
+            if (nodeName.equals("extra")) {
+                parseBundleExtra("extra", parser, outBundle);
+                XmlUtils.skipCurrentTag(parser);
+
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }        
+    }
+    
+    /**
+     * Parse a name/value pair out of an XML tag holding that data.  The
+     * AttributeSet must be holding the data defined by
+     * {@link android.R.styleable#Extra}.  The following value types are supported:
+     * <ul>
+     * <li> {@link TypedValue#TYPE_STRING}:
+     * {@link Bundle#putCharSequence Bundle.putCharSequence()}
+     * <li> {@link TypedValue#TYPE_INT_BOOLEAN}:
+     * {@link Bundle#putCharSequence Bundle.putBoolean()}
+     * <li> {@link TypedValue#TYPE_FIRST_INT}-{@link TypedValue#TYPE_LAST_INT}:
+     * {@link Bundle#putCharSequence Bundle.putBoolean()}
+     * <li> {@link TypedValue#TYPE_FLOAT}:
+     * {@link Bundle#putCharSequence Bundle.putFloat()}
+     * </ul>
+     * 
+     * @param tagName The name of the tag these attributes come from; this is
+     * only used for reporting error messages.
+     * @param attrs The attributes from which to retrieve the name/value pair.
+     * @param outBundle The Bundle in which to place the parsed value.
+     * @throws XmlPullParserException If the attributes are not valid.
+     */
+    public void parseBundleExtra(String tagName, AttributeSet attrs,
+            Bundle outBundle) throws XmlPullParserException {
+        TypedArray sa = obtainAttributes(attrs,
+                com.android.internal.R.styleable.Extra);
+
+        String name = sa.getString(
+                com.android.internal.R.styleable.Extra_name);
+        if (name == null) {
+            sa.recycle();
+            throw new XmlPullParserException("<" + tagName
+                    + "> requires an android:name attribute at "
+                    + attrs.getPositionDescription());
+        }
+
+        TypedValue v = sa.peekValue(
+                com.android.internal.R.styleable.Extra_value);
+        if (v != null) {
+            if (v.type == TypedValue.TYPE_STRING) {
+                CharSequence cs = v.coerceToString();
+                outBundle.putCharSequence(name, cs);
+            } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
+                outBundle.putBoolean(name, v.data != 0);
+            } else if (v.type >= TypedValue.TYPE_FIRST_INT
+                    && v.type <= TypedValue.TYPE_LAST_INT) {
+                outBundle.putInt(name, v.data);
+            } else if (v.type == TypedValue.TYPE_FLOAT) {
+                outBundle.putFloat(name, v.getFloat());
+            } else {
+                sa.recycle();
+                throw new XmlPullParserException("<" + tagName
+                        + "> only supports string, integer, float, color, and boolean at "
+                        + attrs.getPositionDescription());
+            }
+        } else {
+            sa.recycle();
+            throw new XmlPullParserException("<" + tagName
+                    + "> requires an android:value or android:resource attribute at "
+                    + attrs.getPositionDescription());
+        }
+
+        sa.recycle();
+    }
+    
+    /**
+     * Retrieve underlying AssetManager storage for these resources.
+     */
+    public final AssetManager getAssets() {
+        return mAssets;
+    }
+
+    /**
+     * Call this to remove all cached loaded layout resources from the
+     * Resources object.  Only intended for use with performance testing
+     * tools.
+     */
+    public final void flushLayoutCache() {
+        synchronized (mCachedXmlBlockIds) {
+            // First see if this block is in our cache.
+            final int num = mCachedXmlBlockIds.length;
+            for (int i=0; i<num; i++) {
+                mCachedXmlBlockIds[i] = -0;
+                XmlBlock oldBlock = mCachedXmlBlocks[i];
+                if (oldBlock != null) {
+                    oldBlock.close();
+                }
+                mCachedXmlBlocks[i] = null;
+            }
+        }
+    }
+
+    /**
+     * Start preloading of resource data using this Resources object.  Only
+     * for use by the zygote process for loading common system resources.
+     * {@hide}
+     */
+    public final void startPreloading() {
+        synchronized (mSync) {
+            if (mPreloaded) {
+                throw new IllegalStateException("Resources already preloaded");
+            }
+            mPreloaded = true;
+            mPreloading = true;
+        }
+    }
+    
+    /**
+     * Called by zygote when it is done preloading resources, to change back
+     * to normal Resources operation.
+     */
+    public final void finishPreloading() {
+        if (mPreloading) {
+            mPreloading = false;
+            flushLayoutCache();
+        }
+    }
+    
+    /*package*/ Drawable loadDrawable(TypedValue value, int id)
+            throws NotFoundException {
+
+        if (TRACE_FOR_PRELOAD) {
+            // Log only framework resources
+            if ((id >>> 24) == 0x1) {
+                final String name = getResourceName(id);
+                if (name != null) android.util.Log.d("PreloadDrawable", name);
+            }
+        }
+
+        final int key = (value.assetCookie << 24) | value.data;
+        Drawable dr = getCachedDrawable(key);
+
+        if (dr != null) {
+            return dr;
+        }
+
+        Drawable.ConstantState cs = mPreloadedDrawables.get(key);
+        if (cs != null) {
+            dr = cs.newDrawable();
+        } else {
+            if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
+                    value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+                dr = new ColorDrawable(value.data);
+            }
+
+            if (dr == null) {
+                if (value.string == null) {
+                    throw new NotFoundException(
+                            "Resource is not a Drawable (color or path): " + value);
+                }
+
+                String file = value.string.toString();
+
+                if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie "
+                        + value.assetCookie + ": " + file);
+
+                if (file.endsWith(".xml")) {
+                    try {
+                        XmlResourceParser rp = loadXmlResourceParser(
+                                file, id, value.assetCookie, "drawable");
+                        dr = Drawable.createFromXml(this, rp);
+                        rp.close();
+                    } catch (Exception e) {
+                        NotFoundException rnf = new NotFoundException(
+                            "File " + file + " from drawable resource ID #0x"
+                            + Integer.toHexString(id));
+                        rnf.initCause(e);
+                        throw rnf;
+                    }
+
+                } else {
+                    try {
+                        InputStream is = mAssets.openNonAsset(
+                                value.assetCookie, file, AssetManager.ACCESS_BUFFER);
+        //                System.out.println("Opened file " + file + ": " + is);
+                        dr = Drawable.createFromResourceStream(this, value, is, file);
+                        is.close();
+        //                System.out.println("Created stream: " + dr);
+                    } catch (Exception e) {
+                        NotFoundException rnf = new NotFoundException(
+                            "File " + file + " from drawable resource ID #0x"
+                            + Integer.toHexString(id));
+                        rnf.initCause(e);
+                        throw rnf;
+                    }
+                }
+            }
+        }
+
+        if (dr != null) {
+            dr.setChangingConfigurations(value.changingConfigurations);
+            cs = dr.getConstantState();
+            if (cs != null) {
+                if (mPreloading) {
+                    mPreloadedDrawables.put(key, cs);
+                } else {
+                    synchronized (mTmpValue) {
+                        //Log.i(TAG, "Saving cached drawable @ #" +
+                        //        Integer.toHexString(key.intValue())
+                        //        + " in " + this + ": " + cs);
+                        mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
+                    }
+                }
+            }
+        }
+
+        return dr;
+    }
+
+    private Drawable getCachedDrawable(int key) {
+        synchronized (mTmpValue) {
+            WeakReference<Drawable.ConstantState> wr = mDrawableCache.get(key);
+            if (wr != null) {   // we have the key
+                Drawable.ConstantState entry = wr.get();
+                if (entry != null) {
+                    //Log.i(TAG, "Returning cached drawable @ #" +
+                    //        Integer.toHexString(((Integer)key).intValue())
+                    //        + " in " + this + ": " + entry);
+                    return entry.newDrawable();
+                }
+                else {  // our entry has been purged
+                    mDrawableCache.delete(key);
+                }
+            }
+        }
+        return null;
+    }
+
+    /*package*/ ColorStateList loadColorStateList(TypedValue value, int id)
+            throws NotFoundException {
+        if (TRACE_FOR_PRELOAD) {
+            // Log only framework resources
+            if ((id >>> 24) == 0x1) {
+                final String name = getResourceName(id);
+                if (name != null) android.util.Log.d("PreloadColorStateList", name);
+            }
+        }
+
+        final int key = (value.assetCookie << 24) | value.data;
+
+        ColorStateList csl;
+
+        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
+                value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+
+            csl = mPreloadedColorStateLists.get(key);
+            if (csl != null) {
+                return csl;
+            }
+
+            csl = ColorStateList.valueOf(value.data);
+            if (mPreloading) {
+                mPreloadedColorStateLists.put(key, csl);
+            }
+
+            return csl;
+        }
+
+        csl = getCachedColorStateList(key);
+        if (csl != null) {
+            return csl;
+        }
+
+        csl = mPreloadedColorStateLists.get(key);
+        if (csl != null) {
+            return csl;
+        }
+
+        if (value.string == null) {
+            throw new NotFoundException(
+                    "Resource is not a ColorStateList (color or path): " + value);
+        }
+        
+        String file = value.string.toString();
+
+        if (file.endsWith(".xml")) {
+            try {
+                XmlResourceParser rp = loadXmlResourceParser(
+                        file, id, value.assetCookie, "colorstatelist"); 
+                csl = ColorStateList.createFromXml(this, rp);
+                rp.close();
+            } catch (Exception e) {
+                NotFoundException rnf = new NotFoundException(
+                    "File " + file + " from color state list resource ID #0x"
+                    + Integer.toHexString(id));
+                rnf.initCause(e);
+                throw rnf;
+            }
+        } else {
+            throw new NotFoundException(
+                    "File " + file + " from drawable resource ID #0x"
+                    + Integer.toHexString(id) + ": .xml extension required");
+        }
+
+        if (csl != null) {
+            if (mPreloading) {
+                mPreloadedColorStateLists.put(key, csl);
+            } else {
+                synchronized (mTmpValue) {
+                    //Log.i(TAG, "Saving cached color state list @ #" +
+                    //        Integer.toHexString(key.intValue())
+                    //        + " in " + this + ": " + csl);
+                    mColorStateListCache.put(
+                        key, new WeakReference<ColorStateList>(csl));
+                }
+            }
+        }
+
+        return csl;
+    }
+
+    private ColorStateList getCachedColorStateList(int key) {
+        synchronized (mTmpValue) {
+            WeakReference<ColorStateList> wr = mColorStateListCache.get(key);
+            if (wr != null) {   // we have the key
+                ColorStateList entry = wr.get();
+                if (entry != null) {
+                    //Log.i(TAG, "Returning cached color state list @ #" +
+                    //        Integer.toHexString(((Integer)key).intValue())
+                    //        + " in " + this + ": " + entry);
+                    return entry;
+                }
+                else {  // our entry has been purged
+                    mColorStateListCache.delete(key);
+                }
+            }
+        }
+        return null;
+    }
+
+    /*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)
+            throws NotFoundException {
+        synchronized (mTmpValue) {
+            TypedValue value = mTmpValue;
+            getValue(id, value, true);
+            if (value.type == TypedValue.TYPE_STRING) {
+                return loadXmlResourceParser(value.string.toString(), id,
+                        value.assetCookie, type);
+            }
+            throw new NotFoundException(
+                    "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+                    + Integer.toHexString(value.type) + " is not valid");
+        }
+    }
+    
+    /*package*/ XmlResourceParser loadXmlResourceParser(String file, int id,
+            int assetCookie, String type) throws NotFoundException {
+        if (id != 0) {
+            try {
+                // These may be compiled...
+                synchronized (mCachedXmlBlockIds) {
+                    // First see if this block is in our cache.
+                    final int num = mCachedXmlBlockIds.length;
+                    for (int i=0; i<num; i++) {
+                        if (mCachedXmlBlockIds[i] == id) {
+                            //System.out.println("**** REUSING XML BLOCK!  id="
+                            //                   + id + ", index=" + i);
+                            return mCachedXmlBlocks[i].newParser();
+                        }
+                    }
+
+                    // Not in the cache, create a new block and put it at
+                    // the next slot in the cache.
+                    XmlBlock block = mAssets.openXmlBlockAsset(
+                            assetCookie, file);
+                    if (block != null) {
+                        int pos = mLastCachedXmlBlockIndex+1;
+                        if (pos >= num) pos = 0;
+                        mLastCachedXmlBlockIndex = pos;
+                        XmlBlock oldBlock = mCachedXmlBlocks[pos];
+                        if (oldBlock != null) {
+                            oldBlock.close();
+                        }
+                        mCachedXmlBlockIds[pos] = id;
+                        mCachedXmlBlocks[pos] = block;
+                        //System.out.println("**** CACHING NEW XML BLOCK!  id="
+                        //                   + id + ", index=" + pos);
+                        return block.newParser();
+                    }
+                }
+            } catch (Exception e) {
+                NotFoundException rnf = new NotFoundException(
+                        "File " + file + " from xml type " + type + " resource ID #0x"
+                        + Integer.toHexString(id));
+                rnf.initCause(e);
+                throw rnf;
+            }
+        }
+
+        throw new NotFoundException(
+                "File " + file + " from xml type " + type + " resource ID #0x"
+                + Integer.toHexString(id));
+    }
+
+    private TypedArray getCachedStyledAttributes(int len) {
+        synchronized (mTmpValue) {
+            TypedArray attrs = mCachedStyledAttributes;
+            if (attrs != null) {
+                mCachedStyledAttributes = null;
+
+                attrs.mLength = len;
+                int fullLen = len * AssetManager.STYLE_NUM_ENTRIES;
+                if (attrs.mData.length >= fullLen) {
+                    return attrs;
+                }
+                attrs.mData = new int[fullLen];
+                attrs.mIndices = new int[1+len];
+                return attrs;
+            }
+            return new TypedArray(this,
+                    new int[len*AssetManager.STYLE_NUM_ENTRIES],
+                    new int[1+len], len);
+        }
+    }
+
+    private Resources() {
+        mAssets = AssetManager.getSystem();
+        // NOTE: Intentionally leaving this uninitialized (all values set
+        // to zero), so that anyone who tries to do something that requires
+        // metrics will get a very wrong value.
+        mConfiguration.setToDefaults();
+        mMetrics.setToDefaults();
+        updateConfiguration(null, null);
+        mAssets.ensureStringBlocks();
+    }
+}
+
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
new file mode 100644
index 0000000..e684cb8
--- /dev/null
+++ b/core/java/android/content/res/StringBlock.java
@@ -0,0 +1,395 @@
+/*
+ * 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.text.*;
+import android.text.style.*;
+import android.util.Config;
+import android.util.Log;
+import android.util.SparseArray;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import com.android.internal.util.XmlUtils;
+
+/**
+ * Conveniences for retrieving data out of a compiled string resource.
+ *
+ * {@hide}
+ */
+final class StringBlock {
+    private static final String TAG = "AssetManager";
+    private static final boolean localLOGV = Config.LOGV || false;
+
+    private final int mNative;
+    private final boolean mUseSparse;
+    private final boolean mOwnsNative;
+    private CharSequence[] mStrings;
+    private SparseArray<CharSequence> mSparseStrings;
+    StyleIDs mStyleIDs = null;
+
+    public StringBlock(byte[] data, boolean useSparse) {
+        mNative = nativeCreate(data, 0, data.length);
+        mUseSparse = useSparse;
+        mOwnsNative = true;
+        if (localLOGV) Log.v(TAG, "Created string block " + this
+                + ": " + nativeGetSize(mNative));
+    }
+
+    public StringBlock(byte[] data, int offset, int size, boolean useSparse) {
+        mNative = nativeCreate(data, offset, size);
+        mUseSparse = useSparse;
+        mOwnsNative = true;
+        if (localLOGV) Log.v(TAG, "Created string block " + this
+                + ": " + nativeGetSize(mNative));
+    }
+
+    public CharSequence get(int idx) {
+        synchronized (this) {
+            if (mStrings != null) {
+                CharSequence res = mStrings[idx];
+                if (res != null) {
+                    return res;
+                }
+            } else if (mSparseStrings != null) {
+                CharSequence res = mSparseStrings.get(idx);
+                if (res != null) {
+                    return res;
+                }
+            } else {
+                final int num = nativeGetSize(mNative);
+                if (mUseSparse && num > 250) {
+                    mSparseStrings = new SparseArray<CharSequence>();
+                } else {
+                    mStrings = new CharSequence[num];
+                }
+            }
+            String str = nativeGetString(mNative, idx);
+            CharSequence res = str;
+            int[] style = nativeGetStyle(mNative, idx);
+            if (localLOGV) Log.v(TAG, "Got string: " + str);
+            if (localLOGV) Log.v(TAG, "Got styles: " + style);
+            if (style != null) {
+                if (mStyleIDs == null) {
+                    mStyleIDs = new StyleIDs();
+                    mStyleIDs.boldId = nativeIndexOfString(mNative, "b");
+                    mStyleIDs.italicId = nativeIndexOfString(mNative, "i");
+                    mStyleIDs.underlineId = nativeIndexOfString(mNative, "u");
+                    mStyleIDs.ttId = nativeIndexOfString(mNative, "tt");
+                    mStyleIDs.bigId = nativeIndexOfString(mNative, "big");
+                    mStyleIDs.smallId = nativeIndexOfString(mNative, "small");
+                    mStyleIDs.supId = nativeIndexOfString(mNative, "sup");
+                    mStyleIDs.subId = nativeIndexOfString(mNative, "sub");
+                    mStyleIDs.strikeId = nativeIndexOfString(mNative, "strike");
+                    mStyleIDs.listItemId = nativeIndexOfString(mNative, "li");
+                    mStyleIDs.marqueeId = nativeIndexOfString(mNative, "marquee");
+
+                    if (localLOGV) Log.v(TAG, "BoldId=" + mStyleIDs.boldId
+                            + ", ItalicId=" + mStyleIDs.italicId
+                            + ", UnderlineId=" + mStyleIDs.underlineId);
+                }
+
+                res = applyStyles(str, style, mStyleIDs);
+            }
+            if (mStrings != null) mStrings[idx] = res;
+            else mSparseStrings.put(idx, res);
+            return res;
+        }
+    }
+
+    protected void finalize() throws Throwable {
+        if (mOwnsNative) {
+            nativeDestroy(mNative);
+        }
+    }
+
+    static final class StyleIDs {
+        private int boldId;
+        private int italicId;
+        private int underlineId;
+        private int ttId;
+        private int bigId;
+        private int smallId;
+        private int subId;
+        private int supId;
+        private int strikeId;
+        private int listItemId;
+        private int marqueeId;
+    }
+
+    private CharSequence applyStyles(String str, int[] style, StyleIDs ids) {
+        if (style.length == 0)
+            return str;
+
+        SpannableString buffer = new SpannableString(str);
+        int i=0;
+        while (i < style.length) {
+            int type = style[i];
+            if (localLOGV) Log.v(TAG, "Applying style span id=" + type
+                    + ", start=" + style[i+1] + ", end=" + style[i+2]);
+
+
+            if (type == ids.boldId) {
+                buffer.setSpan(new StyleSpan(Typeface.BOLD),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.italicId) {
+                buffer.setSpan(new StyleSpan(Typeface.ITALIC),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.underlineId) {
+                buffer.setSpan(new UnderlineSpan(),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.ttId) {
+                buffer.setSpan(new TypefaceSpan("monospace"),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.bigId) {
+                buffer.setSpan(new RelativeSizeSpan(1.25f),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.smallId) {
+                buffer.setSpan(new RelativeSizeSpan(0.8f),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.subId) {
+                buffer.setSpan(new SubscriptSpan(),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.supId) {
+                buffer.setSpan(new SuperscriptSpan(),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.strikeId) {
+                buffer.setSpan(new StrikethroughSpan(),
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+            } else if (type == ids.listItemId) {
+                addParagraphSpan(buffer, new BulletSpan(10),
+                                style[i+1], style[i+2]+1);
+            } else if (type == ids.marqueeId) {
+                buffer.setSpan(TextUtils.TruncateAt.MARQUEE,
+                               style[i+1], style[i+2]+1,
+                               Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+            } else {
+                String tag = nativeGetString(mNative, type);
+
+                if (tag.startsWith("font;")) {
+                    String sub;
+
+                    sub = subtag(tag, ";height=");
+                    if (sub != null) {
+                        int size = Integer.parseInt(sub);
+                        addParagraphSpan(buffer, new Height(size),
+                                       style[i+1], style[i+2]+1);
+                    }
+
+                    sub = subtag(tag, ";size=");
+                    if (sub != null) {
+                        int size = Integer.parseInt(sub);
+                        buffer.setSpan(new AbsoluteSizeSpan(size),
+                                       style[i+1], style[i+2]+1,
+                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+
+                    sub = subtag(tag, ";fgcolor=");
+                    if (sub != null) {
+                        int color = XmlUtils.convertValueToUnsignedInt(sub, -1);
+                        buffer.setSpan(new ForegroundColorSpan(color),
+                                       style[i+1], style[i+2]+1,
+                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+
+                    sub = subtag(tag, ";bgcolor=");
+                    if (sub != null) {
+                        int color = XmlUtils.convertValueToUnsignedInt(sub, -1);
+                        buffer.setSpan(new BackgroundColorSpan(color),
+                                       style[i+1], style[i+2]+1,
+                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+                } else if (tag.startsWith("a;")) {
+                    String sub;
+
+                    sub = subtag(tag, ";href=");
+                    if (sub != null) {
+                        buffer.setSpan(new URLSpan(sub),
+                                       style[i+1], style[i+2]+1,
+                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+                } else if (tag.startsWith("annotation;")) {
+                    int len = tag.length();
+                    int next;
+
+                    for (int t = tag.indexOf(';'); t < len; t = next) {
+                        int eq = tag.indexOf('=', t);
+                        if (eq < 0) {
+                            break;
+                        }
+
+                        next = tag.indexOf(';', eq);
+                        if (next < 0) {
+                            next = len;
+                        }
+
+                        String key = tag.substring(t + 1, eq);
+                        String value = tag.substring(eq + 1, next);
+
+                        buffer.setSpan(new Annotation(key, value),
+                                       style[i+1], style[i+2]+1,
+                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+                }
+            }
+
+            i += 3;
+        }
+        return new SpannedString(buffer);
+    }
+
+    /**
+     * If a translator has messed up the edges of paragraph-level markup,
+     * fix it to actually cover the entire paragraph that it is attached to
+     * instead of just whatever range they put it on.
+     */
+    private static void addParagraphSpan(Spannable buffer, Object what,
+                                         int start, int end) {
+        int len = buffer.length();
+
+        if (start != 0 && start != len && buffer.charAt(start - 1) != '\n') {
+            for (start--; start > 0; start--) {
+                if (buffer.charAt(start - 1) == '\n') {
+                    break;
+                }
+            }
+        }
+
+        if (end != 0 && end != len && buffer.charAt(end - 1) != '\n') {
+            for (end++; end < len; end++) {
+                if (buffer.charAt(end - 1) == '\n') {
+                    break;
+                }
+            }
+        }
+
+        buffer.setSpan(what, start, end, Spannable.SPAN_PARAGRAPH);
+    }
+
+    private static String subtag(String full, String attribute) {
+        int start = full.indexOf(attribute);
+        if (start < 0) {
+            return null;
+        }
+
+        start += attribute.length();
+        int end = full.indexOf(';', start);
+
+        if (end < 0) {
+            return full.substring(start);
+        } else {
+            return full.substring(start, end);
+        }
+    }
+
+    /**
+     * Forces the text line to be the specified height, shrinking/stretching
+     * the ascent if possible, or the descent if shrinking the ascent further
+     * will make the text unreadable.
+     */
+    private static class Height implements LineHeightSpan {
+        private int mSize;
+        private static float sProportion = 0;
+
+        public Height(int size) {
+            mSize = size;
+        }
+
+        public void chooseHeight(CharSequence text, int start, int end,
+                                 int spanstartv, int v,
+                                 Paint.FontMetricsInt fm) {
+            if (fm.bottom - fm.top < mSize) {
+                fm.top = fm.bottom - mSize;
+                fm.ascent = fm.ascent - mSize;
+            } else {
+                if (sProportion == 0) {
+                    /*
+                     * Calculate what fraction of the nominal ascent
+                     * the height of a capital letter actually is,
+                     * so that we won't reduce the ascent to less than
+                     * that unless we absolutely have to.
+                     */
+
+                    Paint p = new Paint();
+                    p.setTextSize(100);
+                    Rect r = new Rect();
+                    p.getTextBounds("ABCDEFG", 0, 7, r);
+
+                    sProportion = (r.top) / p.ascent();
+                }
+
+                int need = (int) Math.ceil(-fm.top * sProportion);
+
+                if (mSize - fm.descent >= need) {
+                    /*
+                     * It is safe to shrink the ascent this much.
+                     */
+
+                    fm.top = fm.bottom - mSize;
+                    fm.ascent = fm.descent - mSize;
+                } else if (mSize >= need) {
+                    /*
+                     * We can't show all the descent, but we can at least
+                     * show all the ascent.
+                     */
+
+                    fm.top = fm.ascent = -need;
+                    fm.bottom = fm.descent = fm.top + mSize;
+                } else {
+                    /*
+                     * Show as much of the ascent as we can, and no descent.
+                     */
+
+                    fm.top = fm.ascent = -mSize;
+                    fm.bottom = fm.descent = 0;
+                }
+            }
+        }
+    }
+
+    /**
+     * Create from an existing string block native object.  This is
+     * -extremely- dangerous -- only use it if you absolutely know what you
+     *  are doing!  The given native object must exist for the entire lifetime
+     *  of this newly creating StringBlock.
+     */
+    StringBlock(int obj, boolean useSparse) {
+        mNative = obj;
+        mUseSparse = useSparse;
+        mOwnsNative = false;
+        if (localLOGV) Log.v(TAG, "Created string block " + this
+                + ": " + nativeGetSize(mNative));
+    }
+
+    private static final native int nativeCreate(byte[] data,
+                                                 int offset,
+                                                 int size);
+    private static final native int nativeGetSize(int obj);
+    private static final native String nativeGetString(int obj, int idx);
+    private static final native int[] nativeGetStyle(int obj, int idx);
+    private static final native int nativeIndexOfString(int obj, String str);
+    private static final native void nativeDestroy(int obj);
+}
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
new file mode 100644
index 0000000..3a32c03
--- /dev/null
+++ b/core/java/android/content/res/TypedArray.java
@@ -0,0 +1,688 @@
+package android.content.res;
+
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+import com.android.internal.util.XmlUtils;
+
+import java.util.Arrays;
+
+/**
+ * Container for an array of values that were retrieved with
+ * {@link Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)}
+ * or {@link Resources#obtainAttributes}.  Be
+ * sure to call {@link #recycle} when done with them.
+ * 
+ * The indices used to retrieve values from this structure correspond to
+ * the positions of the attributes given to obtainStyledAttributes.
+ */
+public class TypedArray {
+    private final Resources mResources;
+    /*package*/ XmlBlock.Parser mXml;
+    /*package*/ int[] mRsrcs;
+    /*package*/ int[] mData;
+    /*package*/ int[] mIndices;
+    /*package*/ int mLength;
+    private TypedValue mValue = new TypedValue();
+   
+    /**
+     * Return the number of values in this array.
+     */
+    public int length() {
+        return mLength;
+    }
+    
+    /**
+     * Return the number of indices in the array that actually have data.
+     */
+    public int getIndexCount() {
+        return mIndices[0];
+    }
+    
+    /**
+     * Return an index in the array that has data.
+     * 
+     * @param at The index you would like to returned, ranging from 0 to
+     * {@link #getIndexCount()}.
+     * 
+     * @return The index at the given offset, which can be used with
+     * {@link #getValue} and related APIs.
+     */
+    public int getIndex(int at) {
+        return mIndices[1+at];
+    }
+    
+    /**
+     * Return the Resources object this array was loaded from.
+     */
+    public Resources getResources() {
+        return mResources;
+    }
+    
+    /**
+     * Retrieve the styled string value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return CharSequence holding string data.  May be styled.  Returns 
+     *         null if the attribute is not defined.
+     */
+    public CharSequence getText(int index) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return null;
+        } else if (type == TypedValue.TYPE_STRING) {
+            return loadStringValueAt(index);
+        }
+
+        TypedValue v = mValue;
+        if (getValueAt(index, v)) {
+            Log.w(Resources.TAG, "Converting to string: " + v);
+            return v.coerceToString();
+        }
+        Log.w(Resources.TAG, "getString of bad type: 0x"
+              + Integer.toHexString(type));
+        return null;
+    }
+
+    /**
+     * Retrieve the string value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return String holding string data.  Any styling information is
+     * removed.  Returns null if the attribute is not defined.
+     */
+    public String getString(int index) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return null;
+        } else if (type == TypedValue.TYPE_STRING) {
+            return loadStringValueAt(index).toString();
+        }
+
+        TypedValue v = mValue;
+        if (getValueAt(index, v)) {
+            Log.w(Resources.TAG, "Converting to string: " + v);
+            CharSequence cs = v.coerceToString();
+            return cs != null ? cs.toString() : null;
+        }
+        Log.w(Resources.TAG, "getString of bad type: 0x"
+              + Integer.toHexString(type));
+        return null;
+    }
+
+    /**
+     * Retrieve the string value for the attribute at <var>index</var>, but
+     * only if that string comes from an immediate value in an XML file.  That
+     * is, this does not allow references to string resources, string
+     * attributes, or conversions from other types.  As such, this method
+     * will only return strings for TypedArray objects that come from
+     * attributes in an XML file.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return String holding string data.  Any styling information is
+     * removed.  Returns null if the attribute is not defined or is not
+     * an immediate string value.
+     */
+    public String getNonResourceString(int index) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type == TypedValue.TYPE_STRING) {
+            final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+            if (cookie < 0) {
+                return mXml.getPooledString(
+                    data[index+AssetManager.STYLE_DATA]).toString();
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Retrieve the boolean value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined.
+     * 
+     * @return Attribute boolean value, or defValue if not defined.
+     */
+    public boolean getBoolean(int index, boolean defValue) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type >= TypedValue.TYPE_FIRST_INT
+            && type <= TypedValue.TYPE_LAST_INT) {
+            return data[index+AssetManager.STYLE_DATA] != 0;
+        }
+
+        TypedValue v = mValue;
+        if (getValueAt(index, v)) {
+            Log.w(Resources.TAG, "Converting to boolean: " + v);
+            return XmlUtils.convertValueToBoolean(
+                v.coerceToString(), defValue);
+        }
+        Log.w(Resources.TAG, "getBoolean of bad type: 0x"
+              + Integer.toHexString(type));
+        return defValue;
+    }
+
+    /**
+     * Retrieve the integer value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined.
+     * 
+     * @return Attribute int value, or defValue if not defined.
+     */
+    public int getInt(int index, int defValue) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type >= TypedValue.TYPE_FIRST_INT
+            && type <= TypedValue.TYPE_LAST_INT) {
+            return data[index+AssetManager.STYLE_DATA];
+        }
+
+        TypedValue v = mValue;
+        if (getValueAt(index, v)) {
+            Log.w(Resources.TAG, "Converting to int: " + v);
+            return XmlUtils.convertValueToInt(
+                v.coerceToString(), defValue);
+        }
+        Log.w(Resources.TAG, "getInt of bad type: 0x"
+              + Integer.toHexString(type));
+        return defValue;
+    }
+
+    /**
+     * Retrieve the float value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return Attribute float value, or defValue if not defined..
+     */
+    public float getFloat(int index, float defValue) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type == TypedValue.TYPE_FLOAT) {
+            return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]);
+        } else if (type >= TypedValue.TYPE_FIRST_INT
+            && type <= TypedValue.TYPE_LAST_INT) {
+            return data[index+AssetManager.STYLE_DATA];
+        }
+
+        TypedValue v = mValue;
+        if (getValueAt(index, v)) {
+            Log.w(Resources.TAG, "Converting to float: " + v);
+            CharSequence str = v.coerceToString();
+            if (str != null) {
+                return Float.parseFloat(str.toString());
+            }
+        }
+        Log.w(Resources.TAG, "getFloat of bad type: 0x"
+              + Integer.toHexString(type));
+        return defValue;
+    }
+    
+    /**
+     * Retrieve the color value for the attribute at <var>index</var>.  If
+     * the attribute references a color resource holding a complex
+     * {@link android.content.res.ColorStateList}, then the default color from
+     * the set is returned.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute color value, or defValue if not defined.
+     */
+    public int getColor(int index, int defValue) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type >= TypedValue.TYPE_FIRST_INT
+            && type <= TypedValue.TYPE_LAST_INT) {
+            return data[index+AssetManager.STYLE_DATA];
+        } else if (type == TypedValue.TYPE_STRING) {
+            final TypedValue value = mValue;
+            if (getValueAt(index, value)) {
+                ColorStateList csl = mResources.loadColorStateList(
+                        value, value.resourceId);
+                return csl.getDefaultColor();
+            }
+            return defValue;
+        }
+
+        throw new UnsupportedOperationException("Can't convert to color: type=0x"
+                + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieve the ColorStateList for the attribute at <var>index</var>.
+     * The value may be either a single solid color or a reference to
+     * a color or complex {@link android.content.res.ColorStateList} description.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return ColorStateList for the attribute, or null if not defined.
+     */
+    public ColorStateList getColorStateList(int index) {
+        final TypedValue value = mValue;
+        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+            return mResources.loadColorStateList(value, value.resourceId);
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve the integer value for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute integer value, or defValue if not defined.
+     */
+    public int getInteger(int index, int defValue) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type >= TypedValue.TYPE_FIRST_INT
+            && type <= TypedValue.TYPE_LAST_INT) {
+            return data[index+AssetManager.STYLE_DATA];
+        }
+
+        throw new UnsupportedOperationException("Can't convert to integer: type=0x"
+                + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var>.  Unit 
+     * conversions are based on the current {@link DisplayMetrics} 
+     * associated with the resources this {@link TypedArray} object 
+     * came from. 
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute dimension value multiplied by the appropriate 
+     * metric, or defValue if not defined.
+     * 
+     * @see #getDimensionPixelOffset
+     * @see #getDimensionPixelSize
+     */
+    public float getDimension(int index, float defValue) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type == TypedValue.TYPE_DIMENSION) {
+            return TypedValue.complexToDimension(
+                data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
+        }
+
+        throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
+                + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var> for use
+     * as an offset in raw pixels.  This is the same as
+     * {@link #getDimension}, except the returned value is converted to
+     * integer pixels for you.  An offset conversion involves simply
+     * truncating the base value to an integer.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute dimension value multiplied by the appropriate 
+     * metric and truncated to integer pixels, or defValue if not defined.
+     * 
+     * @see #getDimension
+     * @see #getDimensionPixelSize
+     */
+    public int getDimensionPixelOffset(int index, int defValue) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type == TypedValue.TYPE_DIMENSION) {
+            return TypedValue.complexToDimensionPixelOffset(
+                data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
+        }
+
+        throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
+                + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieve a dimensional unit attribute at <var>index</var> for use
+     * as a size in raw pixels.  This is the same as
+     * {@link #getDimension}, except the returned value is converted to
+     * integer pixels for use as a size.  A size conversion involves
+     * rounding the base value, and ensuring that a non-zero base value
+     * is at least one pixel in size.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute dimension value multiplied by the appropriate 
+     * metric and truncated to integer pixels, or defValue if not defined.
+     *  
+     * @see #getDimension
+     * @see #getDimensionPixelOffset
+     */
+    public int getDimensionPixelSize(int index, int defValue) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type == TypedValue.TYPE_DIMENSION) {
+            return TypedValue.complexToDimensionPixelSize(
+                data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
+        }
+
+        throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
+                + Integer.toHexString(type));
+    }
+
+    /**
+     * Special version of {@link #getDimensionPixelSize} for retrieving
+     * {@link android.view.ViewGroup}'s layout_width and layout_height
+     * attributes.  This is only here for performance reasons; applications
+     * should use {@link #getDimensionPixelSize}.
+     * 
+     * @param index Index of the attribute to retrieve.
+     * @param name Textual name of attribute for error reporting.
+     * 
+     * @return Attribute dimension value multiplied by the appropriate 
+     * metric and truncated to integer pixels.
+     */
+    public int getLayoutDimension(int index, String name) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type >= TypedValue.TYPE_FIRST_INT
+                && type <= TypedValue.TYPE_LAST_INT) {
+            return data[index+AssetManager.STYLE_DATA];
+        } else if (type == TypedValue.TYPE_DIMENSION) {
+            return TypedValue.complexToDimensionPixelSize(
+                data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
+        }
+
+        throw new RuntimeException(getPositionDescription()
+                + ": You must supply a " + name + " attribute.");
+    }
+    
+    /**
+     * Special version of {@link #getDimensionPixelSize} for retrieving
+     * {@link android.view.ViewGroup}'s layout_width and layout_height
+     * attributes.  This is only here for performance reasons; applications
+     * should use {@link #getDimensionPixelSize}.
+     * 
+     * @param index Index of the attribute to retrieve.
+     * @param defValue The default value to return if this attribute is not
+     * default or contains the wrong type of data.
+     * 
+     * @return Attribute dimension value multiplied by the appropriate 
+     * metric and truncated to integer pixels.
+     */
+    public int getLayoutDimension(int index, int defValue) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type >= TypedValue.TYPE_FIRST_INT
+                && type <= TypedValue.TYPE_LAST_INT) {
+            return data[index+AssetManager.STYLE_DATA];
+        } else if (type == TypedValue.TYPE_DIMENSION) {
+            return TypedValue.complexToDimensionPixelSize(
+                data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
+        }
+
+        return defValue;
+    }
+
+    /**
+     * Retrieve a fractional unit attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve. 
+     * @param base The base value of this fraction.  In other words, a 
+     *             standard fraction is multiplied by this value.
+     * @param pbase The parent base value of this fraction.  In other 
+     *             words, a parent fraction (nn%p) is multiplied by this
+     *             value.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute fractional value multiplied by the appropriate 
+     * base value, or defValue if not defined. 
+     */
+    public float getFraction(int index, int base, int pbase, float defValue) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return defValue;
+        } else if (type == TypedValue.TYPE_FRACTION) {
+            return TypedValue.complexToFraction(
+                data[index+AssetManager.STYLE_DATA], base, pbase);
+        }
+
+        throw new UnsupportedOperationException("Can't convert to fraction: type=0x"
+                + Integer.toHexString(type));
+    }
+
+    /**
+     * Retrieve the resource identifier for the attribute at
+     * <var>index</var>.  Note that attribute resource as resolved when 
+     * the overall {@link TypedArray} object is retrieved.  As a 
+     * result, this function will return the resource identifier of the 
+     * final resource value that was found, <em>not</em> necessarily the 
+     * original resource that was specified by the attribute. 
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param defValue Value to return if the attribute is not defined or
+     *                 not a resource.
+     * 
+     * @return Attribute resource identifier, or defValue if not defined.
+     */
+    public int getResourceId(int index, int defValue) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) {
+            final int resid = data[index+AssetManager.STYLE_RESOURCE_ID];
+            if (resid != 0) {
+                return resid;
+            }
+        }
+        return defValue;
+    }
+
+    /**
+     * Retrieve the Drawable for the attribute at <var>index</var>.  This
+     * gets the resource ID of the selected attribute, and uses
+     * {@link Resources#getDrawable Resources.getDrawable} of the owning
+     * Resources object to retrieve its Drawable.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return Drawable for the attribute, or null if not defined.
+     */
+    public Drawable getDrawable(int index) {
+        final TypedValue value = mValue;
+        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+            if (false) {
+                System.out.println("******************************************************************");
+                System.out.println("Got drawable resource: type="
+                                   + value.type
+                                   + " str=" + value.string
+                                   + " int=0x" + Integer.toHexString(value.data)
+                                   + " cookie=" + value.assetCookie);
+                System.out.println("******************************************************************");
+            }
+            return mResources.loadDrawable(value, value.resourceId);
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve the CharSequence[] for the attribute at <var>index</var>.
+     * This gets the resource ID of the selected attribute, and uses
+     * {@link Resources#getTextArray Resources.getTextArray} of the owning
+     * Resources object to retrieve its String[].
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return CharSequence[] for the attribute, or null if not defined.
+     */
+    public CharSequence[] getTextArray(int index) {
+        final TypedValue value = mValue;
+        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+            if (false) {
+                System.out.println("******************************************************************");
+                System.out.println("Got drawable resource: type="
+                                   + value.type
+                                   + " str=" + value.string
+                                   + " int=0x" + Integer.toHexString(value.data)
+                                   + " cookie=" + value.assetCookie);
+                System.out.println("******************************************************************");
+            }
+            return mResources.getTextArray(value.resourceId);
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve the raw TypedValue for the attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * @param outValue TypedValue object in which to place the attribute's
+     *                 data.
+     * 
+     * @return Returns true if the value was retrieved, else false. 
+     */
+    public boolean getValue(int index, TypedValue outValue) {
+        return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue);
+    }
+
+    /**
+     * Determines whether there is an attribute at <var>index</var>.
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return True if the attribute has a value, false otherwise.
+     */
+    public boolean hasValue(int index) {
+        index *= AssetManager.STYLE_NUM_ENTRIES;
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        return type != TypedValue.TYPE_NULL;
+    }
+    
+    /**
+     * Retrieve the raw TypedValue for the attribute at <var>index</var> 
+     * and return a temporary object holding its data.  This object is only 
+     * valid until the next call on to {@link TypedArray}. 
+     * 
+     * @param index Index of attribute to retrieve.
+     * 
+     * @return Returns a TypedValue object if the attribute is defined, 
+     *         containing its data; otherwise returns null.  (You will not
+     *         receive a TypedValue whose type is TYPE_NULL.)
+     */
+    public TypedValue peekValue(int index) {
+        final TypedValue value = mValue;
+        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+            return value;
+        }
+        return null;
+    }
+
+    /**
+     * Returns a message about the parser state suitable for printing error messages.
+     */
+    public String getPositionDescription() {
+        return mXml != null ? mXml.getPositionDescription() : "<internal>";
+    }
+
+    /**
+     * Give back a previously retrieved StyledAttributes, for later re-use.
+     */
+    public void recycle() {
+        synchronized (mResources.mTmpValue) {
+            TypedArray cached = mResources.mCachedStyledAttributes;
+            if (cached == null || cached.mData.length < mData.length) {
+                mXml = null;
+                mResources.mCachedStyledAttributes = this;
+            }
+        }
+    }
+
+    private boolean getValueAt(int index, TypedValue outValue) {
+        final int[] data = mData;
+        final int type = data[index+AssetManager.STYLE_TYPE];
+        if (type == TypedValue.TYPE_NULL) {
+            return false;
+        }
+        outValue.type = type;
+        outValue.data = data[index+AssetManager.STYLE_DATA];
+        outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+        outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
+        outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];
+        if (type == TypedValue.TYPE_STRING) {
+            outValue.string = loadStringValueAt(index);
+        }
+        return true;
+    }
+
+    private CharSequence loadStringValueAt(int index) {
+        final int[] data = mData;
+        final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+        if (cookie < 0) {
+            if (mXml != null) {
+                return mXml.getPooledString(
+                    data[index+AssetManager.STYLE_DATA]);
+            }
+            return null;
+        }
+        //System.out.println("Getting pooled from: " + v);
+        return mResources.mAssets.getPooledString(
+            cookie, data[index+AssetManager.STYLE_DATA]);
+    }
+
+    /*package*/ TypedArray(Resources resources, int[] data, int[] indices, int len) {
+        mResources = resources;
+        mData = data;
+        mIndices = indices;
+        mLength = len;
+    }
+
+    public String toString() {
+        return Arrays.toString(mData);
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java
new file mode 100644
index 0000000..6336678
--- /dev/null
+++ b/core/java/android/content/res/XmlBlock.java
@@ -0,0 +1,515 @@
+/*
+ * 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.util.TypedValue;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * Wrapper around a compiled XML file.
+ * 
+ * {@hide}
+ */
+final class XmlBlock {
+    private static final boolean DEBUG=false;
+
+    public XmlBlock(byte[] data) {
+        mAssets = null;
+        mNative = nativeCreate(data, 0, data.length);
+        mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
+    }
+
+    public XmlBlock(byte[] data, int offset, int size) {
+        mAssets = null;
+        mNative = nativeCreate(data, offset, size);
+        mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
+    }
+
+    public void close() {
+        synchronized (this) {
+            if (mOpen) {
+                mOpen = false;
+                decOpenCountLocked();
+            }
+        }
+    }
+
+    private void decOpenCountLocked() {
+        mOpenCount--;
+        if (mOpenCount == 0) {
+            nativeDestroy(mNative);
+            if (mAssets != null) {
+                mAssets.xmlBlockGone();
+            }
+        }
+    }
+
+    public XmlResourceParser newParser() {
+        synchronized (this) {
+            if (mNative != 0) {
+                return new Parser(nativeCreateParseState(mNative), this);
+            }
+            return null;
+        }
+    }
+
+    /*package*/ final class Parser implements XmlResourceParser {
+        Parser(int parseState, XmlBlock block) {
+            mParseState = parseState;
+            mBlock = block;
+            block.mOpenCount++;
+        }
+
+        public void setFeature(String name, boolean state) throws XmlPullParserException {
+            if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
+                return;
+            }
+            if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) {
+                return;
+            }
+            throw new XmlPullParserException("Unsupported feature: " + name);
+        }
+        public boolean getFeature(String name) {
+            if (FEATURE_PROCESS_NAMESPACES.equals(name)) {
+                return true;
+            }
+            if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {
+                return true;
+            }
+            return false;
+        }
+        public void setProperty(String name, Object value) throws XmlPullParserException {
+            throw new XmlPullParserException("setProperty() not supported");
+        }
+        public Object getProperty(String name) {
+            return null;
+        }
+        public void setInput(Reader in) throws XmlPullParserException {
+            throw new XmlPullParserException("setInput() not supported");
+        }
+        public void setInput(InputStream inputStream, String inputEncoding) throws XmlPullParserException {
+            throw new XmlPullParserException("setInput() not supported");
+        }
+        public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException {
+            throw new XmlPullParserException("defineEntityReplacementText() not supported");
+        }
+        public String getNamespacePrefix(int pos) throws XmlPullParserException {
+            throw new XmlPullParserException("getNamespacePrefix() not supported");
+        }
+        public String getInputEncoding() {
+            return null;
+        }
+        public String getNamespace(String prefix) {
+            throw new RuntimeException("getNamespace() not supported");
+        }
+        public int getNamespaceCount(int depth) throws XmlPullParserException {
+            throw new XmlPullParserException("getNamespaceCount() not supported");
+        }
+        public String getPositionDescription() {
+            return "Binary XML file line #" + getLineNumber();
+        }
+        public String getNamespaceUri(int pos) throws XmlPullParserException {
+            throw new XmlPullParserException("getNamespaceUri() not supported");
+        }
+        public int getColumnNumber() {
+            return -1;
+        }
+        public int getDepth() {
+            return mDepth;
+        }
+        public String getText() {
+            int id = nativeGetText(mParseState);
+            return id >= 0 ? mStrings.get(id).toString() : null;
+        }
+        public int getLineNumber() {
+            return nativeGetLineNumber(mParseState);
+        }
+        public int getEventType() throws XmlPullParserException {
+            return mEventType;
+        }
+        public boolean isWhitespace() throws XmlPullParserException {
+            // whitespace was stripped by aapt.
+            return false;
+        }
+        public String getPrefix() {
+            throw new RuntimeException("getPrefix not supported");
+        }
+        public char[] getTextCharacters(int[] holderForStartAndLength) {
+            String txt = getText();
+            char[] chars = null;
+            if (txt != null) {
+                holderForStartAndLength[0] = 0;
+                holderForStartAndLength[1] = txt.length();
+                chars = new char[txt.length()];
+                txt.getChars(0, txt.length(), chars, 0);
+            }
+            return chars;
+        }
+        public String getNamespace() {
+            int id = nativeGetNamespace(mParseState);
+            return id >= 0 ? mStrings.get(id).toString() : "";
+        }
+        public String getName() {
+            int id = nativeGetName(mParseState);
+            return id >= 0 ? mStrings.get(id).toString() : null;
+        }
+        public String getAttributeNamespace(int index) {
+            int id = nativeGetAttributeNamespace(mParseState, index);
+            if (DEBUG) System.out.println("getAttributeNamespace of " + index + " = " + id);
+            if (id >= 0) return mStrings.get(id).toString();
+            else if (id == -1) return "";
+            throw new IndexOutOfBoundsException(String.valueOf(index));
+        }
+        public String getAttributeName(int index) {
+            int id = nativeGetAttributeName(mParseState, index);
+            if (DEBUG) System.out.println("getAttributeName of " + index + " = " + id);
+            if (id >= 0) return mStrings.get(id).toString();
+            throw new IndexOutOfBoundsException(String.valueOf(index));
+        }
+        public String getAttributePrefix(int index) {
+            throw new RuntimeException("getAttributePrefix not supported");
+        }
+        public boolean isEmptyElementTag() throws XmlPullParserException {
+            // XXX Need to detect this.
+            return false;
+        }
+        public int getAttributeCount() {
+            return mEventType == START_TAG ? nativeGetAttributeCount(mParseState) : -1;
+        }
+        public String getAttributeValue(int index) {
+            int id = nativeGetAttributeStringValue(mParseState, index);
+            if (DEBUG) System.out.println("getAttributeValue of " + index + " = " + id);
+            if (id >= 0) return mStrings.get(id).toString();
+
+            // May be some other type...  check and try to convert if so.
+            int t = nativeGetAttributeDataType(mParseState, index);
+            if (t == TypedValue.TYPE_NULL) {
+                throw new IndexOutOfBoundsException(String.valueOf(index));
+            }
+
+            int v = nativeGetAttributeData(mParseState, index);
+            return TypedValue.coerceToString(t, v);
+        }
+        public String getAttributeType(int index) {
+            return "CDATA";
+        }
+        public boolean isAttributeDefault(int index) {
+            return false;
+        }
+        public int nextToken() throws XmlPullParserException,IOException {
+            return next();
+        }
+        public String getAttributeValue(String namespace, String name) {
+            int idx = nativeGetAttributeIndex(mParseState, namespace, name);
+            if (idx >= 0) {
+                if (DEBUG) System.out.println("getAttributeName of "
+                        + namespace + ":" + name + " index = " + idx);
+                if (DEBUG) System.out.println(
+                        "Namespace=" + getAttributeNamespace(idx)
+                        + "Name=" + getAttributeName(idx)
+                        + ", Value=" + getAttributeValue(idx));
+                return getAttributeValue(idx);
+            }
+            return null;
+        }
+        public int next() throws XmlPullParserException,IOException {
+            if (!mStarted) {
+                mStarted = true;
+                return START_DOCUMENT;
+            }
+            if (mParseState == 0) {
+                return END_DOCUMENT;
+            }
+            int ev = nativeNext(mParseState);
+            if (mDecNextDepth) {
+                mDepth--;
+                mDecNextDepth = false;
+            }
+            switch (ev) {
+            case START_TAG:
+                mDepth++;
+                break;
+            case END_TAG:
+                mDecNextDepth = true;
+                break;
+            }
+            mEventType = ev;
+            if (ev == END_DOCUMENT) {
+                // Automatically close the parse when we reach the end of
+                // a document, since the standard XmlPullParser interface
+                // doesn't have such an API so most clients will leave us
+                // dangling.
+                close();
+            }
+            return ev;
+        }
+        public void require(int type, String namespace, String name) throws XmlPullParserException,IOException {
+            if (type != getEventType()
+                || (namespace != null && !namespace.equals( getNamespace () ) )
+                || (name != null && !name.equals( getName() ) ) )
+                throw new XmlPullParserException( "expected "+ TYPES[ type ]+getPositionDescription());
+        }
+        public String nextText() throws XmlPullParserException,IOException {
+            if(getEventType() != START_TAG) {
+               throw new XmlPullParserException(
+                 getPositionDescription()
+                 + ": parser must be on START_TAG to read next text", this, null);
+            }
+            int eventType = next();
+            if(eventType == TEXT) {
+               String result = getText();
+               eventType = next();
+               if(eventType != END_TAG) {
+                 throw new XmlPullParserException(
+                    getPositionDescription()
+                    + ": event TEXT it must be immediately followed by END_TAG", this, null);
+                }
+                return result;
+            } else if(eventType == END_TAG) {
+               return "";
+            } else {
+               throw new XmlPullParserException(
+                 getPositionDescription()
+                 + ": parser must be on START_TAG or TEXT to read text", this, null);
+            }
+        }
+        public int nextTag() throws XmlPullParserException,IOException {
+            int eventType = next();
+            if(eventType == TEXT && isWhitespace()) {   // skip whitespace
+               eventType = next();
+            }
+            if (eventType != START_TAG && eventType != END_TAG) {
+               throw new XmlPullParserException(
+                   getPositionDescription() 
+                   + ": expected start or end tag", this, null);
+            }
+            return eventType;
+        }
+    
+        public int getAttributeNameResource(int index) {
+            return nativeGetAttributeResource(mParseState, index);
+        }
+    
+        public int getAttributeListValue(String namespace, String attribute,
+                String[] options, int defaultValue) {
+            int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+            if (idx >= 0) {
+                return getAttributeListValue(idx, options, defaultValue);
+            }
+            return defaultValue;
+        }
+        public boolean getAttributeBooleanValue(String namespace, String attribute,
+                boolean defaultValue) {
+            int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+            if (idx >= 0) {
+                return getAttributeBooleanValue(idx, defaultValue);
+            }
+            return defaultValue;
+        }
+        public int getAttributeResourceValue(String namespace, String attribute,
+                int defaultValue) {
+            int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+            if (idx >= 0) {
+                return getAttributeResourceValue(idx, defaultValue);
+            }
+            return defaultValue;
+        }
+        public int getAttributeIntValue(String namespace, String attribute,
+                int defaultValue) {
+            int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+            if (idx >= 0) {
+                return getAttributeIntValue(idx, defaultValue);
+            }
+            return defaultValue;
+        }
+        public int getAttributeUnsignedIntValue(String namespace, String attribute,
+                                                int defaultValue)
+        {
+            int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+            if (idx >= 0) {
+                return getAttributeUnsignedIntValue(idx, defaultValue);
+            }
+            return defaultValue;
+        }
+        public float getAttributeFloatValue(String namespace, String attribute,
+                float defaultValue) {
+            int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+            if (idx >= 0) {
+                return getAttributeFloatValue(idx, defaultValue);
+            }
+            return defaultValue;
+        }
+
+        public int getAttributeListValue(int idx,
+                String[] options, int defaultValue) {
+            int t = nativeGetAttributeDataType(mParseState, idx);
+            int v = nativeGetAttributeData(mParseState, idx);
+            if (t == TypedValue.TYPE_STRING) {
+                return XmlUtils.convertValueToList(
+                    mStrings.get(v), options, defaultValue);
+            }
+            return v;
+        }
+        public boolean getAttributeBooleanValue(int idx,
+                boolean defaultValue) {
+            int t = nativeGetAttributeDataType(mParseState, idx);
+            // Note: don't attempt to convert any other types, because
+            // we want to count on appt doing the conversion for us.
+            if (t >= TypedValue.TYPE_FIRST_INT &&
+                t <= TypedValue.TYPE_LAST_INT) {
+                return nativeGetAttributeData(mParseState, idx) != 0;
+            }
+            return defaultValue;
+        }
+        public int getAttributeResourceValue(int idx, int defaultValue) {
+            int t = nativeGetAttributeDataType(mParseState, idx);
+            // Note: don't attempt to convert any other types, because
+            // we want to count on appt doing the conversion for us.
+            if (t == TypedValue.TYPE_REFERENCE) {
+                return nativeGetAttributeData(mParseState, idx);
+            }
+            return defaultValue;
+        }
+        public int getAttributeIntValue(int idx, int defaultValue) {
+            int t = nativeGetAttributeDataType(mParseState, idx);
+            // Note: don't attempt to convert any other types, because
+            // we want to count on appt doing the conversion for us.
+            if (t >= TypedValue.TYPE_FIRST_INT &&
+                t <= TypedValue.TYPE_LAST_INT) {
+                return nativeGetAttributeData(mParseState, idx);
+            }
+            return defaultValue;
+        }
+        public int getAttributeUnsignedIntValue(int idx, int defaultValue) {
+            int t = nativeGetAttributeDataType(mParseState, idx);
+            // Note: don't attempt to convert any other types, because
+            // we want to count on appt doing the conversion for us.
+            if (t >= TypedValue.TYPE_FIRST_INT &&
+                t <= TypedValue.TYPE_LAST_INT) {
+                return nativeGetAttributeData(mParseState, idx);
+            }
+            return defaultValue;
+        }
+        public float getAttributeFloatValue(int idx, float defaultValue) {
+            int t = nativeGetAttributeDataType(mParseState, idx);
+            // Note: don't attempt to convert any other types, because
+            // we want to count on appt doing the conversion for us.
+            if (t == TypedValue.TYPE_FLOAT) {
+                return Float.intBitsToFloat(
+                    nativeGetAttributeData(mParseState, idx));
+            }
+            throw new RuntimeException("not a float!");
+        }
+
+        public String getIdAttribute() {
+            int id = nativeGetIdAttribute(mParseState);
+            return id >= 0 ? mStrings.get(id).toString() : null;
+        }
+        public String getClassAttribute() {
+            int id = nativeGetClassAttribute(mParseState);
+            return id >= 0 ? mStrings.get(id).toString() : null;
+        }
+
+        public int getIdAttributeResourceValue(int defaultValue) {
+            //todo: create and use native method
+            return getAttributeResourceValue(null, "id", defaultValue);
+        }
+
+        public int getStyleAttribute() {
+            return nativeGetStyleAttribute(mParseState);
+        }
+
+        public void close() {
+            synchronized (mBlock) {
+                if (mParseState != 0) {
+                    nativeDestroyParseState(mParseState);
+                    mParseState = 0;
+                    mBlock.decOpenCountLocked();
+                }
+            }
+        }
+        
+        protected void finalize() throws Throwable {
+            close();
+        }
+
+        /*package*/ final CharSequence getPooledString(int id) {
+            return mStrings.get(id);
+        }
+
+        /*package*/ int mParseState;
+        private final XmlBlock mBlock;
+        private boolean mStarted = false;
+        private boolean mDecNextDepth = false;
+        private int mDepth = 0;
+        private int mEventType = START_DOCUMENT;
+    }
+
+    protected void finalize() throws Throwable {
+        close();
+    }
+
+    /**
+     * Create from an existing xml block native object.  This is
+     * -extremely- dangerous -- only use it if you absolutely know what you
+     *  are doing!  The given native object must exist for the entire lifetime
+     *  of this newly creating XmlBlock.
+     */
+    XmlBlock(AssetManager assets, int xmlBlock) {
+        mAssets = assets;
+        mNative = xmlBlock;
+        mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false);
+    }
+
+    private final AssetManager mAssets;
+    private final int mNative;
+    private final StringBlock mStrings;
+    private boolean mOpen = true;
+    private int mOpenCount = 1;
+
+    private static final native int nativeCreate(byte[] data,
+                                                 int offset,
+                                                 int size);
+    private static final native int nativeGetStringBlock(int obj);
+
+    private static final native int nativeCreateParseState(int obj);
+    private static final native int nativeNext(int state);
+    private static final native int nativeGetNamespace(int state);
+    private static final native int nativeGetName(int state);
+    private static final native int nativeGetText(int state);
+    private static final native int nativeGetLineNumber(int state);
+    private static final native int nativeGetAttributeCount(int state);
+    private static final native int nativeGetAttributeNamespace(int state, int idx);
+    private static final native int nativeGetAttributeName(int state, int idx);
+    private static final native int nativeGetAttributeResource(int state, int idx);
+    private static final native int nativeGetAttributeDataType(int state, int idx);
+    private static final native int nativeGetAttributeData(int state, int idx);
+    private static final native int nativeGetAttributeStringValue(int state, int idx);
+    private static final native int nativeGetIdAttribute(int state);
+    private static final native int nativeGetClassAttribute(int state);
+    private static final native int nativeGetStyleAttribute(int state);
+    private static final native int nativeGetAttributeIndex(int state, String namespace, String name);
+    private static final native void nativeDestroyParseState(int state);
+
+    private static final native void nativeDestroy(int obj);
+}
diff --git a/core/java/android/content/res/XmlResourceParser.java b/core/java/android/content/res/XmlResourceParser.java
new file mode 100644
index 0000000..c59e6d4
--- /dev/null
+++ b/core/java/android/content/res/XmlResourceParser.java
@@ -0,0 +1,36 @@
+/*
+ * 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 org.xmlpull.v1.XmlPullParser;
+
+import android.util.AttributeSet;
+
+/**
+ * The XML parsing interface returned for an XML resource.  This is a standard
+ * XmlPullParser interface, as well as an extended AttributeSet interface and
+ * an additional close() method on this interface for the client to indicate
+ * when it is done reading the resource.
+ */
+public interface XmlResourceParser extends XmlPullParser, AttributeSet {
+    /**
+     * Close this interface to the resource.  Calls on the interface are no
+     * longer value after this call.
+     */
+    public void close();
+}
+
diff --git a/core/java/android/content/res/package.html b/core/java/android/content/res/package.html
new file mode 100644
index 0000000..bb09dc7
--- /dev/null
+++ b/core/java/android/content/res/package.html
@@ -0,0 +1,8 @@
+<HTML>
+<BODY>
+Contains classes for accessing application resources, 
+such as raw asset files, colors, drawables, media or other other files 
+in the package, plus important device configuration details 
+(orientation, input types, etc.) that affect how the application may behave.
+</BODY>
+</HTML>
\ No newline at end of file