am 9f09408e: am 336bf2fb: am d21752df: Merge "Reimplement ZipFileRO in terms of libziparchive."

* commit '9f09408e3939c5afdb3f9dafc41afd249303ed7f':
  Reimplement ZipFileRO in terms of libziparchive.
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 0f610e9..7b4e6ee 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -63,14 +63,19 @@
 
 namespace android {
 
+static const int ANIM_ENTRY_NAME_MAX = 256;
+
 // ---------------------------------------------------------------------------
 
-BootAnimation::BootAnimation() : Thread(false)
+BootAnimation::BootAnimation() : Thread(false), mZip(NULL)
 {
     mSession = new SurfaceComposerClient();
 }
 
 BootAnimation::~BootAnimation() {
+    if (mZip != NULL) {
+        delete mZip;
+    }
 }
 
 void BootAnimation::onFirstRef() {
@@ -86,7 +91,7 @@
 }
 
 
-void BootAnimation::binderDied(const wp<IBinder>& who)
+void BootAnimation::binderDied(const wp<IBinder>&)
 {
     // woah, surfaceflinger died!
     ALOGD("SurfaceFlinger died, exiting...");
@@ -268,8 +273,6 @@
     mFlingerSurfaceControl = control;
     mFlingerSurface = s;
 
-    mAndroidAnimation = true;
-
     // If the device has encryption turned on or is in process
     // of being encrypted we show the encrypted boot animation.
     char decrypt[PROPERTY_VALUE_MAX];
@@ -277,16 +280,17 @@
 
     bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);
 
+    ZipFileRO* zipFile = NULL;
     if ((encryptedAnimation &&
             (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
-            (mZip.open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE) == NO_ERROR)) ||
+            ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||
 
             ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) &&
-            (mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR)) ||
+            ((zipFile = ZipFileRO::open(USER_BOOTANIMATION_FILE)) != NULL)) ||
 
             ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
-            (mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR))) {
-        mAndroidAnimation = false;
+            ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {
+        mZip = zipFile;
     }
 
     return NO_ERROR;
@@ -295,7 +299,9 @@
 bool BootAnimation::threadLoop()
 {
     bool r;
-    if (mAndroidAnimation) {
+    // We have no bootanimation file, so we use the stock android logo
+    // animation.
+    if (mZip == NULL) {
         r = android();
     } else {
         r = movie();
@@ -392,11 +398,14 @@
 
 bool BootAnimation::movie()
 {
-    ZipFileRO& zip(mZip);
+    ZipEntryRO desc = mZip->findEntryByName("desc.txt");
+    ALOGE_IF(!desc, "couldn't find desc.txt");
+    if (!desc) {
+        return false;
+    }
 
-    size_t numEntries = zip.getNumEntries();
-    ZipEntryRO desc = zip.findEntryByName("desc.txt");
-    FileMap* descMap = zip.createEntryFileMap(desc);
+    FileMap* descMap = mZip->createEntryFileMap(desc);
+    mZip->releaseEntry(desc);
     ALOGE_IF(!descMap, "descMap is null");
     if (!descMap) {
         return false;
@@ -415,7 +424,7 @@
         String8 line(s, endl - s);
         const char* l = line.string();
         int fps, width, height, count, pause;
-        char path[256];
+        char path[ANIM_ENTRY_NAME_MAX];
         char pathType;
         if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
             //LOGD("> w=%d, h=%d, fps=%d", width, height, fps);
@@ -438,28 +447,37 @@
 
     // read all the data structures
     const size_t pcount = animation.parts.size();
-    for (size_t i=0 ; i<numEntries ; i++) {
-        char name[256];
-        ZipEntryRO entry = zip.findEntryByIndex(i);
-        if (zip.getEntryFileName(entry, name, 256) == 0) {
-            const String8 entryName(name);
-            const String8 path(entryName.getPathDir());
-            const String8 leaf(entryName.getPathLeaf());
-            if (leaf.size() > 0) {
-                for (int j=0 ; j<pcount ; j++) {
-                    if (path == animation.parts[j].path) {
-                        int method;
-                        // supports only stored png files
-                        if (zip.getEntryInfo(entry, &method, 0, 0, 0, 0, 0)) {
-                            if (method == ZipFileRO::kCompressStored) {
-                                FileMap* map = zip.createEntryFileMap(entry);
-                                if (map) {
-                                    Animation::Frame frame;
-                                    frame.name = leaf;
-                                    frame.map = map;
-                                    Animation::Part& part(animation.parts.editItemAt(j));
-                                    part.frames.add(frame);
-                                }
+    void *cookie = NULL;
+    if (!mZip->startIteration(&cookie)) {
+        return false;
+    }
+
+    ZipEntryRO entry;
+    char name[ANIM_ENTRY_NAME_MAX];
+    while ((entry = mZip->nextEntry(cookie)) != NULL) {
+        const int foundEntryName = mZip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
+        if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
+            ALOGE("Error fetching entry file name");
+            continue;
+        }
+
+        const String8 entryName(name);
+        const String8 path(entryName.getPathDir());
+        const String8 leaf(entryName.getPathLeaf());
+        if (leaf.size() > 0) {
+            for (size_t j=0 ; j<pcount ; j++) {
+                if (path == animation.parts[j].path) {
+                    int method;
+                    // supports only stored png files
+                    if (mZip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) {
+                        if (method == ZipFileRO::kCompressStored) {
+                            FileMap* map = mZip->createEntryFileMap(entry);
+                            if (map) {
+                                Animation::Frame frame;
+                                frame.name = leaf;
+                                frame.map = map;
+                                Animation::Part& part(animation.parts.editItemAt(j));
+                                part.frames.add(frame);
                             }
                         }
                     }
@@ -468,6 +486,8 @@
         }
     }
 
+    mZip->endIteration(cookie);
+
     // clear screen
     glShadeModel(GL_FLAT);
     glDisable(GL_DITHER);
@@ -494,7 +514,7 @@
     Region clearReg(Rect(mWidth, mHeight));
     clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height));
 
-    for (int i=0 ; i<pcount ; i++) {
+    for (size_t i=0 ; i<pcount ; i++) {
         const Animation::Part& part(animation.parts[i]);
         const size_t fcount = part.frames.size();
         glBindTexture(GL_TEXTURE_2D, 0);
@@ -504,7 +524,7 @@
             if(exitPending() && !part.playUntilComplete)
                 break;
 
-            for (int j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
+            for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
                 const Animation::Frame& frame(part.frames[j]);
                 nsecs_t lastFrame = systemTime();
 
@@ -564,7 +584,7 @@
 
         // free the textures for this part
         if (part.count != 1) {
-            for (int j=0 ; j<fcount ; j++) {
+            for (size_t j=0 ; j<fcount ; j++) {
                 const Animation::Frame& frame(part.frames[j]);
                 glDeleteTextures(1, &frame.tid);
             }
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index fa908eb..22963c2 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -95,8 +95,7 @@
     EGLDisplay  mSurface;
     sp<SurfaceControl> mFlingerSurfaceControl;
     sp<Surface> mFlingerSurface;
-    bool        mAndroidAnimation;
-    ZipFileRO   mZip;
+    ZipFileRO   *mZip;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index bf5accd..00da0f7 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -21,7 +21,9 @@
 
 #include <utils/Log.h>
 #include <androidfw/ZipFileRO.h>
+#include <androidfw/ZipUtils.h>
 #include <ScopedUtfChars.h>
+#include <UniquePtr.h>
 
 #include <zlib.h>
 
@@ -143,7 +145,7 @@
 }
 
 static install_status_t
-sumFiles(JNIEnv* env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
+sumFiles(JNIEnv*, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char*)
 {
     size_t* total = (size_t*) arg;
     size_t uncompLen;
@@ -178,7 +180,7 @@
         return INSTALL_FAILED_INVALID_APK;
     } else {
         struct tm t;
-        ZipFileRO::zipTimeToTimespec(when, &t);
+        ZipUtils::zipTimeToTimespec(when, &t);
         modTime = mktime(&t);
     }
 
@@ -273,26 +275,25 @@
     ScopedUtfChars cpuAbi(env, javaCpuAbi);
     ScopedUtfChars cpuAbi2(env, javaCpuAbi2);
 
-    ZipFileRO zipFile;
-
-    if (zipFile.open(filePath.c_str()) != NO_ERROR) {
+    UniquePtr<ZipFileRO> zipFile(ZipFileRO::open(filePath.c_str()));
+    if (zipFile.get() == NULL) {
         ALOGI("Couldn't open APK %s\n", filePath.c_str());
         return INSTALL_FAILED_INVALID_APK;
     }
 
-    const int N = zipFile.getNumEntries();
-
     char fileName[PATH_MAX];
     bool hasPrimaryAbi = false;
 
-    for (int i = 0; i < N; i++) {
-        const ZipEntryRO entry = zipFile.findEntryByIndex(i);
-        if (entry == NULL) {
-            continue;
-        }
+    void* cookie = NULL;
+    if (!zipFile->startIteration(&cookie)) {
+        ALOGI("Couldn't iterate over APK%s\n", filePath.c_str());
+        return INSTALL_FAILED_INVALID_APK;
+    }
 
+    ZipEntryRO entry = NULL;
+    while ((entry = zipFile->nextEntry(cookie)) != NULL) {
         // Make sure this entry has a filename.
-        if (zipFile.getEntryFileName(entry, fileName, sizeof(fileName))) {
+        if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) {
             continue;
         }
 
@@ -346,15 +347,18 @@
                     && isFilenameSafe(lastSlash + 1))
                 || !strncmp(lastSlash + 1, GDBSERVER, GDBSERVER_LEN)) {
 
-            install_status_t ret = callFunc(env, callArg, &zipFile, entry, lastSlash + 1);
+            install_status_t ret = callFunc(env, callArg, zipFile.get(), entry, lastSlash + 1);
 
             if (ret != INSTALL_SUCCEEDED) {
                 ALOGV("Failure for entry %s", lastSlash + 1);
+                zipFile->endIteration(cookie);
                 return ret;
             }
         }
     }
 
+    zipFile->endIteration(cookie);
+
     return INSTALL_SUCCEEDED;
 }
 
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 1066715..503c030 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 0522212..3c55375 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -5,7 +5,7 @@
 # Build the unit tests.
 test_src_files := \
     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.";
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 452c60a..2949f8e 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -50,7 +50,8 @@
 	libcutils \
 	libexpat \
 	libpng \
-	liblog
+	liblog \
+	libziparchive-host
 
 ifeq ($(HOST_OS),linux)
 LOCAL_LDLIBS += -lrt -ldl -lpthread