• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
registerAsset(Asset * asset)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 
unregisterAsset(Asset * asset)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 
getGlobalCount()97 int32_t Asset::getGlobalCount()
98 {
99     AutoMutex _l(gAssetLock);
100     return gCount;
101 }
102 
getAssetAllocations()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 
Asset(void)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  */
createFromFile(const char * fileName,AccessMode mode)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  */
createFromCompressedFile(const char * fileName,AccessMode mode)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  */
createFromUncompressedMap(FileMap * dataMap,AccessMode mode)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 
createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,AccessMode mode)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  */
createFromCompressedMap(FileMap * dataMap,size_t uncompressedLen,AccessMode mode)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 
createFromCompressedMap(std::unique_ptr<FileMap> dataMap,size_t uncompressedLen,AccessMode mode)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  */
handleSeek(off64_t offset,int whence,off64_t curPosn,off64_t maxPosn)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  */
_FileAsset(void)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  */
~_FileAsset(void)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  */
openChunk(const char * fileName,int fd,off64_t offset,size_t length)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  */
openChunk(FileMap * dataMap)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  */
read(void * buf,size_t count)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  */
seek(off64_t offset,int whence)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  */
close(void)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  */
getBuffer(bool wordAligned)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 
openFileDescriptor(off64_t * outStart,off64_t * outLength) const674 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 
ensureAlignment(FileMap * map)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  */
_CompressedAsset(void)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  */
~_CompressedAsset(void)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  */
openChunk(int fd,off64_t offset,int compressionMethod,size_t uncompressedLen,size_t compressedLen)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  */
openChunk(FileMap * dataMap,size_t uncompressedLen)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  */
read(void * buf,size_t count)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  */
seek(off64_t offset,int whence)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  */
close(void)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  */
getBuffer(bool)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