Home | History | Annotate | Download | only in androidfw
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //
     18 // Provide access to a read-only asset.
     19 //
     20 
     21 #define LOG_TAG "asset"
     22 //#define NDEBUG 0
     23 
     24 #include <androidfw/Asset.h>
     25 #include <androidfw/StreamingZipInflater.h>
     26 #include <androidfw/ZipFileRO.h>
     27 #include <androidfw/ZipUtils.h>
     28 #include <utils/Atomic.h>
     29 #include <utils/FileMap.h>
     30 #include <utils/Log.h>
     31 #include <utils/threads.h>
     32 
     33 #include <assert.h>
     34 #include <errno.h>
     35 #include <fcntl.h>
     36 #include <memory.h>
     37 #include <string.h>
     38 #include <sys/stat.h>
     39 #include <sys/types.h>
     40 #include <unistd.h>
     41 
     42 using namespace android;
     43 
     44 #ifndef O_BINARY
     45 # define O_BINARY 0
     46 #endif
     47 
     48 static Mutex gAssetLock;
     49 static int32_t gCount = 0;
     50 static Asset* gHead = NULL;
     51 static Asset* gTail = NULL;
     52 
     53 int32_t Asset::getGlobalCount()
     54 {
     55     AutoMutex _l(gAssetLock);
     56     return gCount;
     57 }
     58 
     59 String8 Asset::getAssetAllocations()
     60 {
     61     AutoMutex _l(gAssetLock);
     62     String8 res;
     63     Asset* cur = gHead;
     64     while (cur != NULL) {
     65         if (cur->isAllocated()) {
     66             res.append("    ");
     67             res.append(cur->getAssetSource());
     68             off64_t size = (cur->getLength()+512)/1024;
     69             char buf[64];
     70             sprintf(buf, ": %dK\n", (int)size);
     71             res.append(buf);
     72         }
     73         cur = cur->mNext;
     74     }
     75 
     76     return res;
     77 }
     78 
     79 Asset::Asset(void)
     80     : mAccessMode(ACCESS_UNKNOWN)
     81 {
     82     AutoMutex _l(gAssetLock);
     83     gCount++;
     84     mNext = mPrev = NULL;
     85     if (gTail == NULL) {
     86         gHead = gTail = this;
     87     } else {
     88         mPrev = gTail;
     89         gTail->mNext = this;
     90         gTail = this;
     91     }
     92     //ALOGI("Creating Asset %p #%d\n", this, gCount);
     93 }
     94 
     95 Asset::~Asset(void)
     96 {
     97     AutoMutex _l(gAssetLock);
     98     gCount--;
     99     if (gHead == this) {
    100         gHead = mNext;
    101     }
    102     if (gTail == this) {
    103         gTail = mPrev;
    104     }
    105     if (mNext != NULL) {
    106         mNext->mPrev = mPrev;
    107     }
    108     if (mPrev != NULL) {
    109         mPrev->mNext = mNext;
    110     }
    111     mNext = mPrev = NULL;
    112     //ALOGI("Destroying Asset in %p #%d\n", this, gCount);
    113 }
    114 
    115 /*
    116  * Create a new Asset from a file on disk.  There is a fair chance that
    117  * the file doesn't actually exist.
    118  *
    119  * We can use "mode" to decide how we want to go about it.
    120  */
    121 /*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode)
    122 {
    123     _FileAsset* pAsset;
    124     status_t result;
    125     off64_t length;
    126     int fd;
    127 
    128     fd = open(fileName, O_RDONLY | O_BINARY);
    129     if (fd < 0)
    130         return NULL;
    131 
    132     /*
    133      * Under Linux, the lseek fails if we actually opened a directory.  To
    134      * be correct we should test the file type explicitly, but since we
    135      * always open things read-only it doesn't really matter, so there's
    136      * no value in incurring the extra overhead of an fstat() call.
    137      */
    138     // TODO(kroot): replace this with fstat despite the plea above.
    139 #if 1
    140     length = lseek64(fd, 0, SEEK_END);
    141     if (length < 0) {
    142         ::close(fd);
    143         return NULL;
    144     }
    145     (void) lseek64(fd, 0, SEEK_SET);
    146 #else
    147     struct stat st;
    148     if (fstat(fd, &st) < 0) {
    149         ::close(fd);
    150         return NULL;
    151     }
    152 
    153     if (!S_ISREG(st.st_mode)) {
    154         ::close(fd);
    155         return NULL;
    156     }
    157 #endif
    158 
    159     pAsset = new _FileAsset;
    160     result = pAsset->openChunk(fileName, fd, 0, length);
    161     if (result != NO_ERROR) {
    162         delete pAsset;
    163         return NULL;
    164     }
    165 
    166     pAsset->mAccessMode = mode;
    167     return pAsset;
    168 }
    169 
    170 
    171 /*
    172  * Create a new Asset from a compressed file on disk.  There is a fair chance
    173  * that the file doesn't actually exist.
    174  *
    175  * We currently support gzip files.  We might want to handle .bz2 someday.
    176  */
    177 /*static*/ Asset* Asset::createFromCompressedFile(const char* fileName,
    178     AccessMode mode)
    179 {
    180     _CompressedAsset* pAsset;
    181     status_t result;
    182     off64_t fileLen;
    183     bool scanResult;
    184     long offset;
    185     int method;
    186     long uncompressedLen, compressedLen;
    187     int fd;
    188 
    189     fd = open(fileName, O_RDONLY | O_BINARY);
    190     if (fd < 0)
    191         return NULL;
    192 
    193     fileLen = lseek(fd, 0, SEEK_END);
    194     if (fileLen < 0) {
    195         ::close(fd);
    196         return NULL;
    197     }
    198     (void) lseek(fd, 0, SEEK_SET);
    199 
    200     /* want buffered I/O for the file scan; must dup so fclose() is safe */
    201     FILE* fp = fdopen(dup(fd), "rb");
    202     if (fp == NULL) {
    203         ::close(fd);
    204         return NULL;
    205     }
    206 
    207     unsigned long crc32;
    208     scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen,
    209                     &compressedLen, &crc32);
    210     offset = ftell(fp);
    211     fclose(fp);
    212     if (!scanResult) {
    213         ALOGD("File '%s' is not in gzip format\n", fileName);
    214         ::close(fd);
    215         return NULL;
    216     }
    217 
    218     pAsset = new _CompressedAsset;
    219     result = pAsset->openChunk(fd, offset, method, uncompressedLen,
    220                 compressedLen);
    221     if (result != NO_ERROR) {
    222         delete pAsset;
    223         return NULL;
    224     }
    225 
    226     pAsset->mAccessMode = mode;
    227     return pAsset;
    228 }
    229 
    230 
    231 #if 0
    232 /*
    233  * Create a new Asset from part of an open file.
    234  */
    235 /*static*/ Asset* Asset::createFromFileSegment(int fd, off64_t offset,
    236     size_t length, AccessMode mode)
    237 {
    238     _FileAsset* pAsset;
    239     status_t result;
    240 
    241     pAsset = new _FileAsset;
    242     result = pAsset->openChunk(NULL, fd, offset, length);
    243     if (result != NO_ERROR)
    244         return NULL;
    245 
    246     pAsset->mAccessMode = mode;
    247     return pAsset;
    248 }
    249 
    250 /*
    251  * Create a new Asset from compressed data in an open file.
    252  */
    253 /*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset,
    254     int compressionMethod, size_t uncompressedLen, size_t compressedLen,
    255     AccessMode mode)
    256 {
    257     _CompressedAsset* pAsset;
    258     status_t result;
    259 
    260     pAsset = new _CompressedAsset;
    261     result = pAsset->openChunk(fd, offset, compressionMethod,
    262                 uncompressedLen, compressedLen);
    263     if (result != NO_ERROR)
    264         return NULL;
    265 
    266     pAsset->mAccessMode = mode;
    267     return pAsset;
    268 }
    269 #endif
    270 
    271 /*
    272  * Create a new Asset from a memory mapping.
    273  */
    274 /*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap,
    275     AccessMode mode)
    276 {
    277     _FileAsset* pAsset;
    278     status_t result;
    279 
    280     pAsset = new _FileAsset;
    281     result = pAsset->openChunk(dataMap);
    282     if (result != NO_ERROR)
    283         return NULL;
    284 
    285     pAsset->mAccessMode = mode;
    286     return pAsset;
    287 }
    288 
    289 /*
    290  * Create a new Asset from compressed data in a memory mapping.
    291  */
    292 /*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap,
    293     int method, size_t uncompressedLen, AccessMode mode)
    294 {
    295     _CompressedAsset* pAsset;
    296     status_t result;
    297 
    298     pAsset = new _CompressedAsset;
    299     result = pAsset->openChunk(dataMap, method, uncompressedLen);
    300     if (result != NO_ERROR)
    301         return NULL;
    302 
    303     pAsset->mAccessMode = mode;
    304     return pAsset;
    305 }
    306 
    307 
    308 /*
    309  * Do generic seek() housekeeping.  Pass in the offset/whence values from
    310  * the seek request, along with the current chunk offset and the chunk
    311  * length.
    312  *
    313  * Returns the new chunk offset, or -1 if the seek is illegal.
    314  */
    315 off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn)
    316 {
    317     off64_t newOffset;
    318 
    319     switch (whence) {
    320     case SEEK_SET:
    321         newOffset = offset;
    322         break;
    323     case SEEK_CUR:
    324         newOffset = curPosn + offset;
    325         break;
    326     case SEEK_END:
    327         newOffset = maxPosn + offset;
    328         break;
    329     default:
    330         ALOGW("unexpected whence %d\n", whence);
    331         // this was happening due to an off64_t size mismatch
    332         assert(false);
    333         return (off64_t) -1;
    334     }
    335 
    336     if (newOffset < 0 || newOffset > maxPosn) {
    337         ALOGW("seek out of range: want %ld, end=%ld\n",
    338             (long) newOffset, (long) maxPosn);
    339         return (off64_t) -1;
    340     }
    341 
    342     return newOffset;
    343 }
    344 
    345 
    346 /*
    347  * ===========================================================================
    348  *      _FileAsset
    349  * ===========================================================================
    350  */
    351 
    352 /*
    353  * Constructor.
    354  */
    355 _FileAsset::_FileAsset(void)
    356     : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
    357 {
    358 }
    359 
    360 /*
    361  * Destructor.  Release resources.
    362  */
    363 _FileAsset::~_FileAsset(void)
    364 {
    365     close();
    366 }
    367 
    368 /*
    369  * Operate on a chunk of an uncompressed file.
    370  *
    371  * Zero-length chunks are allowed.
    372  */
    373 status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length)
    374 {
    375     assert(mFp == NULL);    // no reopen
    376     assert(mMap == NULL);
    377     assert(fd >= 0);
    378     assert(offset >= 0);
    379 
    380     /*
    381      * Seek to end to get file length.
    382      */
    383     off64_t fileLength;
    384     fileLength = lseek64(fd, 0, SEEK_END);
    385     if (fileLength == (off64_t) -1) {
    386         // probably a bad file descriptor
    387         ALOGD("failed lseek (errno=%d)\n", errno);
    388         return UNKNOWN_ERROR;
    389     }
    390 
    391     if ((off64_t) (offset + length) > fileLength) {
    392         ALOGD("start (%ld) + len (%ld) > end (%ld)\n",
    393             (long) offset, (long) length, (long) fileLength);
    394         return BAD_INDEX;
    395     }
    396 
    397     /* after fdopen, the fd will be closed on fclose() */
    398     mFp = fdopen(fd, "rb");
    399     if (mFp == NULL)
    400         return UNKNOWN_ERROR;
    401 
    402     mStart = offset;
    403     mLength = length;
    404     assert(mOffset == 0);
    405 
    406     /* seek the FILE* to the start of chunk */
    407     if (fseek(mFp, mStart, SEEK_SET) != 0) {
    408         assert(false);
    409     }
    410 
    411     mFileName = fileName != NULL ? strdup(fileName) : NULL;
    412 
    413     return NO_ERROR;
    414 }
    415 
    416 /*
    417  * Create the chunk from the map.
    418  */
    419 status_t _FileAsset::openChunk(FileMap* dataMap)
    420 {
    421     assert(mFp == NULL);    // no reopen
    422     assert(mMap == NULL);
    423     assert(dataMap != NULL);
    424 
    425     mMap = dataMap;
    426     mStart = -1;            // not used
    427     mLength = dataMap->getDataLength();
    428     assert(mOffset == 0);
    429 
    430     return NO_ERROR;
    431 }
    432 
    433 /*
    434  * Read a chunk of data.
    435  */
    436 ssize_t _FileAsset::read(void* buf, size_t count)
    437 {
    438     size_t maxLen;
    439     size_t actual;
    440 
    441     assert(mOffset >= 0 && mOffset <= mLength);
    442 
    443     if (getAccessMode() == ACCESS_BUFFER) {
    444         /*
    445          * On first access, read or map the entire file.  The caller has
    446          * requested buffer access, either because they're going to be
    447          * using the buffer or because what they're doing has appropriate
    448          * performance needs and access patterns.
    449          */
    450         if (mBuf == NULL)
    451             getBuffer(false);
    452     }
    453 
    454     /* adjust count if we're near EOF */
    455     maxLen = mLength - mOffset;
    456     if (count > maxLen)
    457         count = maxLen;
    458 
    459     if (!count)
    460         return 0;
    461 
    462     if (mMap != NULL) {
    463         /* copy from mapped area */
    464         //printf("map read\n");
    465         memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count);
    466         actual = count;
    467     } else if (mBuf != NULL) {
    468         /* copy from buffer */
    469         //printf("buf read\n");
    470         memcpy(buf, (char*)mBuf + mOffset, count);
    471         actual = count;
    472     } else {
    473         /* read from the file */
    474         //printf("file read\n");
    475         if (ftell(mFp) != mStart + mOffset) {
    476             ALOGE("Hosed: %ld != %ld+%ld\n",
    477                 ftell(mFp), (long) mStart, (long) mOffset);
    478             assert(false);
    479         }
    480 
    481         /*
    482          * This returns 0 on error or eof.  We need to use ferror() or feof()
    483          * to tell the difference, but we don't currently have those on the
    484          * device.  However, we know how much data is *supposed* to be in the
    485          * file, so if we don't read the full amount we know something is
    486          * hosed.
    487          */
    488         actual = fread(buf, 1, count, mFp);
    489         if (actual == 0)        // something failed -- I/O error?
    490             return -1;
    491 
    492         assert(actual == count);
    493     }
    494 
    495     mOffset += actual;
    496     return actual;
    497 }
    498 
    499 /*
    500  * Seek to a new position.
    501  */
    502 off64_t _FileAsset::seek(off64_t offset, int whence)
    503 {
    504     off64_t newPosn;
    505     off64_t actualOffset;
    506 
    507     // compute new position within chunk
    508     newPosn = handleSeek(offset, whence, mOffset, mLength);
    509     if (newPosn == (off64_t) -1)
    510         return newPosn;
    511 
    512     actualOffset = mStart + newPosn;
    513 
    514     if (mFp != NULL) {
    515         if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0)
    516             return (off64_t) -1;
    517     }
    518 
    519     mOffset = actualOffset - mStart;
    520     return mOffset;
    521 }
    522 
    523 /*
    524  * Close the asset.
    525  */
    526 void _FileAsset::close(void)
    527 {
    528     if (mMap != NULL) {
    529         mMap->release();
    530         mMap = NULL;
    531     }
    532     if (mBuf != NULL) {
    533         delete[] mBuf;
    534         mBuf = NULL;
    535     }
    536 
    537     if (mFileName != NULL) {
    538         free(mFileName);
    539         mFileName = NULL;
    540     }
    541 
    542     if (mFp != NULL) {
    543         // can only be NULL when called from destructor
    544         // (otherwise we would never return this object)
    545         fclose(mFp);
    546         mFp = NULL;
    547     }
    548 }
    549 
    550 /*
    551  * Return a read-only pointer to a buffer.
    552  *
    553  * We can either read the whole thing in or map the relevant piece of
    554  * the source file.  Ideally a map would be established at a higher
    555  * level and we'd be using a different object, but we didn't, so we
    556  * deal with it here.
    557  */
    558 const void* _FileAsset::getBuffer(bool wordAligned)
    559 {
    560     /* subsequent requests just use what we did previously */
    561     if (mBuf != NULL)
    562         return mBuf;
    563     if (mMap != NULL) {
    564         if (!wordAligned) {
    565             return  mMap->getDataPtr();
    566         }
    567         return ensureAlignment(mMap);
    568     }
    569 
    570     assert(mFp != NULL);
    571 
    572     if (mLength < kReadVsMapThreshold) {
    573         unsigned char* buf;
    574         long allocLen;
    575 
    576         /* zero-length files are allowed; not sure about zero-len allocs */
    577         /* (works fine with gcc + x86linux) */
    578         allocLen = mLength;
    579         if (mLength == 0)
    580             allocLen = 1;
    581 
    582         buf = new unsigned char[allocLen];
    583         if (buf == NULL) {
    584             ALOGE("alloc of %ld bytes failed\n", (long) allocLen);
    585             return NULL;
    586         }
    587 
    588         ALOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen);
    589         if (mLength > 0) {
    590             long oldPosn = ftell(mFp);
    591             fseek(mFp, mStart, SEEK_SET);
    592             if (fread(buf, 1, mLength, mFp) != (size_t) mLength) {
    593                 ALOGE("failed reading %ld bytes\n", (long) mLength);
    594                 delete[] buf;
    595                 return NULL;
    596             }
    597             fseek(mFp, oldPosn, SEEK_SET);
    598         }
    599 
    600         ALOGV(" getBuffer: loaded into buffer\n");
    601 
    602         mBuf = buf;
    603         return mBuf;
    604     } else {
    605         FileMap* map;
    606 
    607         map = new FileMap;
    608         if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) {
    609             map->release();
    610             return NULL;
    611         }
    612 
    613         ALOGV(" getBuffer: mapped\n");
    614 
    615         mMap = map;
    616         if (!wordAligned) {
    617             return  mMap->getDataPtr();
    618         }
    619         return ensureAlignment(mMap);
    620     }
    621 }
    622 
    623 int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const
    624 {
    625     if (mMap != NULL) {
    626         const char* fname = mMap->getFileName();
    627         if (fname == NULL) {
    628             fname = mFileName;
    629         }
    630         if (fname == NULL) {
    631             return -1;
    632         }
    633         *outStart = mMap->getDataOffset();
    634         *outLength = mMap->getDataLength();
    635         return open(fname, O_RDONLY | O_BINARY);
    636     }
    637     if (mFileName == NULL) {
    638         return -1;
    639     }
    640     *outStart = mStart;
    641     *outLength = mLength;
    642     return open(mFileName, O_RDONLY | O_BINARY);
    643 }
    644 
    645 const void* _FileAsset::ensureAlignment(FileMap* map)
    646 {
    647     void* data = map->getDataPtr();
    648     if ((((size_t)data)&0x3) == 0) {
    649         // We can return this directly if it is aligned on a word
    650         // boundary.
    651         ALOGV("Returning aligned FileAsset %p (%s).", this,
    652                 getAssetSource());
    653         return data;
    654     }
    655     // If not aligned on a word boundary, then we need to copy it into
    656     // our own buffer.
    657     ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this,
    658             getAssetSource(), (int)mLength);
    659     unsigned char* buf = new unsigned char[mLength];
    660     if (buf == NULL) {
    661         ALOGE("alloc of %ld bytes failed\n", (long) mLength);
    662         return NULL;
    663     }
    664     memcpy(buf, data, mLength);
    665     mBuf = buf;
    666     return buf;
    667 }
    668 
    669 /*
    670  * ===========================================================================
    671  *      _CompressedAsset
    672  * ===========================================================================
    673  */
    674 
    675 /*
    676  * Constructor.
    677  */
    678 _CompressedAsset::_CompressedAsset(void)
    679     : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
    680       mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
    681 {
    682 }
    683 
    684 /*
    685  * Destructor.  Release resources.
    686  */
    687 _CompressedAsset::~_CompressedAsset(void)
    688 {
    689     close();
    690 }
    691 
    692 /*
    693  * Open a chunk of compressed data inside a file.
    694  *
    695  * This currently just sets up some values and returns.  On the first
    696  * read, we expand the entire file into a buffer and return data from it.
    697  */
    698 status_t _CompressedAsset::openChunk(int fd, off64_t offset,
    699     int compressionMethod, size_t uncompressedLen, size_t compressedLen)
    700 {
    701     assert(mFd < 0);        // no re-open
    702     assert(mMap == NULL);
    703     assert(fd >= 0);
    704     assert(offset >= 0);
    705     assert(compressedLen > 0);
    706 
    707     if (compressionMethod != ZipFileRO::kCompressDeflated) {
    708         assert(false);
    709         return UNKNOWN_ERROR;
    710     }
    711 
    712     mStart = offset;
    713     mCompressedLen = compressedLen;
    714     mUncompressedLen = uncompressedLen;
    715     assert(mOffset == 0);
    716     mFd = fd;
    717     assert(mBuf == NULL);
    718 
    719     if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
    720         mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen);
    721     }
    722 
    723     return NO_ERROR;
    724 }
    725 
    726 /*
    727  * Open a chunk of compressed data in a mapped region.
    728  *
    729  * Nothing is expanded until the first read call.
    730  */
    731 status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod,
    732     size_t uncompressedLen)
    733 {
    734     assert(mFd < 0);        // no re-open
    735     assert(mMap == NULL);
    736     assert(dataMap != NULL);
    737 
    738     if (compressionMethod != ZipFileRO::kCompressDeflated) {
    739         assert(false);
    740         return UNKNOWN_ERROR;
    741     }
    742 
    743     mMap = dataMap;
    744     mStart = -1;        // not used
    745     mCompressedLen = dataMap->getDataLength();
    746     mUncompressedLen = uncompressedLen;
    747     assert(mOffset == 0);
    748 
    749     if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) {
    750         mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen);
    751     }
    752     return NO_ERROR;
    753 }
    754 
    755 /*
    756  * Read data from a chunk of compressed data.
    757  *
    758  * [For now, that's just copying data out of a buffer.]
    759  */
    760 ssize_t _CompressedAsset::read(void* buf, size_t count)
    761 {
    762     size_t maxLen;
    763     size_t actual;
    764 
    765     assert(mOffset >= 0 && mOffset <= mUncompressedLen);
    766 
    767     /* If we're relying on a streaming inflater, go through that */
    768     if (mZipInflater) {
    769         actual = mZipInflater->read(buf, count);
    770     } else {
    771         if (mBuf == NULL) {
    772             if (getBuffer(false) == NULL)
    773                 return -1;
    774         }
    775         assert(mBuf != NULL);
    776 
    777         /* adjust count if we're near EOF */
    778         maxLen = mUncompressedLen - mOffset;
    779         if (count > maxLen)
    780             count = maxLen;
    781 
    782         if (!count)
    783             return 0;
    784 
    785         /* copy from buffer */
    786         //printf("comp buf read\n");
    787         memcpy(buf, (char*)mBuf + mOffset, count);
    788         actual = count;
    789     }
    790 
    791     mOffset += actual;
    792     return actual;
    793 }
    794 
    795 /*
    796  * Handle a seek request.
    797  *
    798  * If we're working in a streaming mode, this is going to be fairly
    799  * expensive, because it requires plowing through a bunch of compressed
    800  * data.
    801  */
    802 off64_t _CompressedAsset::seek(off64_t offset, int whence)
    803 {
    804     off64_t newPosn;
    805 
    806     // compute new position within chunk
    807     newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen);
    808     if (newPosn == (off64_t) -1)
    809         return newPosn;
    810 
    811     if (mZipInflater) {
    812         mZipInflater->seekAbsolute(newPosn);
    813     }
    814     mOffset = newPosn;
    815     return mOffset;
    816 }
    817 
    818 /*
    819  * Close the asset.
    820  */
    821 void _CompressedAsset::close(void)
    822 {
    823     if (mMap != NULL) {
    824         mMap->release();
    825         mMap = NULL;
    826     }
    827 
    828     delete[] mBuf;
    829     mBuf = NULL;
    830 
    831     delete mZipInflater;
    832     mZipInflater = NULL;
    833 
    834     if (mFd > 0) {
    835         ::close(mFd);
    836         mFd = -1;
    837     }
    838 }
    839 
    840 /*
    841  * Get a pointer to a read-only buffer of data.
    842  *
    843  * The first time this is called, we expand the compressed data into a
    844  * buffer.
    845  */
    846 const void* _CompressedAsset::getBuffer(bool)
    847 {
    848     unsigned char* buf = NULL;
    849 
    850     if (mBuf != NULL)
    851         return mBuf;
    852 
    853     /*
    854      * Allocate a buffer and read the file into it.
    855      */
    856     buf = new unsigned char[mUncompressedLen];
    857     if (buf == NULL) {
    858         ALOGW("alloc %ld bytes failed\n", (long) mUncompressedLen);
    859         goto bail;
    860     }
    861 
    862     if (mMap != NULL) {
    863         if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf,
    864                 mUncompressedLen, mCompressedLen))
    865             goto bail;
    866     } else {
    867         assert(mFd >= 0);
    868 
    869         /*
    870          * Seek to the start of the compressed data.
    871          */
    872         if (lseek(mFd, mStart, SEEK_SET) != mStart)
    873             goto bail;
    874 
    875         /*
    876          * Expand the data into it.
    877          */
    878         if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen,
    879                 mCompressedLen))
    880             goto bail;
    881     }
    882 
    883     /*
    884      * Success - now that we have the full asset in RAM we
    885      * no longer need the streaming inflater
    886      */
    887     delete mZipInflater;
    888     mZipInflater = NULL;
    889 
    890     mBuf = buf;
    891     buf = NULL;
    892 
    893 bail:
    894     delete[] buf;
    895     return mBuf;
    896 }
    897 
    898