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