Reimplement ZipFileRO in terms of libziparchive.

This lets us share zip archive processing code with both
the runtime (Art, dalvik) and critical java code
(StrictJarFile).

This change also moves several utility methods to ZipUtils
and dedups code across several zip inflation methods.

One of the side effects of this change is that several
processing loops are now O(n) instead of O(n^2).

bug: 10193060

(cherry picked from commit afd31e08299008fdc5c2813f21b2573f29dc53df)

Change-Id: Iae67e62f1dc6dfc3f43e29bc38e3ffd1cb14d191
diff --git a/include/androidfw/ZipFileRO.h b/include/androidfw/ZipFileRO.h
index 547e36a..ad5be12 100644
--- a/include/androidfw/ZipFileRO.h
+++ b/include/androidfw/ZipFileRO.h
@@ -40,6 +40,8 @@
 #include <unistd.h>
 #include <time.h>
 
+typedef void* ZipArchiveHandle;
+
 namespace android {
 
 /*
@@ -51,18 +53,13 @@
 /*
  * Open a Zip archive for reading.
  *
- * We want "open" and "find entry by name" to be fast operations, and we
- * want to use as little memory as possible.  We memory-map the file,
- * and load a hash table with pointers to the filenames (which aren't
- * null-terminated).  The other fields are at a fixed offset from the
- * filename, so we don't need to extract those (but we do need to byte-read
- * and endian-swap them every time we want them).
+ * Implemented as a thin wrapper over system/core/libziparchive.
  *
- * To speed comparisons when doing a lookup by name, we could make the mapping
- * "private" (copy-on-write) and null-terminate the filenames after verifying
- * the record structure.  However, this requires a private mapping of
- * every page that the Central Directory touches.  Easier to tuck a copy
- * of the string length into the hash table entry.
+ * "open" and "find entry by name" are fast operations and use as little
+ * memory as possible.
+ *
+ * We also support fast iteration over all entries in the file (with a
+ * stable, but unspecified iteration order).
  *
  * NOTE: If this is used on file descriptors inherited from a fork() operation,
  * you must be on a platform that implements pread() to guarantee correctness
@@ -70,48 +67,44 @@
  */
 class ZipFileRO {
 public:
-    ZipFileRO()
-        : mFd(-1), mFileName(NULL), mFileLength(-1),
-          mDirectoryMap(NULL),
-          mNumEntries(-1), mDirectoryOffset(-1),
-          mHashTableSize(-1), mHashTable(NULL)
-        {}
-
-    ~ZipFileRO();
+    /* Zip compression methods we support */
+    enum {
+        kCompressStored     = 0,        // no compression
+        kCompressDeflated   = 8,        // standard deflate
+    };
 
     /*
      * Open an archive.
      */
-    status_t open(const char* zipFileName);
+    static ZipFileRO* open(const char* zipFileName);
 
     /*
      * Find an entry, by name.  Returns the entry identifier, or NULL if
      * not found.
-     *
-     * If two entries have the same name, one will be chosen at semi-random.
      */
-    ZipEntryRO findEntryByName(const char* fileName) const;
+    ZipEntryRO findEntryByName(const char* entryName) const;
+
+
+    /*
+     * Start iterating over the list of entries in the zip file. Requires
+     * a matching call to endIteration with the same cookie.
+     */
+    bool startIteration(void** cookie);
+
+    /**
+     * Return the next entry in iteration order, or NULL if there are no more
+     * entries in this archive.
+     */
+    ZipEntryRO nextEntry(void* cookie);
+
+    void endIteration(void* cookie);
+
+    void releaseEntry(ZipEntryRO entry) const;
 
     /*
      * Return the #of entries in the Zip archive.
      */
-    int getNumEntries(void) const {
-        return mNumEntries;
-    }
-
-    /*
-     * Return the Nth entry.  Zip file entries are not stored in sorted
-     * order, and updated entries may appear at the end, so anyone walking
-     * the archive needs to avoid making ordering assumptions.  We take
-     * that further by returning the Nth non-empty entry in the hash table
-     * rather than the Nth entry in the archive.
-     *
-     * Valid values are [0..numEntries).
-     *
-     * [This is currently O(n).  If it needs to be fast we can allocate an
-     * additional data structure or provide an iterator interface.]
-     */
-    ZipEntryRO findEntryByIndex(int idx) const;
+    int getNumEntries();
 
     /*
      * Copy the filename into the supplied buffer.  Returns 0 on success,
@@ -149,112 +142,27 @@
      *
      * Returns "true" on success.
      */
-    bool uncompressEntry(ZipEntryRO entry, void* buffer) const;
+    bool uncompressEntry(ZipEntryRO entry, void* buffer, size_t size) const;
 
     /*
      * Uncompress the data to an open file descriptor.
      */
     bool uncompressEntry(ZipEntryRO entry, int fd) const;
 
-    /* Zip compression methods we support */
-    enum {
-        kCompressStored     = 0,        // no compression
-        kCompressDeflated   = 8,        // standard deflate
-    };
-
-    /*
-     * Utility function: uncompress deflated data, buffer to buffer.
-     */
-    static bool inflateBuffer(void* outBuf, const void* inBuf,
-        size_t uncompLen, size_t compLen);
-
-    /*
-     * Utility function: uncompress deflated data, buffer to fd.
-     */
-    static bool inflateBuffer(int fd, const void* inBuf,
-        size_t uncompLen, size_t compLen);
-
-    /*
-     * Utility function to convert ZIP's time format to a timespec struct.
-     */
-    static inline void zipTimeToTimespec(long when, struct tm* timespec) {
-        const long date = when >> 16;
-        timespec->tm_year = ((date >> 9) & 0x7F) + 80; // Zip is years since 1980
-        timespec->tm_mon = (date >> 5) & 0x0F;
-        timespec->tm_mday = date & 0x1F;
-
-        timespec->tm_hour = (when >> 11) & 0x1F;
-        timespec->tm_min = (when >> 5) & 0x3F;
-        timespec->tm_sec = (when & 0x1F) << 1;
-    }
-
-    /*
-     * Some basic functions for raw data manipulation.  "LE" means
-     * Little Endian.
-     */
-    static inline unsigned short get2LE(const unsigned char* buf) {
-        return buf[0] | (buf[1] << 8);
-    }
-    static inline unsigned long get4LE(const unsigned char* buf) {
-        return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
-    }
+    ~ZipFileRO();
 
 private:
-    /* these are private and not defined */ 
+    /* these are private and not defined */
     ZipFileRO(const ZipFileRO& src);
     ZipFileRO& operator=(const ZipFileRO& src);
 
-    /* locate and parse the central directory */
-    bool mapCentralDirectory(void);
+    ZipFileRO(ZipArchiveHandle handle, char* fileName) : mHandle(handle),
+        mFileName(fileName)
+    {
+    }
 
-    /* parse the archive, prepping internal structures */
-    bool parseZipArchive(void);
-
-    /* add a new entry to the hash table */
-    void addToHash(const char* str, int strLen, unsigned int hash);
-
-    /* compute string hash code */
-    static unsigned int computeHash(const char* str, int len);
-
-    /* convert a ZipEntryRO back to a hash table index */
-    int entryToIndex(const ZipEntryRO entry) const;
-
-    /*
-     * One entry in the hash table.
-     */
-    typedef struct HashEntry {
-        const char*     name;
-        unsigned short  nameLen;
-        //unsigned int    hash;
-    } HashEntry;
-
-    /* open Zip archive */
-    int         mFd;
-
-    /* Lock for handling the file descriptor (seeks, etc) */
-    mutable Mutex mFdLock;
-
-    /* zip file name */
-    char*       mFileName;
-
-    /* length of file */
-    size_t      mFileLength;
-
-    /* mapped file */
-    FileMap*    mDirectoryMap;
-
-    /* number of entries in the Zip archive */
-    int         mNumEntries;
-
-    /* CD directory offset in the Zip archive */
-    off64_t     mDirectoryOffset;
-
-    /*
-     * We know how many entries are in the Zip archive, so we have a
-     * fixed-size hash table.  We probe for an empty slot.
-     */
-    int         mHashTableSize;
-    HashEntry*  mHashTable;
+    const ZipArchiveHandle mHandle;
+    char* mFileName;
 };
 
 }; // namespace android
diff --git a/include/androidfw/ZipUtils.h b/include/androidfw/ZipUtils.h
index 42c42b6..6bea25a 100644
--- a/include/androidfw/ZipUtils.h
+++ b/include/androidfw/ZipUtils.h
@@ -21,6 +21,7 @@
 #define __LIBS_ZIPUTILS_H
 
 #include <stdio.h>
+#include <time.h>
 
 namespace android {
 
@@ -33,9 +34,11 @@
      * General utility function for uncompressing "deflate" data from a file
      * to a buffer.
      */
+    static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen,
+        long compressedLen);
     static bool inflateToBuffer(int fd, void* buf, long uncompressedLen,
         long compressedLen);
-    static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen,
+    static bool inflateToBuffer(void *in, void* buf, long uncompressedLen,
         long compressedLen);
 
     /*
@@ -57,6 +60,19 @@
     static bool examineGzip(FILE* fp, int* pCompressionMethod,
         long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32);
 
+    /*
+     * Utility function to convert ZIP's time format to a timespec struct.
+     */
+    static inline void zipTimeToTimespec(long when, struct tm* timespec) {
+        const long date = when >> 16;
+        timespec->tm_year = ((date >> 9) & 0x7F) + 80; // Zip is years since 1980
+        timespec->tm_mon = (date >> 5) & 0x0F;
+        timespec->tm_mday = date & 0x1F;
+
+        timespec->tm_hour = (when >> 11) & 0x1F;
+        timespec->tm_min = (when >> 5) & 0x3F;
+        timespec->tm_sec = (when & 0x1F) << 1;
+    }
 private:
     ZipUtils() {}
     ~ZipUtils() {}
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
index d80612b..ba13d51 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -54,6 +54,7 @@
 	external/zlib
 
 LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_WHOLE_STATIC_LIBRARIES := libziparchive-host
 
 include $(BUILD_HOST_STATIC_LIBRARY)
 
@@ -72,9 +73,12 @@
 	libutils \
 	libz
 
+LOCAL_STATIC_LIBRARIES := libziparchive
+
 LOCAL_C_INCLUDES := \
     external/icu4c/common \
-	external/zlib
+    external/zlib \
+    system/core/include
 
 LOCAL_MODULE:= libandroidfw
 
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index cb7628d..ce6cc38 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -843,7 +843,7 @@
  * The first time this is called, we expand the compressed data into a
  * buffer.
  */
-const void* _CompressedAsset::getBuffer(bool wordAligned)
+const void* _CompressedAsset::getBuffer(bool)
 {
     unsigned char* buf = NULL;
 
@@ -860,7 +860,7 @@
     }
 
     if (mMap != NULL) {
-        if (!ZipFileRO::inflateBuffer(buf, mMap->getDataPtr(),
+        if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf,
                 mUncompressedLen, mCompressedLen))
             goto bail;
     } else {
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 1f0d530..8651764 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -305,10 +305,11 @@
     if (entry == NULL) {
         return false;
     }
-    if (!zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc)) {
-        return false;
-    }
-    return true;
+
+    const bool gotInfo = zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc);
+    zip->releaseEntry(entry);
+
+    return gotInfo;
 }
 
 bool AssetManager::createIdmapFileLocked(const String8& originalPath, const String8& overlayPath,
@@ -821,16 +822,14 @@
         String8 path(fileName);
 
         /* check the appropriate Zip file */
-        ZipFileRO* pZip;
-        ZipEntryRO entry;
-
-        pZip = getZipFileLocked(ap);
+        ZipFileRO* pZip = getZipFileLocked(ap);
         if (pZip != NULL) {
             //printf("GOT zip, checking NA '%s'\n", (const char*) path);
-            entry = pZip->findEntryByName(path.string());
+            ZipEntryRO entry = pZip->findEntryByName(path.string());
             if (entry != NULL) {
                 //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon);
                 pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
+                pZip->releaseEntry(entry);
             }
         }
 
@@ -975,17 +974,15 @@
         path.appendPath(fileName);
 
         /* check the appropriate Zip file */
-        ZipFileRO* pZip;
-        ZipEntryRO entry;
-
-        pZip = getZipFileLocked(ap);
+        ZipFileRO* pZip = getZipFileLocked(ap);
         if (pZip != NULL) {
             //printf("GOT zip, checking '%s'\n", (const char*) path);
-            entry = pZip->findEntryByName(path.string());
+            ZipEntryRO entry = pZip->findEntryByName(path.string());
             if (entry != NULL) {
                 //printf("FOUND in Zip file for %s/%s-%s\n",
                 //    appName, locale, vendor);
                 pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
+                pZip->releaseEntry(entry);
             }
         }
 
@@ -1487,11 +1484,16 @@
      * semantics.
      */
     int dirNameLen = dirName.length();
-    for (int i = 0; i < pZip->getNumEntries(); i++) {
-        ZipEntryRO entry;
+    void *iterationCookie;
+    if (!pZip->startIteration(&iterationCookie)) {
+        ALOGW("ZipFileRO::startIteration returned false");
+        return false;
+    }
+
+    ZipEntryRO entry;
+    while ((entry = pZip->nextEntry(iterationCookie)) != NULL) {
         char nameBuf[256];
 
-        entry = pZip->findEntryByIndex(i);
         if (pZip->getEntryFileName(entry, nameBuf, sizeof(nameBuf)) != 0) {
             // TODO: fix this if we expect to have long names
             ALOGE("ARGH: name too long?\n");
@@ -1541,6 +1543,8 @@
         }
     }
 
+    pZip->endIteration(iterationCookie);
+
     /*
      * Add the set of unique directories.
      */
@@ -1814,12 +1818,10 @@
       mResourceTableAsset(NULL), mResourceTable(NULL)
 {
     //ALOGI("Creating SharedZip %p %s\n", this, (const char*)mPath);
-    mZipFile = new ZipFileRO;
     ALOGV("+++ opening zip '%s'\n", mPath.string());
-    if (mZipFile->open(mPath.string()) != NO_ERROR) {
+    mZipFile = ZipFileRO::open(mPath.string());
+    if (mZipFile == NULL) {
         ALOGD("failed to open Zip archive '%s'\n", mPath.string());
-        delete mZipFile;
-        mZipFile = NULL;
     }
 }
 
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index ec5f95c..1ab18ad 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -24,6 +24,7 @@
 #include <utils/Compat.h>
 #include <utils/misc.h>
 #include <utils/threads.h>
+#include <ziparchive/zip_archive.h>
 
 #include <zlib.h>
 
@@ -43,460 +44,55 @@
 
 using namespace android;
 
-/*
- * Zip file constants.
- */
-#define kEOCDSignature       0x06054b50
-#define kEOCDLen             22
-#define kEOCDDiskNumber      4               // number of the current disk
-#define kEOCDDiskNumberForCD 6               // disk number with the Central Directory
-#define kEOCDNumEntries      8               // offset to #of entries in file
-#define kEOCDTotalNumEntries 10              // offset to total #of entries in spanned archives
-#define kEOCDSize            12              // size of the central directory
-#define kEOCDFileOffset      16              // offset to central directory
-#define kEOCDCommentSize     20              // offset to the length of the file comment
+class _ZipEntryRO {
+public:
+    ZipEntry entry;
+    ZipEntryName name;
+    void *cookie;
 
-#define kMaxCommentLen       65535           // longest possible in ushort
-#define kMaxEOCDSearch       (kMaxCommentLen + kEOCDLen)
+    _ZipEntryRO() : cookie(NULL) {
+    }
 
-#define kLFHSignature        0x04034b50
-#define kLFHLen              30              // excluding variable-len fields
-#define kLFHGPBFlags          6              // offset to GPB flags
-#define kLFHNameLen          26              // offset to filename length
-#define kLFHExtraLen         28              // offset to extra length
-
-#define kCDESignature        0x02014b50
-#define kCDELen              46              // excluding variable-len fields
-#define kCDEGPBFlags          8              // offset to GPB flags
-#define kCDEMethod           10              // offset to compression method
-#define kCDEModWhen          12              // offset to modification timestamp
-#define kCDECRC              16              // offset to entry CRC
-#define kCDECompLen          20              // offset to compressed length
-#define kCDEUncompLen        24              // offset to uncompressed length
-#define kCDENameLen          28              // offset to filename length
-#define kCDEExtraLen         30              // offset to extra length
-#define kCDECommentLen       32              // offset to comment length
-#define kCDELocalOffset      42              // offset to local hdr
-
-/* General Purpose Bit Flag */
-#define kGPFEncryptedFlag    (1 << 0)
-#define kGPFUnsupportedMask  (kGPFEncryptedFlag)
-
-/*
- * The values we return for ZipEntryRO use 0 as an invalid value, so we
- * want to adjust the hash table index by a fixed amount.  Using a large
- * value helps insure that people don't mix & match arguments, e.g. to
- * findEntryByIndex().
- */
-#define kZipEntryAdj        10000
+private:
+    _ZipEntryRO(const _ZipEntryRO& other);
+    _ZipEntryRO& operator=(const _ZipEntryRO& other);
+};
 
 ZipFileRO::~ZipFileRO() {
-    free(mHashTable);
-    if (mDirectoryMap)
-        mDirectoryMap->release();
-    if (mFd >= 0)
-        TEMP_FAILURE_RETRY(close(mFd));
-    if (mFileName)
-        free(mFileName);
+    CloseArchive(mHandle);
+    free(mFileName);
 }
 
 /*
- * Convert a ZipEntryRO to a hash table index, verifying that it's in a
- * valid range.
- */
-int ZipFileRO::entryToIndex(const ZipEntryRO entry) const
-{
-    long ent = ((intptr_t) entry) - kZipEntryAdj;
-    if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) {
-        ALOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent);
-        return -1;
-    }
-    return ent;
-}
-
-
-/*
  * Open the specified file read-only.  We memory-map the entire thing and
  * close the file before returning.
  */
-status_t ZipFileRO::open(const char* zipFileName)
+/* static */ ZipFileRO* ZipFileRO::open(const char* zipFileName)
 {
-    int fd = -1;
-
-    assert(mDirectoryMap == NULL);
-
-    /*
-     * Open and map the specified file.
-     */
-    fd = TEMP_FAILURE_RETRY(::open(zipFileName, O_RDONLY | O_BINARY));
-    if (fd < 0) {
-        ALOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno));
-        return NAME_NOT_FOUND;
-    }
-
-    mFileLength = lseek64(fd, 0, SEEK_END);
-    if (mFileLength < kEOCDLen) {
-        TEMP_FAILURE_RETRY(close(fd));
-        return UNKNOWN_ERROR;
-    }
-
-    if (mFileName != NULL) {
-        free(mFileName);
-    }
-    mFileName = strdup(zipFileName);
-
-    mFd = fd;
-
-    /*
-     * Find the Central Directory and store its size and number of entries.
-     */
-    if (!mapCentralDirectory()) {
-        goto bail;
-    }
-
-    /*
-     * Verify Central Directory and create data structures for fast access.
-     */
-    if (!parseZipArchive()) {
-        goto bail;
-    }
-
-    return OK;
-
-bail:
-    free(mFileName);
-    mFileName = NULL;
-    TEMP_FAILURE_RETRY(close(fd));
-    return UNKNOWN_ERROR;
-}
-
-/*
- * Parse the Zip archive, verifying its contents and initializing internal
- * data structures.
- */
-bool ZipFileRO::mapCentralDirectory(void)
-{
-    ssize_t readAmount = kMaxEOCDSearch;
-    if (readAmount > (ssize_t) mFileLength)
-        readAmount = mFileLength;
-
-    if (readAmount < kEOCDSize) {
-        ALOGW("File too short to be a zip file");
-        return false;
-    }
-
-    unsigned char* scanBuf = (unsigned char*) malloc(readAmount);
-    if (scanBuf == NULL) {
-        ALOGW("couldn't allocate scanBuf: %s", strerror(errno));
-        free(scanBuf);
-        return false;
-    }
-
-    /*
-     * Make sure this is a Zip archive.
-     */
-    if (lseek64(mFd, 0, SEEK_SET) != 0) {
-        ALOGW("seek to start failed: %s", strerror(errno));
-        free(scanBuf);
-        return false;
-    }
-
-    ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t)));
-    if (actual != (ssize_t) sizeof(int32_t)) {
-        ALOGI("couldn't read first signature from zip archive: %s", strerror(errno));
-        free(scanBuf);
-        return false;
-    }
-
-    unsigned int header = get4LE(scanBuf);
-    if (header != kLFHSignature) {
-        ALOGV("Not a Zip archive (found 0x%08x)\n", header);
-        free(scanBuf);
-        return false;
-    }
-
-    /*
-     * Perform the traditional EOCD snipe hunt.
-     *
-     * We're searching for the End of Central Directory magic number,
-     * which appears at the start of the EOCD block.  It's followed by
-     * 18 bytes of EOCD stuff and up to 64KB of archive comment.  We
-     * need to read the last part of the file into a buffer, dig through
-     * it to find the magic number, parse some values out, and use those
-     * to determine the extent of the CD.
-     *
-     * We start by pulling in the last part of the file.
-     */
-    off64_t searchStart = mFileLength - readAmount;
-
-    if (lseek64(mFd, searchStart, SEEK_SET) != searchStart) {
-        ALOGW("seek %ld failed: %s\n",  (long) searchStart, strerror(errno));
-        free(scanBuf);
-        return false;
-    }
-    actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount));
-    if (actual != (ssize_t) readAmount) {
-        ALOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n",
-            (ZD_TYPE) actual, (ZD_TYPE) readAmount, strerror(errno));
-        free(scanBuf);
-        return false;
-    }
-
-    /*
-     * Scan backward for the EOCD magic.  In an archive without a trailing
-     * comment, we'll find it on the first try.  (We may want to consider
-     * doing an initial minimal read; if we don't find it, retry with a
-     * second read as above.)
-     */
-    int i;
-    for (i = readAmount - kEOCDLen; i >= 0; i--) {
-        if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) {
-            ALOGV("+++ Found EOCD at buf+%d\n", i);
-            break;
-        }
-    }
-    if (i < 0) {
-        ALOGD("Zip: EOCD not found, %s is not zip\n", mFileName);
-        free(scanBuf);
-        return false;
-    }
-
-    off64_t eocdOffset = searchStart + i;
-    const unsigned char* eocdPtr = scanBuf + i;
-
-    assert(eocdOffset < mFileLength);
-
-    /*
-     * Grab the CD offset and size, and the number of entries in the
-     * archive. After that, we can release our EOCD hunt buffer.
-     */
-    unsigned int diskNumber = get2LE(eocdPtr + kEOCDDiskNumber);
-    unsigned int diskWithCentralDir = get2LE(eocdPtr + kEOCDDiskNumberForCD);
-    unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries);
-    unsigned int totalNumEntries = get2LE(eocdPtr + kEOCDTotalNumEntries);
-    unsigned int centralDirSize = get4LE(eocdPtr + kEOCDSize);
-    unsigned int centralDirOffset = get4LE(eocdPtr + kEOCDFileOffset);
-    unsigned int commentSize = get2LE(eocdPtr + kEOCDCommentSize);
-    free(scanBuf);
-
-    // Verify that they look reasonable.
-    if ((long long) centralDirOffset + (long long) centralDirSize > (long long) eocdOffset) {
-        ALOGW("bad offsets (dir %ld, size %u, eocd %ld)\n",
-            (long) centralDirOffset, centralDirSize, (long) eocdOffset);
-        return false;
-    }
-    if (numEntries == 0) {
-        ALOGW("empty archive?\n");
-        return false;
-    } else if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) {
-        ALOGW("spanned archives not supported");
-        return false;
-    }
-
-    // Check to see if comment is a sane size
-    if ((commentSize > (mFileLength - kEOCDLen))
-            || (eocdOffset > (mFileLength - kEOCDLen) - commentSize)) {
-        ALOGW("comment size runs off end of file");
-        return false;
-    }
-
-    ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n",
-        numEntries, centralDirSize, centralDirOffset);
-
-    mDirectoryMap = new FileMap();
-    if (mDirectoryMap == NULL) {
-        ALOGW("Unable to create directory map: %s", strerror(errno));
-        return false;
-    }
-
-    if (!mDirectoryMap->create(mFileName, mFd, centralDirOffset, centralDirSize, true)) {
-        ALOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName,
-                (ZD_TYPE) centralDirOffset, (ZD_TYPE) (centralDirOffset + centralDirSize), strerror(errno));
-        return false;
-    }
-
-    mNumEntries = numEntries;
-    mDirectoryOffset = centralDirOffset;
-
-    return true;
-}
-
-
-/*
- * Round up to the next highest power of 2.
- *
- * Found on http://graphics.stanford.edu/~seander/bithacks.html.
- */
-static unsigned int roundUpPower2(unsigned int val)
-{
-    val--;
-    val |= val >> 1;
-    val |= val >> 2;
-    val |= val >> 4;
-    val |= val >> 8;
-    val |= val >> 16;
-    val++;
-
-    return val;
-}
-
-bool ZipFileRO::parseZipArchive(void)
-{
-    bool result = false;
-    const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr();
-    size_t cdLength = mDirectoryMap->getDataLength();
-    int numEntries = mNumEntries;
-
-    /*
-     * Create hash table.  We have a minimum 75% load factor, possibly as
-     * low as 50% after we round off to a power of 2.
-     */
-    mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3);
-    mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry));
-
-    /*
-     * Walk through the central directory, adding entries to the hash
-     * table.
-     */
-    const unsigned char* ptr = cdPtr;
-    for (int i = 0; i < numEntries; i++) {
-        if (get4LE(ptr) != kCDESignature) {
-            ALOGW("Missed a central dir sig (at %d)\n", i);
-            goto bail;
-        }
-        if (ptr + kCDELen > cdPtr + cdLength) {
-            ALOGW("Ran off the end (at %d)\n", i);
-            goto bail;
-        }
-
-        long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset);
-        if (localHdrOffset >= mDirectoryOffset) {
-            ALOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i);
-            goto bail;
-        }
-
-        unsigned int gpbf = get2LE(ptr + kCDEGPBFlags);
-        if ((gpbf & kGPFUnsupportedMask) != 0) {
-            ALOGW("Invalid General Purpose Bit Flag: %d", gpbf);
-            goto bail;
-        }
-
-        unsigned int nameLen = get2LE(ptr + kCDENameLen);
-        unsigned int extraLen = get2LE(ptr + kCDEExtraLen);
-        unsigned int commentLen = get2LE(ptr + kCDECommentLen);
-
-        const char *name = (const char *) ptr + kCDELen;
-
-        /* Check name for NULL characters */
-        if (memchr(name, 0, nameLen) != NULL) {
-            ALOGW("Filename contains NUL byte");
-            goto bail;
-        }
-
-        /* add the CDE filename to the hash table */
-        unsigned int hash = computeHash(name, nameLen);
-        addToHash(name, nameLen, hash);
-
-        /* We don't care about the comment or extra data. */
-        ptr += kCDELen + nameLen + extraLen + commentLen;
-        if ((size_t)(ptr - cdPtr) > cdLength) {
-            ALOGW("bad CD advance (%d vs " ZD ") at entry %d\n",
-                (int) (ptr - cdPtr), (ZD_TYPE) cdLength, i);
-            goto bail;
-        }
-    }
-    ALOGV("+++ zip good scan %d entries\n", numEntries);
-    result = true;
-
-bail:
-    return result;
-}
-
-/*
- * Simple string hash function for non-null-terminated strings.
- */
-/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len)
-{
-    unsigned int hash = 0;
-
-    while (len--)
-        hash = hash * 31 + *str++;
-
-    return hash;
-}
-
-/*
- * Add a new entry to the hash table.
- */
-void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash)
-{
-    int ent = hash & (mHashTableSize-1);
-
-    /*
-     * We over-allocate the table, so we're guaranteed to find an empty slot.
-     */
-    while (mHashTable[ent].name != NULL)
-        ent = (ent + 1) & (mHashTableSize-1);
-
-    mHashTable[ent].name = str;
-    mHashTable[ent].nameLen = strLen;
-}
-
-/*
- * Find a matching entry.
- *
- * Returns NULL if not found.
- */
-ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const
-{
-    /*
-     * If the ZipFileRO instance is not initialized, the entry number will
-     * end up being garbage since mHashTableSize is -1.
-     */
-    if (mHashTableSize <= 0) {
+    ZipArchiveHandle handle;
+    const int32_t error = OpenArchive(zipFileName, &handle);
+    if (error) {
+        ALOGW("Error opening archive %s: %s", zipFileName, ErrorCodeString(error));
         return NULL;
     }
 
-    int nameLen = strlen(fileName);
-    unsigned int hash = computeHash(fileName, nameLen);
-    int ent = hash & (mHashTableSize-1);
-
-    while (mHashTable[ent].name != NULL) {
-        if (mHashTable[ent].nameLen == nameLen &&
-            memcmp(mHashTable[ent].name, fileName, nameLen) == 0)
-        {
-            /* match */
-            return (ZipEntryRO)(long)(ent + kZipEntryAdj);
-        }
-
-        ent = (ent + 1) & (mHashTableSize-1);
-    }
-
-    return NULL;
+    return new ZipFileRO(handle, strdup(zipFileName));
 }
 
-/*
- * Find the Nth entry.
- *
- * This currently involves walking through the sparse hash table, counting
- * non-empty entries.  If we need to speed this up we can either allocate
- * a parallel lookup table or (perhaps better) provide an iterator interface.
- */
-ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const
+
+ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const
 {
-    if (idx < 0 || idx >= mNumEntries) {
-        ALOGW("Invalid index %d\n", idx);
+    _ZipEntryRO* data = new _ZipEntryRO;
+    const int32_t error = FindEntry(mHandle, entryName, &(data->entry));
+    if (error) {
+        delete data;
         return NULL;
     }
 
-    for (int ent = 0; ent < mHashTableSize; ent++) {
-        if (mHashTable[ent].name != NULL) {
-            if (idx-- == 0)
-                return (ZipEntryRO) (intptr_t)(ent + kZipEntryAdj);
-        }
-    }
+    data->name.name = entryName;
+    data->name.name_length = strlen(entryName);
 
-    return NULL;
+    return (ZipEntryRO) data;
 }
 
 /*
@@ -508,172 +104,86 @@
 bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen,
     size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const
 {
-    bool ret = false;
+    const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
+    const ZipEntry& ze = zipEntry->entry;
 
-    const int ent = entryToIndex(entry);
-    if (ent < 0) {
-        ALOGW("cannot find entry");
-        return false;
+    if (pMethod != NULL) {
+        *pMethod = ze.method;
     }
-
-    HashEntry hashEntry = mHashTable[ent];
-
-    /*
-     * Recover the start of the central directory entry from the filename
-     * pointer.  The filename is the first entry past the fixed-size data,
-     * so we can just subtract back from that.
-     */
-    const unsigned char* ptr = (const unsigned char*) hashEntry.name;
-    off64_t cdOffset = mDirectoryOffset;
-
-    ptr -= kCDELen;
-
-    int method = get2LE(ptr + kCDEMethod);
-    if (pMethod != NULL)
-        *pMethod = method;
-
-    if (pModWhen != NULL)
-        *pModWhen = get4LE(ptr + kCDEModWhen);
-    if (pCrc32 != NULL)
-        *pCrc32 = get4LE(ptr + kCDECRC);
-
-    size_t compLen = get4LE(ptr + kCDECompLen);
-    if (pCompLen != NULL)
-        *pCompLen = compLen;
-    size_t uncompLen = get4LE(ptr + kCDEUncompLen);
-    if (pUncompLen != NULL)
-        *pUncompLen = uncompLen;
-
-    /*
-     * If requested, determine the offset of the start of the data.  All we
-     * have is the offset to the Local File Header, which is variable size,
-     * so we have to read the contents of the struct to figure out where
-     * the actual data starts.
-     *
-     * We also need to make sure that the lengths are not so large that
-     * somebody trying to map the compressed or uncompressed data runs
-     * off the end of the mapped region.
-     *
-     * Note we don't verify compLen/uncompLen if they don't request the
-     * dataOffset, because dataOffset is expensive to determine.  However,
-     * if they don't have the file offset, they're not likely to be doing
-     * anything with the contents.
-     */
+    if (pUncompLen != NULL) {
+        *pUncompLen = ze.uncompressed_length;
+    }
+    if (pCompLen != NULL) {
+        *pCompLen = ze.compressed_length;
+    }
     if (pOffset != NULL) {
-        long localHdrOffset = get4LE(ptr + kCDELocalOffset);
-        if (localHdrOffset + kLFHLen >= cdOffset) {
-            ALOGE("ERROR: bad local hdr offset in zip\n");
-            return false;
-        }
-
-        unsigned char lfhBuf[kLFHLen];
-
-#ifdef HAVE_PREAD
-        /*
-         * This file descriptor might be from zygote's preloaded assets,
-         * so we need to do an pread64() instead of a lseek64() + read() to
-         * guarantee atomicity across the processes with the shared file
-         * descriptors.
-         */
-        ssize_t actual =
-                TEMP_FAILURE_RETRY(pread64(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset));
-
-        if (actual != sizeof(lfhBuf)) {
-            ALOGW("failed reading lfh from offset %ld\n", localHdrOffset);
-            return false;
-        }
-
-        if (get4LE(lfhBuf) != kLFHSignature) {
-            ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; "
-                    "got: data=0x%08lx\n",
-                    localHdrOffset, kLFHSignature, get4LE(lfhBuf));
-            return false;
-        }
-#else /* HAVE_PREAD */
-        /*
-         * For hosts don't have pread64() we cannot guarantee atomic reads from
-         * an offset in a file. Android should never run on those platforms.
-         * File descriptors inherited from a fork() share file offsets and
-         * there would be nothing to protect from two different processes
-         * calling lseek64() concurrently.
-         */
-
-        {
-            AutoMutex _l(mFdLock);
-
-            if (lseek64(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
-                ALOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
-                return false;
-            }
-
-            ssize_t actual =
-                    TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
-            if (actual != sizeof(lfhBuf)) {
-                ALOGW("failed reading lfh from offset %ld\n", localHdrOffset);
-                return false;
-            }
-
-            if (get4LE(lfhBuf) != kLFHSignature) {
-                off64_t actualOffset = lseek64(mFd, 0, SEEK_CUR);
-                ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; "
-                        "got: offset=" ZD " data=0x%08lx\n",
-                        localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf));
-                return false;
-            }
-        }
-#endif /* HAVE_PREAD */
-
-        unsigned int gpbf = get2LE(lfhBuf + kLFHGPBFlags);
-        if ((gpbf & kGPFUnsupportedMask) != 0) {
-            ALOGW("Invalid General Purpose Bit Flag: %d", gpbf);
-            return false;
-        }
-
-        off64_t dataOffset = localHdrOffset + kLFHLen
-            + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen);
-        if (dataOffset >= cdOffset) {
-            ALOGW("bad data offset %ld in zip\n", (long) dataOffset);
-            return false;
-        }
-
-        /* check lengths */
-        if ((dataOffset >= cdOffset) || (compLen > (cdOffset - dataOffset))) {
-            ALOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n",
-                (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset);
-            return false;
-        }
-
-        if (method == kCompressStored &&
-            ((dataOffset >= cdOffset) ||
-             (uncompLen > (cdOffset - dataOffset))))
-        {
-            ALOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n",
-                (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset);
-            return false;
-        }
-
-        *pOffset = dataOffset;
+        *pOffset = ze.offset;
+    }
+    if (pModWhen != NULL) {
+        *pModWhen = ze.mod_time;
+    }
+    if (pCrc32 != NULL) {
+        *pCrc32 = ze.crc32;
     }
 
     return true;
 }
 
+bool ZipFileRO::startIteration(void** cookie)
+{
+    _ZipEntryRO* ze = new _ZipEntryRO;
+    int32_t error = StartIteration(mHandle, &(ze->cookie), NULL /* prefix */);
+    if (error) {
+        ALOGW("Could not start iteration over %s: %s", mFileName, ErrorCodeString(error));
+        delete ze;
+        return false;
+    }
+
+    *cookie = ze;
+    return true;
+}
+
+ZipEntryRO ZipFileRO::nextEntry(void* cookie)
+{
+    _ZipEntryRO* ze = reinterpret_cast<_ZipEntryRO*>(cookie);
+    int32_t error = Next(ze->cookie, &(ze->entry), &(ze->name));
+    if (error) {
+        if (error != -1) {
+            ALOGW("Error iteration over %s: %s", mFileName, ErrorCodeString(error));
+        }
+        return NULL;
+    }
+
+    return &(ze->entry);
+}
+
+void ZipFileRO::endIteration(void* cookie)
+{
+    delete reinterpret_cast<_ZipEntryRO*>(cookie);
+}
+
+void ZipFileRO::releaseEntry(ZipEntryRO entry) const
+{
+    delete reinterpret_cast<_ZipEntryRO*>(entry);
+}
+
 /*
  * Copy the entry's filename to the buffer.
  */
 int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen)
     const
 {
-    int ent = entryToIndex(entry);
-    if (ent < 0)
-        return -1;
+    const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
+    const uint16_t requiredSize = zipEntry->name.name_length + 1;
 
-    int nameLen = mHashTable[ent].nameLen;
-    if (bufLen < nameLen+1)
-        return nameLen+1;
+    if (bufLen < requiredSize) {
+        ALOGW("Buffer too short, requires %d bytes for entry name", requiredSize);
+        return requiredSize;
+    }
 
-    memcpy(buffer, mHashTable[ent].name, nameLen);
-    buffer[nameLen] = '\0';
+    memcpy(buffer, zipEntry->name.name, requiredSize - 1);
+    buffer[requiredSize - 1] = '\0';
+
     return 0;
 }
 
@@ -682,32 +192,19 @@
  */
 FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const
 {
-    /*
-     * TODO: the efficient way to do this is to modify FileMap to allow
-     * sub-regions of a file to be mapped.  A reference-counting scheme
-     * can manage the base memory mapping.  For now, we just create a brand
-     * new mapping off of the Zip archive file descriptor.
-     */
+    const _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
+    const ZipEntry& ze = zipEntry->entry;
+    int fd = GetFileDescriptor(mHandle);
+    size_t actualLen = 0;
 
-    FileMap* newMap;
-    int method;
-    size_t uncompLen;
-    size_t compLen;
-    off64_t offset;
-
-    if (!getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL)) {
-        return NULL;
-    }
-
-    size_t actualLen;
-    if (method == kCompressStored) {
-        actualLen = uncompLen;
+    if (ze.method == kCompressStored) {
+        actualLen = ze.uncompressed_length;
     } else {
-        actualLen = compLen;
+        actualLen = ze.compressed_length;
     }
 
-    newMap = new FileMap();
-    if (!newMap->create(mFileName, mFd, offset, actualLen, true)) {
+    FileMap* newMap = new FileMap();
+    if (!newMap->create(mFileName, fd, ze.offset, actualLen, true)) {
         newMap->release();
         return NULL;
     }
@@ -721,64 +218,17 @@
  * This doesn't verify the data's CRC, which might be useful for
  * uncompressed data.  The caller should be able to manage it.
  */
-bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const
+bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer, size_t size) const
 {
-    const size_t kSequentialMin = 32768;
-    bool result = false;
-    int ent = entryToIndex(entry);
-    if (ent < 0) {
+    _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
+    const int32_t error = ExtractToMemory(mHandle, &(zipEntry->entry),
+        (uint8_t*) buffer, size);
+    if (error) {
+        ALOGW("ExtractToMemory failed with %s", ErrorCodeString(error));
         return false;
     }
 
-    int method;
-    size_t uncompLen, compLen;
-    off64_t offset;
-    const unsigned char* ptr;
-    FileMap *file;
-
-    if (!getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL)) {
-        goto bail;
-    }
-
-    file = createEntryFileMap(entry);
-    if (file == NULL) {
-        goto bail;
-    }
-
-    ptr = (const unsigned char*) file->getDataPtr();
-
-    /*
-     * Experiment with madvise hint.  When we want to uncompress a file,
-     * we pull some stuff out of the central dir entry and then hit a
-     * bunch of compressed or uncompressed data sequentially.  The CDE
-     * visit will cause a limited amount of read-ahead because it's at
-     * the end of the file.  We could end up doing lots of extra disk
-     * access if the file we're prying open is small.  Bottom line is we
-     * probably don't want to turn MADV_SEQUENTIAL on and leave it on.
-     *
-     * So, if the compressed size of the file is above a certain minimum
-     * size, temporarily boost the read-ahead in the hope that the extra
-     * pair of system calls are negated by a reduction in page faults.
-     */
-    if (compLen > kSequentialMin)
-        file->advise(FileMap::SEQUENTIAL);
-
-    if (method == kCompressStored) {
-        memcpy(buffer, ptr, uncompLen);
-    } else {
-        if (!inflateBuffer(buffer, ptr, uncompLen, compLen))
-            goto unmap;
-    }
-
-    if (compLen > kSequentialMin)
-        file->advise(FileMap::NORMAL);
-
-    result = true;
-
-unmap:
-    file->release();
-bail:
-    return result;
+    return true;
 }
 
 /*
@@ -788,208 +238,12 @@
  */
 bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const
 {
-    bool result = false;
-    int ent = entryToIndex(entry);
-    if (ent < 0) {
+    _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry);
+    const int32_t error = ExtractEntryToFile(mHandle, &(zipEntry->entry), fd);
+    if (error) {
+        ALOGW("ExtractToMemory failed with %s", ErrorCodeString(error));
         return false;
     }
 
-    int method;
-    size_t uncompLen, compLen;
-    off64_t offset;
-    const unsigned char* ptr;
-    FileMap *file;
-
-    if (!getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL)) {
-        goto bail;
-    }
-
-    file = createEntryFileMap(entry);
-    if (file == NULL) {
-        goto bail;
-    }
-
-    ptr = (const unsigned char*) file->getDataPtr();
-
-    if (method == kCompressStored) {
-        ssize_t actual = TEMP_FAILURE_RETRY(write(fd, ptr, uncompLen));
-        if (actual < 0) {
-            ALOGE("Write failed: %s\n", strerror(errno));
-            goto unmap;
-        } else if ((size_t) actual != uncompLen) {
-            ALOGE("Partial write during uncompress (" ZD " of " ZD ")\n",
-                (ZD_TYPE) actual, (ZD_TYPE) uncompLen);
-            goto unmap;
-        } else {
-            ALOGI("+++ successful write\n");
-        }
-    } else {
-        if (!inflateBuffer(fd, ptr, uncompLen, compLen)) {
-            goto unmap;
-        }
-    }
-
-    result = true;
-
-unmap:
-    file->release();
-bail:
-    return result;
-}
-
-/*
- * Uncompress "deflate" data from one buffer to another.
- */
-/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf,
-    size_t uncompLen, size_t compLen)
-{
-    bool result = false;
-    z_stream zstream;
-    int zerr;
-
-    /*
-     * Initialize the zlib stream struct.
-     */
-    memset(&zstream, 0, sizeof(zstream));
-    zstream.zalloc = Z_NULL;
-    zstream.zfree = Z_NULL;
-    zstream.opaque = Z_NULL;
-    zstream.next_in = (Bytef*)inBuf;
-    zstream.avail_in = compLen;
-    zstream.next_out = (Bytef*) outBuf;
-    zstream.avail_out = uncompLen;
-    zstream.data_type = Z_UNKNOWN;
-
-    /*
-     * Use the undocumented "negative window bits" feature to tell zlib
-     * that there's no zlib header waiting for it.
-     */
-    zerr = inflateInit2(&zstream, -MAX_WBITS);
-    if (zerr != Z_OK) {
-        if (zerr == Z_VERSION_ERROR) {
-            ALOGE("Installed zlib is not compatible with linked version (%s)\n",
-                ZLIB_VERSION);
-        } else {
-            ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
-        }
-        goto bail;
-    }
-
-    /*
-     * Expand data.
-     */
-    zerr = inflate(&zstream, Z_FINISH);
-    if (zerr != Z_STREAM_END) {
-        ALOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n",
-            zerr, zstream.next_in, zstream.avail_in,
-            zstream.next_out, zstream.avail_out);
-        goto z_bail;
-    }
-
-    /* paranoia */
-    if (zstream.total_out != uncompLen) {
-        ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n",
-            zstream.total_out, (ZD_TYPE) uncompLen);
-        goto z_bail;
-    }
-
-    result = true;
-
-z_bail:
-    inflateEnd(&zstream);        /* free up any allocated structures */
-
-bail:
-    return result;
-}
-
-/*
- * Uncompress "deflate" data from one buffer to an open file descriptor.
- */
-/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf,
-    size_t uncompLen, size_t compLen)
-{
-    bool result = false;
-    const size_t kWriteBufSize = 32768;
-    unsigned char writeBuf[kWriteBufSize];
-    z_stream zstream;
-    int zerr;
-
-    /*
-     * Initialize the zlib stream struct.
-     */
-    memset(&zstream, 0, sizeof(zstream));
-    zstream.zalloc = Z_NULL;
-    zstream.zfree = Z_NULL;
-    zstream.opaque = Z_NULL;
-    zstream.next_in = (Bytef*)inBuf;
-    zstream.avail_in = compLen;
-    zstream.next_out = (Bytef*) writeBuf;
-    zstream.avail_out = sizeof(writeBuf);
-    zstream.data_type = Z_UNKNOWN;
-
-    /*
-     * Use the undocumented "negative window bits" feature to tell zlib
-     * that there's no zlib header waiting for it.
-     */
-    zerr = inflateInit2(&zstream, -MAX_WBITS);
-    if (zerr != Z_OK) {
-        if (zerr == Z_VERSION_ERROR) {
-            ALOGE("Installed zlib is not compatible with linked version (%s)\n",
-                ZLIB_VERSION);
-        } else {
-            ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
-        }
-        goto bail;
-    }
-
-    /*
-     * Loop while we have more to do.
-     */
-    do {
-        /*
-         * Expand data.
-         */
-        zerr = inflate(&zstream, Z_NO_FLUSH);
-        if (zerr != Z_OK && zerr != Z_STREAM_END) {
-            ALOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n",
-                zerr, zstream.next_in, zstream.avail_in,
-                zstream.next_out, zstream.avail_out);
-            goto z_bail;
-        }
-
-        /* write when we're full or when we're done */
-        if (zstream.avail_out == 0 ||
-            (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf)))
-        {
-            long writeSize = zstream.next_out - writeBuf;
-            int cc = TEMP_FAILURE_RETRY(write(fd, writeBuf, writeSize));
-            if (cc < 0) {
-                ALOGW("write failed in inflate: %s", strerror(errno));
-                goto z_bail;
-            } else if (cc != (int) writeSize) {
-                ALOGW("write failed in inflate (%d vs %ld)", cc, writeSize);
-                goto z_bail;
-            }
-
-            zstream.next_out = writeBuf;
-            zstream.avail_out = sizeof(writeBuf);
-        }
-    } while (zerr == Z_OK);
-
-    assert(zerr == Z_STREAM_END);       /* other errors should've been caught */
-
-    /* paranoia */
-    if (zstream.total_out != uncompLen) {
-        ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n",
-            zstream.total_out, (ZD_TYPE) uncompLen);
-        goto z_bail;
-    }
-
-    result = true;
-
-z_bail:
-    inflateEnd(&zstream);        /* free up any allocated structures */
-
-bail:
-    return result;
+    return true;
 }
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
index 997eb7d..e9ac2fe 100644
--- a/libs/androidfw/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -33,115 +33,13 @@
 
 using namespace android;
 
-/*
- * Utility function that expands zip/gzip "deflate" compressed data
- * into a buffer.
- *
- * "fd" is an open file positioned at the start of the "deflate" data
- * "buf" must hold at least "uncompressedLen" bytes.
- */
-/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf,
-    long uncompressedLen, long compressedLen)
-{
-    bool result = false;
-	const unsigned long kReadBufSize = 32768;
-	unsigned char* readBuf = NULL;
-    z_stream zstream;
-    int zerr;
-    unsigned long compRemaining;
-
-    assert(uncompressedLen >= 0);
-    assert(compressedLen >= 0);
-
-	readBuf = new unsigned char[kReadBufSize];
-	if (readBuf == NULL)
-        goto bail;
-    compRemaining = compressedLen;
-
-    /*
-     * Initialize the zlib stream.
-     */
-	memset(&zstream, 0, sizeof(zstream));
-    zstream.zalloc = Z_NULL;
-    zstream.zfree = Z_NULL;
-    zstream.opaque = Z_NULL;
-    zstream.next_in = NULL;
-    zstream.avail_in = 0;
-    zstream.next_out = (Bytef*) buf;
-    zstream.avail_out = uncompressedLen;
-    zstream.data_type = Z_UNKNOWN;
-
-	/*
-	 * Use the undocumented "negative window bits" feature to tell zlib
-	 * that there's no zlib header waiting for it.
-	 */
-    zerr = inflateInit2(&zstream, -MAX_WBITS);
-    if (zerr != Z_OK) {
-        if (zerr == Z_VERSION_ERROR) {
-            ALOGE("Installed zlib is not compatible with linked version (%s)\n",
-                ZLIB_VERSION);
-        } else {
-            ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
-        }
-        goto bail;
-    }
-
-    /*
-     * Loop while we have data.
-     */
-    do {
-        unsigned long getSize;
-
-        /* read as much as we can */
-        if (zstream.avail_in == 0) {
-            getSize = (compRemaining > kReadBufSize) ?
-                        kReadBufSize : compRemaining;
-            ALOGV("+++ reading %ld bytes (%ld left)\n",
-                getSize, compRemaining);
-
-            int cc = TEMP_FAILURE_RETRY(read(fd, readBuf, getSize));
-            if (cc < 0) {
-                ALOGW("inflate read failed: %s", strerror(errno));
-            } else if (cc != (int) getSize) {
-                ALOGW("inflate read failed (%d vs %ld)", cc, getSize);
-                goto z_bail;
-            }
-
-            compRemaining -= getSize;
-
-            zstream.next_in = readBuf;
-            zstream.avail_in = getSize;
-        }
-
-        /* uncompress the data */
-        zerr = inflate(&zstream, Z_NO_FLUSH);
-        if (zerr != Z_OK && zerr != Z_STREAM_END) {
-            ALOGD("zlib inflate call failed (zerr=%d)\n", zerr);
-            goto z_bail;
-        }
-
-		/* output buffer holds all, so no need to write the output */
-    } while (zerr == Z_OK);
-
-    assert(zerr == Z_STREAM_END);       /* other errors should've been caught */
-
-    if ((long) zstream.total_out != uncompressedLen) {
-        ALOGW("Size mismatch on inflated file (%ld vs %ld)\n",
-            zstream.total_out, uncompressedLen);
-        goto z_bail;
-    }
-
-    // success!
-    result = true;
-
-z_bail:
-    inflateEnd(&zstream);        /* free up any allocated structures */
-
-bail:
-	delete[] readBuf;
-    return result;
+static inline unsigned long get4LE(const unsigned char* buf) {
+    return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
 }
 
+
+static const unsigned long kReadBufSize = 32768;
+
 /*
  * Utility function that expands zip/gzip "deflate" compressed data
  * into a buffer.
@@ -153,12 +51,11 @@
  * "fp" is an open file positioned at the start of the "deflate" data
  * "buf" must hold at least "uncompressedLen" bytes.
  */
-/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf,
+/*static*/ template<typename T> bool inflateToBuffer(T& reader, void* buf,
     long uncompressedLen, long compressedLen)
 {
     bool result = false;
-	const unsigned long kReadBufSize = 32768;
-	unsigned char* readBuf = NULL;
+
     z_stream zstream;
     int zerr;
     unsigned long compRemaining;
@@ -166,15 +63,12 @@
     assert(uncompressedLen >= 0);
     assert(compressedLen >= 0);
 
-	readBuf = new unsigned char[kReadBufSize];
-	if (readBuf == NULL)
-        goto bail;
     compRemaining = compressedLen;
 
     /*
      * Initialize the zlib stream.
      */
-	memset(&zstream, 0, sizeof(zstream));
+    memset(&zstream, 0, sizeof(zstream));
     zstream.zalloc = Z_NULL;
     zstream.zfree = Z_NULL;
     zstream.opaque = Z_NULL;
@@ -184,10 +78,10 @@
     zstream.avail_out = uncompressedLen;
     zstream.data_type = Z_UNKNOWN;
 
-	/*
-	 * Use the undocumented "negative window bits" feature to tell zlib
-	 * that there's no zlib header waiting for it.
-	 */
+    /*
+     * Use the undocumented "negative window bits" feature to tell zlib
+     * that there's no zlib header waiting for it.
+     */
     zerr = inflateInit2(&zstream, -MAX_WBITS);
     if (zerr != Z_OK) {
         if (zerr == Z_VERSION_ERROR) {
@@ -212,17 +106,18 @@
             ALOGV("+++ reading %ld bytes (%ld left)\n",
                 getSize, compRemaining);
 
-            int cc = fread(readBuf, 1, getSize, fp);
-            if (cc != (int) getSize) {
-                ALOGD("inflate read failed (%d vs %ld)\n",
-                    cc, getSize);
+            unsigned char* nextBuffer = NULL;
+            const unsigned long nextSize = reader.read(&nextBuffer, getSize);
+
+            if (nextSize < getSize || nextBuffer == NULL) {
+                ALOGD("inflate read failed (%ld vs %ld)\n", nextSize, getSize);
                 goto z_bail;
             }
 
-            compRemaining -= getSize;
+            compRemaining -= nextSize;
 
-            zstream.next_in = readBuf;
-            zstream.avail_in = getSize;
+            zstream.next_in = nextBuffer;
+            zstream.avail_in = nextSize;
         }
 
         /* uncompress the data */
@@ -250,10 +145,100 @@
     inflateEnd(&zstream);        /* free up any allocated structures */
 
 bail:
-	delete[] readBuf;
     return result;
 }
 
+class FileReader {
+public:
+   FileReader(FILE* fp) :
+       mFp(fp), mReadBuf(new unsigned char[kReadBufSize])
+   {
+   }
+
+   ~FileReader() {
+       delete[] mReadBuf;
+   }
+
+   long read(unsigned char** nextBuffer, long readSize) const {
+       *nextBuffer = mReadBuf;
+       return fread(mReadBuf, 1, readSize, mFp);
+   }
+
+   FILE* mFp;
+   unsigned char* mReadBuf;
+};
+
+class FdReader {
+public:
+   FdReader(int fd) :
+       mFd(fd), mReadBuf(new unsigned char[kReadBufSize])
+   {
+   }
+
+   ~FdReader() {
+       delete[] mReadBuf;
+   }
+
+   long read(unsigned char** nextBuffer, long readSize) const {
+       *nextBuffer = mReadBuf;
+       return TEMP_FAILURE_RETRY(::read(mFd, mReadBuf, readSize));
+   }
+
+   int mFd;
+   unsigned char* mReadBuf;
+};
+
+class BufferReader {
+public:
+    BufferReader(void* input, size_t inputSize) :
+        mInput(reinterpret_cast<unsigned char*>(input)),
+        mInputSize(inputSize),
+        mBufferReturned(false)
+    {
+    }
+
+    long read(unsigned char** nextBuffer, long readSize) {
+        if (!mBufferReturned) {
+            mBufferReturned = true;
+            *nextBuffer = mInput;
+            return mInputSize;
+        }
+
+        *nextBuffer = NULL;
+        return 0;
+    }
+
+    unsigned char* mInput;
+    const size_t mInputSize;
+    bool mBufferReturned;
+};
+
+/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf,
+    long uncompressedLen, long compressedLen)
+{
+    FileReader reader(fp);
+    return ::inflateToBuffer<FileReader>(reader, buf,
+        uncompressedLen, compressedLen);
+}
+
+/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf,
+    long uncompressedLen, long compressedLen)
+{
+    FdReader reader(fd);
+    return ::inflateToBuffer<FdReader>(reader, buf,
+        uncompressedLen, compressedLen);
+}
+
+/*static*/ bool ZipUtils::inflateToBuffer(void* in, void* buf,
+    long uncompressedLen, long compressedLen)
+{
+    BufferReader reader(in, compressedLen);
+    return ::inflateToBuffer<BufferReader>(reader, buf,
+        uncompressedLen, compressedLen);
+}
+
+
+
 /*
  * Look at the contents of a gzip archive.  We want to know where the
  * data starts, and how long it will be after it is uncompressed.
@@ -338,8 +323,8 @@
     fseek(fp, curPosn, SEEK_SET);
 
     *pCompressionMethod = method;
-    *pCRC32 = ZipFileRO::get4LE(&buf[0]);
-    *pUncompressedLen = ZipFileRO::get4LE(&buf[4]);
+    *pCRC32 = get4LE(&buf[0]);
+    *pUncompressedLen = get4LE(&buf[4]);
 
     return true;
 }
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index c8e3f2b..6e6522c 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -6,7 +6,7 @@
 test_src_files := \
     BackupData_test.cpp \
     ObbFile_test.cpp \
-    ZipFileRO_test.cpp
+    ZipUtils_test.cpp
 
 shared_libraries := \
     libandroidfw \
diff --git a/libs/androidfw/tests/ZipFileRO_test.cpp b/libs/androidfw/tests/ZipUtils_test.cpp
similarity index 87%
rename from libs/androidfw/tests/ZipFileRO_test.cpp
rename to libs/androidfw/tests/ZipUtils_test.cpp
index cb9c721..c6038b5 100644
--- a/libs/androidfw/tests/ZipFileRO_test.cpp
+++ b/libs/androidfw/tests/ZipUtils_test.cpp
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "ZipFileRO_test"
+#define LOG_TAG "ZipUtils_test"
 #include <utils/Log.h>
-#include <androidfw/ZipFileRO.h>
+#include <androidfw/ZipUtils.h>
 
 #include <gtest/gtest.h>
 
@@ -25,7 +25,7 @@
 
 namespace android {
 
-class ZipFileROTest : public testing::Test {
+class ZipUtilsTest : public testing::Test {
 protected:
     virtual void SetUp() {
     }
@@ -34,13 +34,13 @@
     }
 };
 
-TEST_F(ZipFileROTest, ZipTimeConvertSuccess) {
+TEST_F(ZipUtilsTest, ZipTimeConvertSuccess) {
     struct tm t;
 
     // 2011-06-29 14:40:40
     long when = 0x3EDD7514;
 
-    ZipFileRO::zipTimeToTimespec(when, &t);
+    ZipUtils::zipTimeToTimespec(when, &t);
 
     EXPECT_EQ(2011, t.tm_year + 1900)
             << "Year was improperly converted.";