auto import from //depot/cupcake/@135843
diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp
new file mode 100644
index 0000000..91203dd
--- /dev/null
+++ b/libs/utils/Asset.cpp
@@ -0,0 +1,813 @@
+/*
+ * 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.
+ */
+
+//
+// Provide access to a read-only asset.
+//
+
+#define LOG_TAG "asset"
+//#define NDEBUG 0
+
+#include <utils/Asset.h>
+#include <utils/Atomic.h>
+#include <utils/FileMap.h>
+#include <utils/ZipUtils.h>
+#include <utils/ZipFileRO.h>
+#include <utils/Log.h>
+
+#include <string.h>
+#include <memory.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+
+using namespace android;
+
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+static volatile int32_t gCount = 0;
+
+int32_t Asset::getGlobalCount()
+{
+    return gCount;
+}
+
+Asset::Asset(void)
+    : mAccessMode(ACCESS_UNKNOWN)
+{
+    int count = android_atomic_inc(&gCount)+1;
+    //LOGI("Creating Asset %p #%d\n", this, count);
+}
+
+Asset::~Asset(void)
+{
+    int count = android_atomic_dec(&gCount);
+    //LOGI("Destroying Asset in %p #%d\n", this, count);
+}
+
+/*
+ * Create a new Asset from a file on disk.  There is a fair chance that
+ * the file doesn't actually exist.
+ *
+ * We can use "mode" to decide how we want to go about it.
+ */
+/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode)
+{
+    _FileAsset* pAsset;
+    status_t result;
+    off_t length;
+    int fd;
+
+    fd = open(fileName, O_RDONLY | O_BINARY);
+    if (fd < 0)
+        return NULL;
+
+    /*
+     * Under Linux, the lseek fails if we actually opened a directory.  To
+     * be correct we should test the file type explicitly, but since we
+     * always open things read-only it doesn't really matter, so there's
+     * no value in incurring the extra overhead of an fstat() call.
+     */
+    length = lseek(fd, 0, SEEK_END);
+    if (length < 0) {
+        ::close(fd);
+        return NULL;
+    }
+    (void) lseek(fd, 0, SEEK_SET);
+
+    pAsset = new _FileAsset;
+    result = pAsset->openChunk(fileName, fd, 0, length);
+    if (result != NO_ERROR) {
+        delete pAsset;
+        return NULL;
+    }
+
+    pAsset->mAccessMode = mode;
+    return pAsset;
+}
+
+
+/*
+ * Create a new Asset from a compressed file on disk.  There is a fair chance
+ * that the file doesn't actually exist.
+ *
+ * We currently support gzip files.  We might want to handle .bz2 someday.
+ */
+/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName,
+    AccessMode mode)
+{
+    _CompressedAsset* pAsset;
+    status_t result;
+    off_t fileLen;
+    bool scanResult;
+    long offset;
+    int method;
+    long uncompressedLen, compressedLen;
+    int fd;
+
+    fd = open(fileName, O_RDONLY | O_BINARY);
+    if (fd < 0)
+        return NULL;
+
+    fileLen = lseek(fd, 0, SEEK_END);
+    if (fileLen < 0) {
+        ::close(fd);
+        return NULL;
+    }
+    (void) lseek(fd, 0, SEEK_SET);
+
+    /* want buffered I/O for the file scan; must dup so fclose() is safe */
+    FILE* fp = fdopen(dup(fd), "rb");
+    if (fp == NULL) {
+        ::close(fd);
+        return NULL;
+    }
+
+    unsigned long crc32;
+    scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen,
+                    &compressedLen, &crc32);
+    offset = ftell(fp);
+    fclose(fp);
+    if (!scanResult) {
+        LOGD("File '%s' is not in gzip format\n", fileName);
+        ::close(fd);
+        return NULL;
+    }
+
+    pAsset = new _CompressedAsset;
+    result = pAsset->openChunk(fd, offset, method, uncompressedLen,
+                compressedLen);
+    if (result != NO_ERROR) {
+        delete pAsset;
+        return NULL;
+    }
+
+    pAsset->mAccessMode = mode;
+    return pAsset;
+}
+
+
+#if 0
+/*
+ * Create a new Asset from part of an open file.
+ */
+/*static*/ Asset* Asset::createFromFileSegment(int fd, off_t offset,
+    size_t length, AccessMode mode)
+{
+    _FileAsset* pAsset;
+    status_t result;
+
+    pAsset = new _FileAsset;
+    result = pAsset->openChunk(NULL, fd, offset, length);
+    if (result != NO_ERROR)
+        return NULL;
+
+    pAsset->mAccessMode = mode;
+    return pAsset;
+}
+
+/*
+ * Create a new Asset from compressed data in an open file.
+ */
+/*static*/ Asset* Asset::createFromCompressedData(int fd, off_t offset,
+    int compressionMethod, size_t uncompressedLen, size_t compressedLen,
+    AccessMode mode)
+{
+    _CompressedAsset* pAsset;
+    status_t result;
+
+    pAsset = new _CompressedAsset;
+    result = pAsset->openChunk(fd, offset, compressionMethod,
+                uncompressedLen, compressedLen);
+    if (result != NO_ERROR)
+        return NULL;
+
+    pAsset->mAccessMode = mode;
+    return pAsset;
+}
+#endif
+
+/*
+ * Create a new Asset from a memory mapping.
+ */
+/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap,
+    AccessMode mode)
+{
+    _FileAsset* pAsset;
+    status_t result;
+
+    pAsset = new _FileAsset;
+    result = pAsset->openChunk(dataMap);
+    if (result != NO_ERROR)
+        return NULL;
+
+    pAsset->mAccessMode = mode;
+    return pAsset;
+}
+
+/*
+ * Create a new Asset from compressed data in a memory mapping.
+ */
+/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
+    int method, size_t uncompressedLen, AccessMode mode)
+{
+    _CompressedAsset* pAsset;
+    status_t result;
+
+    pAsset = new _CompressedAsset;
+    result = pAsset->openChunk(dataMap, method, uncompressedLen);
+    if (result != NO_ERROR)
+        return NULL;
+
+    pAsset->mAccessMode = mode;
+    return pAsset;
+}
+
+
+/*
+ * Do generic seek() housekeeping.  Pass in the offset/whence values from
+ * the seek request, along with the current chunk offset and the chunk
+ * length.
+ *
+ * Returns the new chunk offset, or -1 if the seek is illegal.
+ */
+off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn)
+{
+    off_t newOffset;
+
+    switch (whence) {
+    case SEEK_SET:
+        newOffset = offset;
+        break;
+    case SEEK_CUR:
+        newOffset = curPosn + offset;
+        break;
+    case SEEK_END:
+        newOffset = maxPosn + offset;
+        break;
+    default:
+        LOGW("unexpected whence %d\n", whence);
+        // this was happening due to an off_t size mismatch
+        assert(false);
+        return (off_t) -1;
+    }
+
+    if (newOffset < 0 || newOffset > maxPosn) {
+        LOGW("seek out of range: want %ld, end=%ld\n",
+            (long) newOffset, (long) maxPosn);
+        return (off_t) -1;
+    }
+
+    return newOffset;
+}
+
+
+/*
+ * ===========================================================================
+ *      _FileAsset
+ * ===========================================================================
+ */
+
+/*
+ * Constructor.
+ */
+_FileAsset::_FileAsset(void)
+    : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
+{
+}
+
+/*
+ * Destructor.  Release resources.
+ */
+_FileAsset::~_FileAsset(void)
+{
+    close();
+}
+
+/*
+ * Operate on a chunk of an uncompressed file.
+ *
+ * Zero-length chunks are allowed.
+ */
+status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_t length)
+{
+    assert(mFp == NULL);    // no reopen
+    assert(mMap == NULL);
+    assert(fd >= 0);
+    assert(offset >= 0);
+
+    /*
+     * Seek to end to get file length.
+     */
+    off_t fileLength;
+    fileLength = lseek(fd, 0, SEEK_END);
+    if (fileLength == (off_t) -1) {
+        // probably a bad file descriptor
+        LOGD("failed lseek (errno=%d)\n", errno);
+        return UNKNOWN_ERROR;
+    }
+
+    if ((off_t) (offset + length) > fileLength) {
+        LOGD("start (%ld) + len (%ld) > end (%ld)\n",
+            (long) offset, (long) length, (long) fileLength);
+        return BAD_INDEX;
+    }
+
+    /* after fdopen, the fd will be closed on fclose() */
+    mFp = fdopen(fd, "rb");
+    if (mFp == NULL)
+        return UNKNOWN_ERROR;
+
+    mStart = offset;
+    mLength = length;
+    assert(mOffset == 0);
+
+    /* seek the FILE* to the start of chunk */
+    if (fseek(mFp, mStart, SEEK_SET) != 0) {
+        assert(false);
+    }
+
+    mFileName = fileName != NULL ? strdup(fileName) : NULL;
+    
+    return NO_ERROR;
+}
+
+/*
+ * Create the chunk from the map.
+ */
+status_t _FileAsset::openChunk(FileMap* dataMap)
+{
+    assert(mFp == NULL);    // no reopen
+    assert(mMap == NULL);
+    assert(dataMap != NULL);
+
+    mMap = dataMap;
+    mStart = -1;            // not used
+    mLength = dataMap->getDataLength();
+    assert(mOffset == 0);
+
+    return NO_ERROR;
+}
+
+/*
+ * Read a chunk of data.
+ */
+ssize_t _FileAsset::read(void* buf, size_t count)
+{
+    size_t maxLen;
+    size_t actual;
+
+    assert(mOffset >= 0 && mOffset <= mLength);
+
+    if (getAccessMode() == ACCESS_BUFFER) {
+        /*
+         * On first access, read or map the entire file.  The caller has
+         * requested buffer access, either because they're going to be
+         * using the buffer or because what they're doing has appropriate
+         * performance needs and access patterns.
+         */
+        if (mBuf == NULL)
+            getBuffer(false);
+    }
+
+    /* adjust count if we're near EOF */
+    maxLen = mLength - mOffset;
+    if (count > maxLen)
+        count = maxLen;
+
+    if (!count)
+        return 0;
+
+    if (mMap != NULL) {
+        /* copy from mapped area */
+        //printf("map read\n");
+        memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
+        actual = count;
+    } else if (mBuf != NULL) {
+        /* copy from buffer */
+        //printf("buf read\n");
+        memcpy(buf, (char*)mBuf + mOffset, count);
+        actual = count;
+    } else {
+        /* read from the file */
+        //printf("file read\n");
+        if (ftell(mFp) != mStart + mOffset) {
+            LOGE("Hosed: %ld != %ld+%ld\n",
+                ftell(mFp), (long) mStart, (long) mOffset);
+            assert(false);
+        }
+
+        /*
+         * This returns 0 on error or eof.  We need to use ferror() or feof()
+         * to tell the difference, but we don't currently have those on the
+         * device.  However, we know how much data is *supposed* to be in the
+         * file, so if we don't read the full amount we know something is
+         * hosed.
+         */
+        actual = fread(buf, 1, count, mFp);
+        if (actual == 0)        // something failed -- I/O error?
+            return -1;
+
+        assert(actual == count);
+    }
+
+    mOffset += actual;
+    return actual;
+}
+
+/*
+ * Seek to a new position.
+ */
+off_t _FileAsset::seek(off_t offset, int whence)
+{
+    off_t newPosn;
+    long actualOffset;
+
+    // compute new position within chunk
+    newPosn = handleSeek(offset, whence, mOffset, mLength);
+    if (newPosn == (off_t) -1)
+        return newPosn;
+
+    actualOffset = (long) (mStart + newPosn);
+
+    if (mFp != NULL) {
+        if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
+            return (off_t) -1;
+    }
+
+    mOffset = actualOffset - mStart;
+    return mOffset;
+}
+
+/*
+ * Close the asset.
+ */
+void _FileAsset::close(void)
+{
+    if (mMap != NULL) {
+        mMap->release();
+        mMap = NULL;
+    }
+    if (mBuf != NULL) {
+        delete[] mBuf;
+        mBuf = NULL;
+    }
+
+    if (mFileName != NULL) {
+        free(mFileName);
+        mFileName = NULL;
+    }
+    
+    if (mFp != NULL) {
+        // can only be NULL when called from destructor
+        // (otherwise we would never return this object)
+        fclose(mFp);
+        mFp = NULL;
+    }
+}
+
+/*
+ * Return a read-only pointer to a buffer.
+ *
+ * We can either read the whole thing in or map the relevant piece of
+ * the source file.  Ideally a map would be established at a higher
+ * level and we'd be using a different object, but we didn't, so we
+ * deal with it here.
+ */
+const void* _FileAsset::getBuffer(bool wordAligned)
+{
+    /* subsequent requests just use what we did previously */
+    if (mBuf != NULL)
+        return mBuf;
+    if (mMap != NULL) {
+        if (!wordAligned) {
+            return  mMap->getDataPtr();
+        }
+        return ensureAlignment(mMap);
+    }
+
+    assert(mFp != NULL);
+
+    if (mLength < kReadVsMapThreshold) {
+        unsigned char* buf;
+        long allocLen;
+
+        /* zero-length files are allowed; not sure about zero-len allocs */
+        /* (works fine with gcc + x86linux) */
+        allocLen = mLength;
+        if (mLength == 0)
+            allocLen = 1;
+
+        buf = new unsigned char[allocLen];
+        if (buf == NULL) {
+            LOGE("alloc of %ld bytes failed\n", (long) allocLen);
+            return NULL;
+        }
+
+        LOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
+        if (mLength > 0) {
+            long oldPosn = ftell(mFp);
+            fseek(mFp, mStart, SEEK_SET);
+            if (fread(buf, 1, mLength, mFp) != (size_t) mLength) {
+                LOGE("failed reading %ld bytes\n", (long) mLength);
+                delete[] buf;
+                return NULL;
+            }
+            fseek(mFp, oldPosn, SEEK_SET);
+        }
+
+        LOGV(" getBuffer: loaded into buffer\n");
+
+        mBuf = buf;
+        return mBuf;
+    } else {
+        FileMap* map;
+
+        map = new FileMap;
+        if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
+            map->release();
+            return NULL;
+        }
+
+        LOGV(" getBuffer: mapped\n");
+
+        mMap = map;
+        if (!wordAligned) {
+            return  mMap->getDataPtr();
+        }
+        return ensureAlignment(mMap);
+    }
+}
+
+int _FileAsset::openFileDescriptor(off_t* outStart, off_t* outLength) const
+{
+    if (mMap != NULL) {
+        const char* fname = mMap->getFileName();
+        if (fname == NULL) {
+            fname = mFileName;
+        }
+        if (fname == NULL) {
+            return -1;
+        }
+        *outStart = mMap->getDataOffset();
+        *outLength = mMap->getDataLength();
+        return open(fname, O_RDONLY | O_BINARY);
+    }
+    if (mFileName == NULL) {
+        return -1;
+    }
+    *outStart = mStart;
+    *outLength = mLength;
+    return open(mFileName, O_RDONLY | O_BINARY);
+}
+
+const void* _FileAsset::ensureAlignment(FileMap* map)
+{
+    void* data = map->getDataPtr();
+    if ((((size_t)data)&0x3) == 0) {
+        // We can return this directly if it is aligned on a word
+        // boundary.
+        return data;
+    }
+    // If not aligned on a word boundary, then we need to copy it into
+    // our own buffer.
+    LOGV("Copying FileAsset %p to buffer size %d to make it aligned.", this, (int)mLength);
+    unsigned char* buf = new unsigned char[mLength];
+    if (buf == NULL) {
+        LOGE("alloc of %ld bytes failed\n", (long) mLength);
+        return NULL;
+    }
+    memcpy(buf, data, mLength);
+    mBuf = buf;
+    return buf;
+}
+
+/*
+ * ===========================================================================
+ *      _CompressedAsset
+ * ===========================================================================
+ */
+
+/*
+ * Constructor.
+ */
+_CompressedAsset::_CompressedAsset(void)
+    : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
+      mMap(NULL), mFd(-1), mBuf(NULL)
+{
+}
+
+/*
+ * Destructor.  Release resources.
+ */
+_CompressedAsset::~_CompressedAsset(void)
+{
+    close();
+}
+
+/*
+ * Open a chunk of compressed data inside a file.
+ *
+ * This currently just sets up some values and returns.  On the first
+ * read, we expand the entire file into a buffer and return data from it.
+ */
+status_t _CompressedAsset::openChunk(int fd, off_t offset,
+    int compressionMethod, size_t uncompressedLen, size_t compressedLen)
+{
+    assert(mFd < 0);        // no re-open
+    assert(mMap == NULL);
+    assert(fd >= 0);
+    assert(offset >= 0);
+    assert(compressedLen > 0);
+
+    if (compressionMethod != ZipFileRO::kCompressDeflated) {
+        assert(false);
+        return UNKNOWN_ERROR;
+    }
+
+    mStart = offset;
+    mCompressedLen = compressedLen;
+    mUncompressedLen = uncompressedLen;
+    assert(mOffset == 0);
+    mFd = fd;
+    assert(mBuf == NULL);
+
+    return NO_ERROR;
+}
+
+/*
+ * Open a chunk of compressed data in a mapped region.
+ *
+ * Nothing is expanded until the first read call.
+ */
+status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod,
+    size_t uncompressedLen)
+{
+    assert(mFd < 0);        // no re-open
+    assert(mMap == NULL);
+    assert(dataMap != NULL);
+
+    if (compressionMethod != ZipFileRO::kCompressDeflated) {
+        assert(false);
+        return UNKNOWN_ERROR;
+    }
+
+    mMap = dataMap;
+    mStart = -1;        // not used
+    mCompressedLen = dataMap->getDataLength();
+    mUncompressedLen = uncompressedLen;
+    assert(mOffset == 0);
+
+    return NO_ERROR;
+}
+
+/*
+ * Read data from a chunk of compressed data.
+ *
+ * [For now, that's just copying data out of a buffer.]
+ */
+ssize_t _CompressedAsset::read(void* buf, size_t count)
+{
+    size_t maxLen;
+    size_t actual;
+
+    assert(mOffset >= 0 && mOffset <= mUncompressedLen);
+
+    // TODO: if mAccessMode == ACCESS_STREAMING, use zlib more cleverly
+
+    if (mBuf == NULL) {
+        if (getBuffer(false) == NULL)
+            return -1;
+    }
+    assert(mBuf != NULL);
+
+    /* adjust count if we're near EOF */
+    maxLen = mUncompressedLen - mOffset;
+    if (count > maxLen)
+        count = maxLen;
+
+    if (!count)
+        return 0;
+
+    /* copy from buffer */
+    //printf("comp buf read\n");
+    memcpy(buf, (char*)mBuf + mOffset, count);
+    actual = count;
+
+    mOffset += actual;
+    return actual;
+}
+
+/*
+ * Handle a seek request.
+ *
+ * If we're working in a streaming mode, this is going to be fairly
+ * expensive, because it requires plowing through a bunch of compressed
+ * data.
+ */
+off_t _CompressedAsset::seek(off_t offset, int whence)
+{
+    off_t newPosn;
+
+    // compute new position within chunk
+    newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
+    if (newPosn == (off_t) -1)
+        return newPosn;
+
+    mOffset = newPosn;
+    return mOffset;
+}
+
+/*
+ * Close the asset.
+ */
+void _CompressedAsset::close(void)
+{
+    if (mMap != NULL) {
+        mMap->release();
+        mMap = NULL;
+    }
+    if (mBuf != NULL) {
+        delete[] mBuf;
+        mBuf = NULL;
+    }
+
+    if (mFd > 0) {
+        ::close(mFd);
+        mFd = -1;
+    }
+}
+
+/*
+ * Get a pointer to a read-only buffer of data.
+ *
+ * The first time this is called, we expand the compressed data into a
+ * buffer.
+ */
+const void* _CompressedAsset::getBuffer(bool wordAligned)
+{
+    unsigned char* buf = NULL;
+
+    if (mBuf != NULL)
+        return mBuf;
+
+    if (mUncompressedLen > UNCOMPRESS_DATA_MAX) {
+        LOGD("Data exceeds UNCOMPRESS_DATA_MAX (%ld vs %d)\n",
+            (long) mUncompressedLen, UNCOMPRESS_DATA_MAX);
+        goto bail;
+    }
+
+    /*
+     * Allocate a buffer and read the file into it.
+     */
+    buf = new unsigned char[mUncompressedLen];
+    if (buf == NULL) {
+        LOGW("alloc %ld bytes failed\n", (long) mUncompressedLen);
+        goto bail;
+    }
+
+    if (mMap != NULL) {
+        if (!ZipFileRO::inflateBuffer(buf, mMap->getDataPtr(),
+                mUncompressedLen, mCompressedLen))
+            goto bail;
+    } else {
+        assert(mFd >= 0);
+
+        /*
+         * Seek to the start of the compressed data.
+         */
+        if (lseek(mFd, mStart, SEEK_SET) != mStart)
+            goto bail;
+
+        /*
+         * Expand the data into it.
+         */
+        if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen,
+                mCompressedLen))
+            goto bail;
+    }
+
+    /* success! */
+    mBuf = buf;
+    buf = NULL;
+
+bail:
+    delete[] buf;
+    return mBuf;
+}
+