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