Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit afd31e08 authored by Narayan Kamath's avatar Narayan Kamath
Browse files

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

Change-Id: I3c7188496837a47246c4f342e45485a70fef3169
parent 6e2d0c1d
Loading
Loading
Loading
Loading
+59 −39
Original line number Diff line number Diff line
@@ -63,14 +63,19 @@ extern "C" int clock_nanosleep(clockid_t clock_id, int flags,

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 @@ sp<SurfaceComposerClient> BootAnimation::session() const {
}


void BootAnimation::binderDied(const wp<IBinder>& who)
void BootAnimation::binderDied(const wp<IBinder>&)
{
    // woah, surfaceflinger died!
    ALOGD("SurfaceFlinger died, exiting...");
@@ -268,8 +273,6 @@ status_t BootAnimation::readyToRun() {
    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 @@ status_t BootAnimation::readyToRun() {

    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 @@ status_t BootAnimation::readyToRun() {
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 @@ void BootAnimation::checkExit() {

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 @@ bool BootAnimation::movie()
        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,21 +447,31 @@ bool BootAnimation::movie()

    // 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) {
    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 (int j=0 ; j<pcount ; j++) {
            for (size_t 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 (mZip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) {
                        if (method == ZipFileRO::kCompressStored) {
                                FileMap* map = zip.createEntryFileMap(entry);
                            FileMap* map = mZip->createEntryFileMap(entry);
                            if (map) {
                                Animation::Frame frame;
                                frame.name = leaf;
@@ -466,7 +485,8 @@ bool BootAnimation::movie()
            }
        }
    }
    }

    mZip->endIteration(cookie);

    // clear screen
    glShadeModel(GL_FLAT);
@@ -494,7 +514,7 @@ bool BootAnimation::movie()
    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 @@ bool BootAnimation::movie()
            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 @@ bool BootAnimation::movie()

        // 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);
            }
+1 −2
Original line number Diff line number Diff line
@@ -95,8 +95,7 @@ private:
    EGLDisplay  mSurface;
    sp<SurfaceControl> mFlingerSurfaceControl;
    sp<Surface> mFlingerSurface;
    bool        mAndroidAnimation;
    ZipFileRO   mZip;
    ZipFileRO   *mZip;
};

// ---------------------------------------------------------------------------
+18 −14
Original line number Diff line number Diff line
@@ -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 @@ isFileDifferent(const char* filePath, size_t fileSize, time_t modifiedTime,
}

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 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr
        return INSTALL_FAILED_INVALID_APK;
    } else {
        struct tm t;
        ZipFileRO::zipTimeToTimespec(when, &t);
        ZipUtils::zipTimeToTimespec(when, &t);
        modTime = mktime(&t);
    }

@@ -273,26 +275,25 @@ iterateOverNativeFiles(JNIEnv *env, jstring javaFilePath, jstring javaCpuAbi, js
    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 @@ iterateOverNativeFiles(JNIEnv *env, jstring javaFilePath, jstring javaCpuAbi, js
                    && 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;
}

+40 −132
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@
#include <unistd.h>
#include <time.h>

typedef void* ZipArchiveHandle;

namespace android {

/*
@@ -51,18 +53,13 @@ typedef void* ZipEntryRO;
/*
 * 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.
 *
 * "open" and "find entry by name" are fast operations and use as little
 * memory as possible.
 *
 * 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.
 * 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 @@ typedef void* ZipEntryRO;
 */
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;


    /*
     * Return the #of entries in the Zip archive.
     * Start iterating over the list of entries in the zip file. Requires
     * a matching call to endIteration with the same cookie.
     */
    int getNumEntries(void) const {
        return mNumEntries;
    }
    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 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.]
     * Return the #of entries in the Zip archive.
     */
    ZipEntryRO findEntryByIndex(int idx) const;
    int getNumEntries();

    /*
     * Copy the filename into the supplied buffer.  Returns 0 on success,
@@ -149,112 +142,27 @@ public:
     *
     * 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 */
    ZipFileRO(const ZipFileRO& src);
    ZipFileRO& operator=(const ZipFileRO& src);

    /* locate and parse the central directory */
    bool mapCentralDirectory(void);

    /* 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;
    ZipFileRO(ZipArchiveHandle handle, char* fileName) : mHandle(handle),
        mFileName(fileName)
    {
    }

    /* zip file name */
    const ZipArchiveHandle mHandle;
    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;
};

}; // namespace android
+17 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#define __LIBS_ZIPUTILS_H

#include <stdio.h>
#include <time.h>

namespace android {

@@ -33,9 +34,11 @@ public:
     * 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 @@ public:
    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() {}
Loading