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