Merge commit 'goog/master' into merge_master
diff --git a/include/utils/backup_helpers.h b/include/utils/BackupHelpers.h
similarity index 100%
rename from include/utils/backup_helpers.h
rename to include/utils/BackupHelpers.h
diff --git a/include/utils/LogSocket.h b/include/utils/LogSocket.h
deleted file mode 100644
index 01fbfb5..0000000
--- a/include/utils/LogSocket.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* utils/LogSocket.h
-** 
-** Copyright 2008, The Android Open Source Project
-**
-** This file is dual licensed.  It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
-
-#ifndef _UTILS_LOGSOCKET_H
-#define _UTILS_LOGSOCKET_H
-
-#define SOCKET_CLOSE_LOCAL 0
-
-void add_send_stats(int fd, int send);
-void add_recv_stats(int fd, int recv);
-void log_socket_close(int fd, short reason);
-void log_socket_connect(int fd, unsigned int ip, unsigned short port);
-
-#endif /* _UTILS_LOGSOCKET_H */
diff --git a/include/utils/Socket.h b/include/utils/Socket.h
deleted file mode 100644
index 8b7f406..0000000
--- a/include/utils/Socket.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Socket class.  Modeled after Java classes.
-//
-#ifndef _RUNTIME_SOCKET_H
-#define _RUNTIME_SOCKET_H
-
-#include <utils/inet_address.h>
-#include <sys/types.h>
-
-namespace android {
-
-/*
- * Basic socket class, needed to abstract away the differences between
- * BSD sockets and WinSock.  This establishes a streaming network
- * connection (TCP/IP) to somebody.
- */
-class Socket {
-public:
-    Socket(void);
-    ~Socket(void);
-
-    // Create a connection to somewhere.
-    // Return 0 on success.
-    int connect(const char* host, int port);
-    int connect(const InetAddress* addr, int port);
-
-
-    // Close the socket.  Don't try to use this object again after
-    // calling this.  Returns false on failure.
-    bool close(void);
-
-    // If we created the socket without an address, we can use these
-    // to finish the connection.  Returns 0 on success.
-    int bind(const SocketAddress& bindPoint);
-    int connect(const SocketAddress& endPoint);
-
-    // Here we deviate from the traditional object-oriented fanciness
-    // and just provide read/write operators instead of getters for
-    // objects that abstract a stream.
-    //
-    // Standard read/write semantics.
-    int read(void* buf, ssize_t len) const;
-    int write(const void* buf, ssize_t len) const;
-
-    // This must be called once, at program startup.
-    static bool bootInit(void);
-    static void finalShutdown(void);
-
-private:
-    // Internal function that establishes a connection.
-    int doConnect(const InetSocketAddress& addr);
-
-    unsigned long   mSock;      // holds SOCKET or int
-
-    static bool     mBootInitialized;
-};
-
-
-// debug -- unit tests
-void TestSockets(void);
-
-}; // namespace android
-
-#endif // _RUNTIME_SOCKET_H
diff --git a/include/utils/StringArray.h b/include/utils/StringArray.h
new file mode 100644
index 0000000..c2445871
--- /dev/null
+++ b/include/utils/StringArray.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Sortable array of strings.  STL-ish, but STL-free.
+//  
+#ifndef _LIBS_UTILS_STRING_ARRAY_H
+#define _LIBS_UTILS_STRING_ARRAY_H
+
+#include <stdlib.h>
+#include <string.h>
+
+namespace android {
+
+//
+// An expanding array of strings.  Add, get, sort, delete.
+//
+class StringArray {
+public:
+    StringArray();
+    virtual ~StringArray();
+
+    //
+    // Add a string.  A copy of the string is made.
+    //
+    bool push_back(const char* str);
+
+    //
+    // Delete an entry.
+    //
+    void erase(int idx);
+
+    //
+    // Sort the array.
+    //
+    void sort(int (*compare)(const void*, const void*));
+    
+    //
+    // Pass this to the sort routine to do an ascending alphabetical sort.
+    //
+    static int cmpAscendingAlpha(const void* pstr1, const void* pstr2);
+    
+    //
+    // Get the #of items in the array.
+    //
+    inline int size(void) const { return mCurrent; }
+
+    //
+    // Return entry N.
+    // [should use operator[] here]
+    //
+    const char* getEntry(int idx) const {
+        return (unsigned(idx) >= unsigned(mCurrent)) ? NULL : mArray[idx];
+    }
+
+    //
+    // Set entry N to specified string.
+    // [should use operator[] here]
+    //
+    void setEntry(int idx, const char* str);
+
+private:
+    int     mMax;
+    int     mCurrent;
+    char**  mArray;
+};
+
+}; // namespace android
+
+#endif // _LIBS_UTILS_STRING_ARRAY_H
diff --git a/include/utils/ZipEntry.h b/include/utils/ZipEntry.h
deleted file mode 100644
index e4698df..0000000
--- a/include/utils/ZipEntry.h
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Zip archive entries.
-//
-// The ZipEntry class is tightly meshed with the ZipFile class.
-//
-#ifndef __LIBS_ZIPENTRY_H
-#define __LIBS_ZIPENTRY_H
-
-#include "Errors.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-
-namespace android {
-
-class ZipFile;
-
-/*
- * ZipEntry objects represent a single entry in a Zip archive.
- *
- * You can use one of these to get or set information about an entry, but
- * there are no functions here for accessing the data itself.  (We could
- * tuck a pointer to the ZipFile in here for convenience, but that raises
- * the likelihood of using ZipEntry objects after discarding the ZipFile.)
- *
- * File information is stored in two places: next to the file data (the Local
- * File Header, and possibly a Data Descriptor), and at the end of the file
- * (the Central Directory Entry).  The two must be kept in sync.
- */
-class ZipEntry {
-public:
-    friend class ZipFile;
-
-    ZipEntry(void)
-        : mDeleted(false), mMarked(false)
-        {}
-    ~ZipEntry(void) {}
-
-    /*
-     * Returns "true" if the data is compressed.
-     */
-    bool isCompressed(void) const {
-        return mCDE.mCompressionMethod != kCompressStored;
-    }
-    int getCompressionMethod(void) const { return mCDE.mCompressionMethod; }
-
-    /*
-     * Return the uncompressed length.
-     */
-    off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; }
-
-    /*
-     * Return the compressed length.  For uncompressed data, this returns
-     * the same thing as getUncompresesdLen().
-     */
-    off_t getCompressedLen(void) const { return mCDE.mCompressedSize; }
-
-    /*
-     * Return the absolute file offset of the start of the compressed or
-     * uncompressed data.
-     */
-    off_t getFileOffset(void) const {
-        return mCDE.mLocalHeaderRelOffset +
-                LocalFileHeader::kLFHLen +
-                mLFH.mFileNameLength +
-                mLFH.mExtraFieldLength;
-    }
-
-    /*
-     * Return the data CRC.
-     */
-    unsigned long getCRC32(void) const { return mCDE.mCRC32; }
-
-    /*
-     * Return file modification time in UNIX seconds-since-epoch.
-     */
-    time_t getModWhen(void) const;
-
-    /*
-     * Return the archived file name.
-     */
-    const char* getFileName(void) const { return (const char*) mCDE.mFileName; }
-
-    /*
-     * Application-defined "mark".  Can be useful when synchronizing the
-     * contents of an archive with contents on disk.
-     */
-    bool getMarked(void) const { return mMarked; }
-    void setMarked(bool val) { mMarked = val; }
-
-    /*
-     * Some basic functions for raw data manipulation.  "LE" means
-     * Little Endian.
-     */
-    static inline unsigned short getShortLE(const unsigned char* buf) {
-        return buf[0] | (buf[1] << 8);
-    }
-    static inline unsigned long getLongLE(const unsigned char* buf) {
-        return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
-    }
-    static inline void putShortLE(unsigned char* buf, short val) {
-        buf[0] = (unsigned char) val;
-        buf[1] = (unsigned char) (val >> 8);
-    }
-    static inline void putLongLE(unsigned char* buf, long val) {
-        buf[0] = (unsigned char) val;
-        buf[1] = (unsigned char) (val >> 8);
-        buf[2] = (unsigned char) (val >> 16);
-        buf[3] = (unsigned char) (val >> 24);
-    }
-
-    /* defined for Zip archives */
-    enum {
-        kCompressStored     = 0,        // no compression
-        // shrunk           = 1,
-        // reduced 1        = 2,
-        // reduced 2        = 3,
-        // reduced 3        = 4,
-        // reduced 4        = 5,
-        // imploded         = 6,
-        // tokenized        = 7,
-        kCompressDeflated   = 8,        // standard deflate
-        // Deflate64        = 9,
-        // lib imploded     = 10,
-        // reserved         = 11,
-        // bzip2            = 12,
-    };
-
-    /*
-     * Deletion flag.  If set, the entry will be removed on the next
-     * call to "flush".
-     */
-    bool getDeleted(void) const { return mDeleted; }
-
-protected:
-    /*
-     * Initialize the structure from the file, which is pointing at
-     * our Central Directory entry.
-     */
-    status_t initFromCDE(FILE* fp);
-
-    /*
-     * Initialize the structure for a new file.  We need the filename
-     * and comment so that we can properly size the LFH area.  The
-     * filename is mandatory, the comment is optional.
-     */
-    void initNew(const char* fileName, const char* comment);
-
-    /*
-     * Initialize the structure with the contents of a ZipEntry from
-     * another file.
-     */
-    status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry);
-
-    /*
-     * Add some pad bytes to the LFH.  We do this by adding or resizing
-     * the "extra" field.
-     */
-    status_t addPadding(int padding);
-
-    /*
-     * Set information about the data for this entry.
-     */
-    void setDataInfo(long uncompLen, long compLen, unsigned long crc32,
-        int compressionMethod);
-
-    /*
-     * Set the modification date.
-     */
-    void setModWhen(time_t when);
-
-    /*
-     * Return the offset of the local file header.
-     */
-    off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; }
-
-    /*
-     * Set the offset of the local file header, relative to the start of
-     * the current file.
-     */
-    void setLFHOffset(off_t offset) {
-        mCDE.mLocalHeaderRelOffset = (long) offset;
-    }
-
-    /* mark for deletion; used by ZipFile::remove() */
-    void setDeleted(void) { mDeleted = true; }
-
-private:
-    /* these are private and not defined */
-    ZipEntry(const ZipEntry& src);
-    ZipEntry& operator=(const ZipEntry& src);
-
-    /* returns "true" if the CDE and the LFH agree */
-    bool compareHeaders(void) const;
-    void copyCDEtoLFH(void);
-
-    bool        mDeleted;       // set if entry is pending deletion
-    bool        mMarked;        // app-defined marker
-
-    /*
-     * Every entry in the Zip archive starts off with one of these.
-     */
-    class LocalFileHeader {
-    public:
-        LocalFileHeader(void) :
-            mVersionToExtract(0),
-            mGPBitFlag(0),
-            mCompressionMethod(0),
-            mLastModFileTime(0),
-            mLastModFileDate(0),
-            mCRC32(0),
-            mCompressedSize(0),
-            mUncompressedSize(0),
-            mFileNameLength(0),
-            mExtraFieldLength(0),
-            mFileName(NULL),
-            mExtraField(NULL)
-        {}
-        virtual ~LocalFileHeader(void) {
-            delete[] mFileName;
-            delete[] mExtraField;
-        }
-
-        status_t read(FILE* fp);
-        status_t write(FILE* fp);
-
-        // unsigned long mSignature;
-        unsigned short  mVersionToExtract;
-        unsigned short  mGPBitFlag;
-        unsigned short  mCompressionMethod;
-        unsigned short  mLastModFileTime;
-        unsigned short  mLastModFileDate;
-        unsigned long   mCRC32;
-        unsigned long   mCompressedSize;
-        unsigned long   mUncompressedSize;
-        unsigned short  mFileNameLength;
-        unsigned short  mExtraFieldLength;
-        unsigned char*  mFileName;
-        unsigned char*  mExtraField;
-
-        enum {
-            kSignature      = 0x04034b50,
-            kLFHLen         = 30,       // LocalFileHdr len, excl. var fields
-        };
-
-        void dump(void) const;
-    };
-
-    /*
-     * Every entry in the Zip archive has one of these in the "central
-     * directory" at the end of the file.
-     */
-    class CentralDirEntry {
-    public:
-        CentralDirEntry(void) :
-            mVersionMadeBy(0),
-            mVersionToExtract(0),
-            mGPBitFlag(0),
-            mCompressionMethod(0),
-            mLastModFileTime(0),
-            mLastModFileDate(0),
-            mCRC32(0),
-            mCompressedSize(0),
-            mUncompressedSize(0),
-            mFileNameLength(0),
-            mExtraFieldLength(0),
-            mFileCommentLength(0),
-            mDiskNumberStart(0),
-            mInternalAttrs(0),
-            mExternalAttrs(0),
-            mLocalHeaderRelOffset(0),
-            mFileName(NULL),
-            mExtraField(NULL),
-            mFileComment(NULL)
-        {}
-        virtual ~CentralDirEntry(void) {
-            delete[] mFileName;
-            delete[] mExtraField;
-            delete[] mFileComment;
-        }
-
-        status_t read(FILE* fp);
-        status_t write(FILE* fp);
-
-        // unsigned long mSignature;
-        unsigned short  mVersionMadeBy;
-        unsigned short  mVersionToExtract;
-        unsigned short  mGPBitFlag;
-        unsigned short  mCompressionMethod;
-        unsigned short  mLastModFileTime;
-        unsigned short  mLastModFileDate;
-        unsigned long   mCRC32;
-        unsigned long   mCompressedSize;
-        unsigned long   mUncompressedSize;
-        unsigned short  mFileNameLength;
-        unsigned short  mExtraFieldLength;
-        unsigned short  mFileCommentLength;
-        unsigned short  mDiskNumberStart;
-        unsigned short  mInternalAttrs;
-        unsigned long   mExternalAttrs;
-        unsigned long   mLocalHeaderRelOffset;
-        unsigned char*  mFileName;
-        unsigned char*  mExtraField;
-        unsigned char*  mFileComment;
-
-        void dump(void) const;
-
-        enum {
-            kSignature      = 0x02014b50,
-            kCDELen         = 46,       // CentralDirEnt len, excl. var fields
-        };
-    };
-
-    enum {
-        //kDataDescriptorSignature  = 0x08074b50,   // currently unused
-        kDataDescriptorLen  = 16,           // four 32-bit fields
-
-        kDefaultVersion     = 20,           // need deflate, nothing much else
-        kDefaultMadeBy      = 0x0317,       // 03=UNIX, 17=spec v2.3
-        kUsesDataDescr      = 0x0008,       // GPBitFlag bit 3
-    };
-
-    LocalFileHeader     mLFH;
-    CentralDirEntry     mCDE;
-};
-
-}; // namespace android
-
-#endif // __LIBS_ZIPENTRY_H
diff --git a/include/utils/ZipFile.h b/include/utils/ZipFile.h
deleted file mode 100644
index 44df5bb..0000000
--- a/include/utils/ZipFile.h
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// General-purpose Zip archive access.  This class allows both reading and
-// writing to Zip archives, including deletion of existing entries.
-//
-#ifndef __LIBS_ZIPFILE_H
-#define __LIBS_ZIPFILE_H
-
-#include "ZipEntry.h"
-#include "Vector.h"
-#include "Errors.h"
-#include <stdio.h>
-
-namespace android {
-
-/*
- * Manipulate a Zip archive.
- *
- * Some changes will not be visible in the until until "flush" is called.
- *
- * The correct way to update a file archive is to make all changes to a
- * copy of the archive in a temporary file, and then unlink/rename over
- * the original after everything completes.  Because we're only interested
- * in using this for packaging, we don't worry about such things.  Crashing
- * after making changes and before flush() completes could leave us with
- * an unusable Zip archive.
- */
-class ZipFile {
-public:
-    ZipFile(void)
-      : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false)
-      {}
-    ~ZipFile(void) {
-        if (!mReadOnly)
-            flush();
-        if (mZipFp != NULL)
-            fclose(mZipFp);
-        discardEntries();
-    }
-
-    /*
-     * Open a new or existing archive.
-     */
-    typedef enum {
-        kOpenReadOnly   = 0x01,
-        kOpenReadWrite  = 0x02,
-        kOpenCreate     = 0x04,     // create if it doesn't exist
-        kOpenTruncate   = 0x08,     // if it exists, empty it
-    };
-    status_t open(const char* zipFileName, int flags);
-
-    /*
-     * Add a file to the end of the archive.  Specify whether you want the
-     * library to try to store it compressed.
-     *
-     * If "storageName" is specified, the archive will use that instead
-     * of "fileName".
-     *
-     * If there is already an entry with the same name, the call fails.
-     * Existing entries with the same name must be removed first.
-     *
-     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
-     */
-    status_t add(const char* fileName, int compressionMethod,
-        ZipEntry** ppEntry)
-    {
-        return add(fileName, fileName, compressionMethod, ppEntry);
-    }
-    status_t add(const char* fileName, const char* storageName,
-        int compressionMethod, ZipEntry** ppEntry)
-    {
-        return addCommon(fileName, NULL, 0, storageName,
-                         ZipEntry::kCompressStored,
-                         compressionMethod, ppEntry);
-    }
-
-    /*
-     * Add a file that is already compressed with gzip.
-     *
-     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
-     */
-    status_t addGzip(const char* fileName, const char* storageName,
-        ZipEntry** ppEntry)
-    {
-        return addCommon(fileName, NULL, 0, storageName,
-                         ZipEntry::kCompressDeflated,
-                         ZipEntry::kCompressDeflated, ppEntry);
-    }
-
-    /*
-     * Add a file from an in-memory data buffer.
-     *
-     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
-     */
-    status_t add(const void* data, size_t size, const char* storageName,
-        int compressionMethod, ZipEntry** ppEntry)
-    {
-        return addCommon(NULL, data, size, storageName,
-                         ZipEntry::kCompressStored,
-                         compressionMethod, ppEntry);
-    }
-
-    /*
-     * Add an entry by copying it from another zip file.  If "padding" is
-     * nonzero, the specified number of bytes will be added to the "extra"
-     * field in the header.
-     *
-     * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
-     */
-    status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
-        int padding, ZipEntry** ppEntry);
-
-    /*
-     * Mark an entry as having been removed.  It is not actually deleted
-     * from the archive or our internal data structures until flush() is
-     * called.
-     */
-    status_t remove(ZipEntry* pEntry);
-
-    /*
-     * Flush changes.  If mNeedCDRewrite is set, this writes the central dir.
-     */
-    status_t flush(void);
-
-    /*
-     * Expand the data into the buffer provided.  The buffer must hold
-     * at least <uncompressed len> bytes.  Variation expands directly
-     * to a file.
-     *
-     * Returns "false" if an error was encountered in the compressed data.
-     */
-    //bool uncompress(const ZipEntry* pEntry, void* buf) const;
-    //bool uncompress(const ZipEntry* pEntry, FILE* fp) const;
-    void* uncompress(const ZipEntry* pEntry);
-
-    /*
-     * Get an entry, by name.  Returns NULL if not found.
-     *
-     * Does not return entries pending deletion.
-     */
-    ZipEntry* getEntryByName(const char* fileName) const;
-
-    /*
-     * Get the Nth entry in the archive.
-     *
-     * This will return an entry that is pending deletion.
-     */
-    int getNumEntries(void) const { return mEntries.size(); }
-    ZipEntry* getEntryByIndex(int idx) const;
-
-private:
-    /* these are private and not defined */
-    ZipFile(const ZipFile& src);
-    ZipFile& operator=(const ZipFile& src);
-
-    class EndOfCentralDir {
-    public:
-        EndOfCentralDir(void) :
-            mDiskNumber(0),
-            mDiskWithCentralDir(0),
-            mNumEntries(0),
-            mTotalNumEntries(0),
-            mCentralDirSize(0),
-            mCentralDirOffset(0),
-            mCommentLen(0),
-            mComment(NULL)
-            {}
-        virtual ~EndOfCentralDir(void) {
-            delete[] mComment;
-        }
-
-        status_t readBuf(const unsigned char* buf, int len);
-        status_t write(FILE* fp);
-
-        //unsigned long   mSignature;
-        unsigned short  mDiskNumber;
-        unsigned short  mDiskWithCentralDir;
-        unsigned short  mNumEntries;
-        unsigned short  mTotalNumEntries;
-        unsigned long   mCentralDirSize;
-        unsigned long   mCentralDirOffset;      // offset from first disk
-        unsigned short  mCommentLen;
-        unsigned char*  mComment;
-
-        enum {
-            kSignature      = 0x06054b50,
-            kEOCDLen        = 22,       // EndOfCentralDir len, excl. comment
-
-            kMaxCommentLen  = 65535,    // longest possible in ushort
-            kMaxEOCDSearch  = kMaxCommentLen + EndOfCentralDir::kEOCDLen,
-
-        };
-
-        void dump(void) const;
-    };
-
-
-    /* read all entries in the central dir */
-    status_t readCentralDir(void);
-
-    /* crunch deleted entries out */
-    status_t crunchArchive(void);
-
-    /* clean up mEntries */
-    void discardEntries(void);
-
-    /* common handler for all "add" functions */
-    status_t addCommon(const char* fileName, const void* data, size_t size,
-        const char* storageName, int sourceType, int compressionMethod,
-        ZipEntry** ppEntry);
-
-    /* copy all of "srcFp" into "dstFp" */
-    status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32);
-    /* copy all of "data" into "dstFp" */
-    status_t copyDataToFp(FILE* dstFp,
-        const void* data, size_t size, unsigned long* pCRC32);
-    /* copy some of "srcFp" into "dstFp" */
-    status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
-        unsigned long* pCRC32);
-    /* like memmove(), but on parts of a single file */
-    status_t filemove(FILE* fp, off_t dest, off_t src, size_t n);
-    /* compress all of "srcFp" into "dstFp", using Deflate */
-    status_t compressFpToFp(FILE* dstFp, FILE* srcFp,
-        const void* data, size_t size, unsigned long* pCRC32);
-
-    /* get modification date from a file descriptor */
-    time_t getModTime(int fd);
-
-    /*
-     * We use stdio FILE*, which gives us buffering but makes dealing
-     * with files >2GB awkward.  Until we support Zip64, we're fine.
-     */
-    FILE*           mZipFp;             // Zip file pointer
-
-    /* one of these per file */
-    EndOfCentralDir mEOCD;
-
-    /* did we open this read-only? */
-    bool            mReadOnly;
-
-    /* set this when we trash the central dir */
-    bool            mNeedCDRewrite;
-
-    /*
-     * One ZipEntry per entry in the zip file.  I'm using pointers instead
-     * of objects because it's easier than making operator= work for the
-     * classes and sub-classes.
-     */
-    Vector<ZipEntry*>   mEntries;
-};
-
-}; // namespace android
-
-#endif // __LIBS_ZIPFILE_H
diff --git a/include/utils/inet_address.h b/include/utils/inet_address.h
deleted file mode 100644
index dbd8672..0000000
--- a/include/utils/inet_address.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Internet address classes.  Modeled after Java classes.
-//
-#ifndef _RUNTIME_INET_ADDRESS_H
-#define _RUNTIME_INET_ADDRESS_H
-
-#ifdef HAVE_ANDROID_OS
-#error DO NOT USE THIS FILE IN THE DEVICE BUILD
-#endif
-
-
-namespace android {
-
-/*
- * This class holds Internet addresses.  Perhaps more useful is its
- * ability to look up addresses by name.
- *
- * Invoke one of the static factory methods to create a new object.
- */
-class InetAddress {
-public:
-    virtual ~InetAddress(void);
-
-    // create from w.x.y.z or foo.bar.com notation
-    static InetAddress* getByName(const char* host);
-
-    // copy-construction
-    InetAddress(const InetAddress& orig);
-
-    const void* getAddress(void) const { return mAddress; }
-    int getAddressLength(void) const { return mLength; }
-    const char* getHostName(void) const { return mName; }
-
-private:
-    InetAddress(void);
-    // assignment (private)
-    InetAddress& operator=(const InetAddress& addr);
-
-    // use a void* here so we don't have to expose actual socket headers
-    void*       mAddress;   // this is really a ptr to sockaddr_in
-    int         mLength;
-    char*       mName;
-};
-
-
-/*
- * Base class for socket addresses.
- */
-class SocketAddress {
-public:
-    SocketAddress() {}
-    virtual ~SocketAddress() {}
-};
-
-
-/*
- * Internet address class.  This combines an InetAddress with a port.
- */
-class InetSocketAddress : public SocketAddress {
-public:
-    InetSocketAddress() :
-        mAddress(0), mPort(-1)
-        {}
-    ~InetSocketAddress(void) {
-        delete mAddress;
-    }
-
-    // Create an address with a host wildcard (useful for servers).
-    bool create(int port);
-    // Create an address with the specified host and port.
-    bool create(const InetAddress* addr, int port);
-    // Create an address with the specified host and port.  Does the
-    // hostname lookup.
-    bool create(const char* host, int port);
-
-    const InetAddress* getAddress(void) const { return mAddress; }
-    const int getPort(void) const { return mPort; }
-    const char* getHostName(void) const { return mAddress->getHostName(); }
-
-private:
-    InetAddress* mAddress;
-    int         mPort;
-};
-
-}; // namespace android
-
-#endif // _RUNTIME_INET_ADDRESS_H
diff --git a/include/utils/string_array.h b/include/utils/string_array.h
deleted file mode 100644
index 064dda2..0000000
--- a/include/utils/string_array.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Sortable array of strings.  STL-ish, but STL-free.
-//  
-#ifndef _LIBS_UTILS_STRING_ARRAY_H
-#define _LIBS_UTILS_STRING_ARRAY_H
-
-#include <stdlib.h>
-#include <string.h>
-
-namespace android {
-
-//
-// An expanding array of strings.  Add, get, sort, delete.
-//
-class StringArray {
-public:
-    StringArray()
-        : mMax(0), mCurrent(0), mArray(NULL)
-        {}
-    virtual ~StringArray() {
-        for (int i = 0; i < mCurrent; i++)
-            delete[] mArray[i];
-        delete[] mArray;
-    }
-
-    //
-    // Add a string.  A copy of the string is made.
-    //
-    bool push_back(const char* str) {
-        if (mCurrent >= mMax) {
-            char** tmp;
-
-            if (mMax == 0)
-                mMax = 16;      // initial storage
-            else
-                mMax *= 2;
-
-            tmp = new char*[mMax];
-            if (tmp == NULL)
-                return false;
-
-            memcpy(tmp, mArray, mCurrent * sizeof(char*));
-            delete[] mArray;
-            mArray = tmp;
-        }
-
-        int len = strlen(str);
-        mArray[mCurrent] = new char[len+1];
-        memcpy(mArray[mCurrent], str, len+1);
-        mCurrent++;
-
-        return true;
-    }
-
-    //
-    // Delete an entry.
-    //
-    void erase(int idx) {
-        if (idx < 0 || idx >= mCurrent)
-            return;
-        delete[] mArray[idx];
-        if (idx < mCurrent-1) {
-            memmove(&mArray[idx], &mArray[idx+1],
-                (mCurrent-1 - idx) * sizeof(char*));
-        }
-        mCurrent--;
-    }
-
-    //
-    // Sort the array.
-    //
-    void sort(int (*compare)(const void*, const void*)) {
-        qsort(mArray, mCurrent, sizeof(char*), compare);
-    }
-
-    //
-    // Pass this to the sort routine to do an ascending alphabetical sort.
-    //
-    static int cmpAscendingAlpha(const void* pstr1, const void* pstr2) {
-        return strcmp(*(const char**)pstr1, *(const char**)pstr2);
-    }
-
-    //
-    // Get the #of items in the array.
-    //
-    inline int size(void) const { return mCurrent; }
-
-    //
-    // Return entry N.
-    // [should use operator[] here]
-    //
-    const char* getEntry(int idx) const {
-        if (idx < 0 || idx >= mCurrent)
-            return NULL;
-        return mArray[idx];
-    }
-
-    //
-    // Set entry N to specified string.
-    // [should use operator[] here]
-    //
-    void setEntry(int idx, const char* str) {
-        if (idx < 0 || idx >= mCurrent)
-            return;
-        delete[] mArray[idx];
-        int len = strlen(str);
-        mArray[idx] = new char[len+1];
-        memcpy(mArray[idx], str, len+1);
-    }
-
-private:
-    int     mMax;
-    int     mCurrent;
-    char**  mArray;
-};
-
-}; // namespace android
-
-#endif // _LIBS_UTILS_STRING_ARRAY_H
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 1acc1a4..3f5cb85 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -32,6 +32,7 @@
 	StopWatch.cpp \
 	String8.cpp \
 	String16.cpp \
+	StringArray.cpp \
 	SystemClock.cpp \
 	TextOutput.cpp \
 	Threads.cpp \
@@ -40,29 +41,15 @@
     ZipFileCRO.cpp \
 	ZipFileRO.cpp \
 	ZipUtils.cpp \
-	misc.cpp \
-	LogSocket.cpp
+	misc.cpp
 
-#
-# The cpp files listed here do not belong in the device
-# build.  Consult with the swetland before even thinking about
-# putting them in commonSources.
-#
-# They're used by the simulator runtime and by host-side tools like
-# aapt and the simulator front-end.
-#
-hostSources:= \
-	InetAddress.cpp \
-	Socket.cpp \
-	ZipEntry.cpp \
-	ZipFile.cpp
 
 # For the host
 # =====================================================
 
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:= $(commonSources) $(hostSources)
+LOCAL_SRC_FILES:= $(commonSources)
 
 ifeq ($(HOST_OS),linux)
 # Use the futex based mutex and condition variable
@@ -96,12 +83,8 @@
 LOCAL_SRC_FILES:= \
 	$(commonSources) \
 	Unicode.cpp \
-    backup_data.cpp \
-	backup_helper_file.cpp
-
-ifeq ($(TARGET_SIMULATOR),true)
-LOCAL_SRC_FILES += $(hostSources)
-endif
+    BackupData.cpp \
+	BackupHelpers.cpp
 
 ifeq ($(TARGET_OS),linux)
 # Use the futex based mutex and condition variable
@@ -129,9 +112,5 @@
 endif # sim
 
 LOCAL_MODULE:= libutils
-
-#LOCAL_CFLAGS+=
-#LOCAL_LDFLAGS:=
-
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/libs/utils/backup_data.cpp b/libs/utils/BackupData.cpp
similarity index 99%
rename from libs/utils/backup_data.cpp
rename to libs/utils/BackupData.cpp
index 95c05b7..120f23d 100644
--- a/libs/utils/backup_data.cpp
+++ b/libs/utils/BackupData.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "backup_data"
 
-#include <utils/backup_helpers.h>
+#include <utils/BackupHelpers.h>
 #include <utils/ByteOrder.h>
 
 #include <stdio.h>
diff --git a/libs/utils/backup_helper_file.cpp b/libs/utils/BackupHelpers.cpp
similarity index 99%
rename from libs/utils/backup_helper_file.cpp
rename to libs/utils/BackupHelpers.cpp
index 7ec2ce8..7f423a8 100644
--- a/libs/utils/backup_helper_file.cpp
+++ b/libs/utils/BackupHelpers.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "file_backup_helper"
 
-#include <utils/backup_helpers.h>
+#include <utils/BackupHelpers.h>
 
 #include <utils/KeyedVector.h>
 #include <utils/ByteOrder.h>
@@ -350,10 +350,11 @@
             FileState& g = newSnapshot.editValueAt(m);
 
             int fd = open(realFilename.string(), O_RDONLY);
-            if (fd != -1) {
+            if (fd < 0) {
                 // We can't open the file.  Don't report it as a delete either.  Let the
                 // server keep the old version.  Maybe they'll be able to deal with it
                 // on restore.
+                LOGP("Unable to open file %s - skipping", realFilename.string());
             } else {
                 g.crc32 = compute_crc32(fd);
 
diff --git a/libs/utils/characterData.h b/libs/utils/CharacterData.h
similarity index 100%
rename from libs/utils/characterData.h
rename to libs/utils/CharacterData.h
diff --git a/libs/utils/InetAddress.cpp b/libs/utils/InetAddress.cpp
deleted file mode 100644
index 39a0a68..0000000
--- a/libs/utils/InetAddress.cpp
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Internet address class.
-//
-#ifdef HAVE_WINSOCK
-# include <winsock2.h>
-#else
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-//# include <arpa/inet.h>
-# include <netdb.h>
-#endif
-
-#include <utils/inet_address.h>
-#include <utils/threads.h>
-#include <utils/Log.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-using namespace android;
-
-
-/*
- * ===========================================================================
- *      InetAddress
- * ===========================================================================
- */
-
-// lock for the next couple of functions; could tuck into InetAddress
-static Mutex*   gGHBNLock;
-
-/*
- * Lock/unlock access to the hostent struct returned by gethostbyname().
- */
-static inline void lock_gethostbyname(void)
-{
-    if (gGHBNLock == NULL)
-        gGHBNLock = new Mutex;
-    gGHBNLock->lock();
-}
-static inline void unlock_gethostbyname(void)
-{
-    assert(gGHBNLock != NULL);
-    gGHBNLock->unlock();
-}
-
-
-/*
- * Constructor -- just init members.  This is private so that callers
- * are required to use getByName().
- */
-InetAddress::InetAddress(void)
-    : mAddress(NULL), mLength(-1), mName(NULL)
-{
-}
-
-/*
- * Destructor -- free address storage.
- */
-InetAddress::~InetAddress(void)
-{
-    delete[] (char*) mAddress;
-    delete[] mName;
-}
-
-/*
- * Copy constructor.
- */
-InetAddress::InetAddress(const InetAddress& orig)
-{
-    *this = orig;   // use assignment code
-}
-
-/*
- * Assignment operator.
- */
-InetAddress& InetAddress::operator=(const InetAddress& addr)
-{
-    // handle self-assignment
-    if (this == &addr)
-        return *this;
-    // copy mLength and mAddress
-    mLength = addr.mLength;
-    if (mLength > 0) {
-        mAddress = new char[mLength];
-        memcpy(mAddress, addr.mAddress, mLength);
-        LOG(LOG_DEBUG, "socket",
-            "HEY: copied %d bytes in assignment operator\n", mLength);
-    } else {
-        mAddress = NULL;
-    }
-    // copy mName
-    mName = new char[strlen(addr.mName)+1];
-    strcpy(mName, addr.mName);
-
-    return *this;
-}
-
-/*
- * Create a new object from a name or a dotted-number IP notation.
- *
- * Returns NULL on failure.
- */
-InetAddress*
-InetAddress::getByName(const char* host)
-{
-    InetAddress* newAddr = NULL;
-    struct sockaddr_in addr;
-    struct hostent* he;
-    DurationTimer hostTimer, lockTimer;
-
-    // gethostbyname() isn't reentrant, so we need to lock things until
-    // we can copy the data out.
-    lockTimer.start();
-    lock_gethostbyname();
-    hostTimer.start();
-
-    he = gethostbyname(host);
-    if (he == NULL) {
-        LOG(LOG_WARN, "socket", "WARNING: cannot resolve host %s\n", host);
-        unlock_gethostbyname();
-        return NULL;
-    }
-
-    memcpy(&addr.sin_addr, he->h_addr, he->h_length);
-    addr.sin_family = he->h_addrtype;
-    addr.sin_port = 0;
-
-    // got it, unlock us
-    hostTimer.stop();
-    he = NULL;
-    unlock_gethostbyname();
-
-    lockTimer.stop();
-    if ((long) lockTimer.durationUsecs() > 100000) {
-        long lockTime = (long) lockTimer.durationUsecs();
-        long hostTime = (long) hostTimer.durationUsecs();
-        LOG(LOG_DEBUG, "socket",
-            "Lookup of %s took %.3fs (gethostbyname=%.3fs lock=%.3fs)\n",
-            host, lockTime / 1000000.0, hostTime / 1000000.0,
-            (lockTime - hostTime) / 1000000.0);
-    }
-
-    // Alloc storage and copy it over.
-    newAddr = new InetAddress();
-    if (newAddr == NULL)
-        return NULL;
-
-    newAddr->mLength = sizeof(struct sockaddr_in);
-    newAddr->mAddress = new char[sizeof(struct sockaddr_in)];
-    if (newAddr->mAddress == NULL) {
-        delete newAddr;
-        return NULL;
-    }
-    memcpy(newAddr->mAddress, &addr, newAddr->mLength);
-
-    // Keep this for debug messages.
-    newAddr->mName = new char[strlen(host)+1];
-    if (newAddr->mName == NULL) {
-        delete newAddr;
-        return NULL;
-    }
-    strcpy(newAddr->mName, host);
-
-    return newAddr;
-}
-
-
-/*
- * ===========================================================================
- *      InetSocketAddress
- * ===========================================================================
- */
-
-/*
- * Create an address with the host wildcard (INADDR_ANY).
- */
-bool InetSocketAddress::create(int port)
-{
-    assert(mAddress == NULL);
-
-    mAddress = InetAddress::getByName("0.0.0.0");
-    if (mAddress == NULL)
-        return false;
-    mPort = port;
-    return true;
-}
-
-/*
- * Create address with host and port specified.
- */
-bool InetSocketAddress::create(const InetAddress* addr, int port)
-{
-    assert(mAddress == NULL);
-
-    mAddress = new InetAddress(*addr);  // make a copy
-    if (mAddress == NULL)
-        return false;
-    mPort = port;
-    return true;
-}
-
-/*
- * Create address with host and port specified.
- */
-bool InetSocketAddress::create(const char* host, int port)
-{
-    assert(mAddress == NULL);
-
-    mAddress = InetAddress::getByName(host);
-    if (mAddress == NULL)
-        return false;
-    mPort = port;
-    return true;
-}
-
diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp
deleted file mode 100644
index 55c1b99..0000000
--- a/libs/utils/LogSocket.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef HAVE_WINSOCK
-//#define SOCKETLOG
-#endif
-
-#ifdef SOCKETLOG
-
-#define LOG_TAG "SOCKETLOG"
-
-#include <string.h>
-#include <cutils/log.h>
-#include "utils/LogSocket.h"
-#include "utils/logger.h"
-#include "cutils/hashmap.h"
-
-// defined in //device/data/etc/event-log-tags
-#define SOCKET_CLOSE_LOG 51000
-
-static Hashmap* statsMap = NULL;
-
-#define LOG_LIST_NUMBER 5
-
-typedef struct SocketStats {
-    int fd;
-    unsigned int send;
-    unsigned int recv;
-    unsigned int ip;
-    unsigned short port;
-    short reason;
-}SocketStats;
-
-SocketStats *get_socket_stats(int fd) {
-    if (statsMap == NULL) {
-        statsMap = hashmapCreate(8, &hashmapIntHash, &hashmapIntEquals);
-    }
-
-    SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd);
-    if (s == NULL) {
-        // LOGD("create SocketStats for fd %d", fd);
-        s = (SocketStats*) malloc(sizeof(SocketStats));
-        memset(s, 0, sizeof(SocketStats));
-        s->fd = fd;
-        hashmapPut(statsMap, &s->fd, s);
-    }
-    return s;
-}
-
-void log_socket_connect(int fd, unsigned int ip, unsigned short port) {
-    // LOGD("log_socket_connect for fd %d ip %d port%d", fd, ip, port);
-    SocketStats *s = get_socket_stats(fd);
-    s->ip = ip;
-    s->port = port;
-}
-
-void add_send_stats(int fd, int send) {
-    if (send <=0) {
-        LOGE("add_send_stats send %d", send);
-        return;
-    }
-    SocketStats *s = get_socket_stats(fd);
-    s->send += send;
-    // LOGD("add_send_stats for fd %d ip %d port%d", fd, s->ip, s->port);
-}
-
-void add_recv_stats(int fd, int recv) {
-    if (recv <=0) {
-        LOGE("add_recv_stats recv %d", recv);
-        return;
-    }
-    SocketStats *s = get_socket_stats(fd);
-    s->recv += recv;
-    // LOGD("add_recv_stats for fd %d ip %d port%d", fd, s->ip, s->port);
-}
-
-char* put_int(char* buf, int value) {
-    *buf = EVENT_TYPE_INT;
-    buf++;
-    memcpy(buf, &value, sizeof(int));
-    return buf + sizeof(int);
-}
-
-void log_socket_close(int fd, short reason) {
-    if (statsMap) {
-        SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd);
-        if (s != NULL) {
-            if (s->send != 0 || s->recv != 0) {
-                s->reason = reason;
-                // 5 int + list type need 2 bytes
-                char buf[LOG_LIST_NUMBER * 5 + 2];
-                buf[0] = EVENT_TYPE_LIST;
-                buf[1] = LOG_LIST_NUMBER;
-                char* writePos = buf + 2;
-                writePos = put_int(writePos, s->send);
-                writePos = put_int(writePos, s->recv);
-                writePos = put_int(writePos, s->ip);
-                writePos = put_int(writePos, s->port);
-                writePos = put_int(writePos, s->reason);
-                
-                android_bWriteLog(SOCKET_CLOSE_LOG, buf, sizeof(buf));
-                // LOGD("send %d recv %d reason %d", s->send, s->recv, s->reason);
-            }
-            hashmapRemove(statsMap, &s->fd);
-            free(s);
-        }
-    }
-}
-
-#else
-void add_send_stats(int fd, int send) {} 
-void add_recv_stats(int fd, int recv) {}
-void log_socket_close(int fd, short reason) {}
-void log_socket_connect(int fd, unsigned int ip, unsigned short port) {}
-#endif
diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp
deleted file mode 100644
index 51509a3..0000000
--- a/libs/utils/Socket.cpp
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Internet address class.
-//
-
-#ifdef HAVE_WINSOCK
-// This needs to come first, or Cygwin gets concerned about a potential
-// clash between WinSock and <sys/types.h>.
-# include <winsock2.h>
-#endif
-
-#include <utils/Socket.h>
-#include <utils/inet_address.h>
-#include <utils/Log.h>
-#include <utils/Timers.h>
-
-#ifndef HAVE_WINSOCK
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-# include <arpa/inet.h>
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-
-using namespace android;
-
-
-/*
- * ===========================================================================
- *      Socket
- * ===========================================================================
- */
-
-#ifndef INVALID_SOCKET
-# define INVALID_SOCKET (-1)
-#endif
-#define UNDEF_SOCKET   ((unsigned long) INVALID_SOCKET)
-
-/*static*/ bool Socket::mBootInitialized = false;
-
-/*
- * Extract system-dependent error code.
- */
-static inline int getSocketError(void) {
-#ifdef HAVE_WINSOCK
-    return WSAGetLastError();
-#else
-    return errno;
-#endif
-}
-
-/*
- * One-time initialization for socket code.
- */
-/*static*/ bool Socket::bootInit(void)
-{
-#ifdef HAVE_WINSOCK
-    WSADATA wsaData;
-    int err;
-
-    err = WSAStartup(MAKEWORD(2, 0), &wsaData);
-    if (err != 0) {
-        LOG(LOG_ERROR, "socket", "Unable to start WinSock\n");
-        return false;
-    }
-
-    LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n",
-        LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
-#endif
-
-    mBootInitialized = true;
-    return true;
-}
-
-/*
- * One-time shutdown for socket code.
- */
-/*static*/ void Socket::finalShutdown(void)
-{
-#ifdef HAVE_WINSOCK
-    WSACleanup();
-#endif
-    mBootInitialized = false;
-}
-
-
-/*
- * Simple constructor.  Allow the application to create us and then make
- * bind/connect calls.
- */
-Socket::Socket(void)
-    : mSock(UNDEF_SOCKET)
-{
-    if (!mBootInitialized)
-        LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n");
-}
-
-/*
- * Destructor.  Closes the socket and resets our storage.
- */
-Socket::~Socket(void)
-{
-    close();
-}
-
-
-/*
- * Create a socket and connect to the specified host and port.
- */
-int Socket::connect(const char* host, int port)
-{
-    if (mSock != UNDEF_SOCKET) {
-        LOG(LOG_WARN, "socket", "Socket already connected\n");
-        return -1;
-    }
-
-    InetSocketAddress sockAddr;
-    if (!sockAddr.create(host, port))
-        return -1;
-
-    //return doConnect(sockAddr);
-    int foo;
-    foo = doConnect(sockAddr);
-    return foo;
-}
-
-/*
- * Create a socket and connect to the specified host and port.
- */
-int Socket::connect(const InetAddress* addr, int port)
-{
-    if (mSock != UNDEF_SOCKET) {
-        LOG(LOG_WARN, "socket", "Socket already connected\n");
-        return -1;
-    }
-
-    InetSocketAddress sockAddr;
-    if (!sockAddr.create(addr, port))
-        return -1;
-
-    return doConnect(sockAddr);
-}
-
-/*
- * Finish creating a socket by connecting to the remote host.
- *
- * Returns 0 on success.
- */
-int Socket::doConnect(const InetSocketAddress& sockAddr)
-{
-#ifdef HAVE_WINSOCK
-    SOCKET sock;
-#else
-    int sock;
-#endif
-    const InetAddress* addr = sockAddr.getAddress();
-    int port = sockAddr.getPort();
-    struct sockaddr_in inaddr;
-    DurationTimer connectTimer;
-
-    assert(sizeof(struct sockaddr_in) == addr->getAddressLength());
-    memcpy(&inaddr, addr->getAddress(), addr->getAddressLength());
-    inaddr.sin_port = htons(port);
-
-    //fprintf(stderr, "--- connecting to %s:%d\n",
-    //    sockAddr.getHostName(), port);
-
-    sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
-    if (sock == INVALID_SOCKET) {
-        int err = getSocketError();
-        LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err);
-        return (err != 0) ? err : -1;
-    }
-
-    connectTimer.start();
-
-    if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) {
-        int err = getSocketError();
-        LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n",
-            sockAddr.getHostName(), port, err);
-        return (err != 0) ? err : -1;
-    }
-
-    connectTimer.stop();
-    if ((long) connectTimer.durationUsecs() > 100000) {
-        LOG(LOG_INFO, "socket",
-            "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(),
-            port, ((long) connectTimer.durationUsecs()) / 1000000.0);
-    }
-
-    mSock = (unsigned long) sock;
-    LOG(LOG_VERBOSE, "socket",
-        "--- connected to %s:%d\n", sockAddr.getHostName(), port);
-    return 0;
-}
-
-
-/*
- * Close the socket if it needs closing.
- */
-bool Socket::close(void)
-{
-    if (mSock != UNDEF_SOCKET) {
-        //fprintf(stderr, "--- closing socket %lu\n", mSock);
-#ifdef HAVE_WINSOCK
-        if (::closesocket((SOCKET) mSock) != 0)
-            return false;
-#else
-        if (::close((int) mSock) != 0)
-            return false;
-#endif
-    }
-
-    mSock = UNDEF_SOCKET;
-
-    return true;
-}
-
-/*
- * Read data from socket.
- *
- * Standard semantics: read up to "len" bytes into "buf".  Returns the
- * number of bytes read, or less than zero on error.
- */
-int Socket::read(void* buf, ssize_t len) const
-{
-    if (mSock == UNDEF_SOCKET) {
-        LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n");
-        return -500;
-    }
-
-#ifdef HAVE_WINSOCK
-    SOCKET sock = (SOCKET) mSock;
-#else
-    int sock = (int) mSock;
-#endif
-    int cc;
-
-    cc = recv(sock, (char*)buf, len, 0);
-    if (cc < 0) {
-        int err = getSocketError();
-        return (err > 0) ? -err : -1;
-    }
-
-    return cc;
-}
-
-/*
- * Write data to a socket.
- *
- * Standard semantics: write up to "len" bytes into "buf".  Returns the
- * number of bytes written, or less than zero on error.
- */
-int Socket::write(const void* buf, ssize_t len) const
-{
-    if (mSock == UNDEF_SOCKET) {
-        LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n");
-        return -500;
-    }
-
-#ifdef HAVE_WINSOCK
-    SOCKET sock = (SOCKET) mSock;
-#else
-    int sock = (int) mSock;
-#endif
-    int cc;
-
-    cc = send(sock, (const char*)buf, len, 0);
-    if (cc < 0) {
-        int err = getSocketError();
-        return (err > 0) ? -err : -1;
-    }
-
-    return cc;
-}
-
-
-/*
- * ===========================================================================
- *      Socket tests
- * ===========================================================================
- */
-
-/*
- * Read all data from the socket.  The data is read into a buffer that
- * expands as needed.
- *
- * On exit, the buffer is returned, and the length of the data is stored
- * in "*sz".  A null byte is added to the end, but is not included in
- * the length.
- */
-static char* socketReadAll(const Socket& s, int *sz)
-{
-    int max, r;
-    char *data, *ptr, *tmp;
-
-    data = (char*) malloc(max = 32768);
-    if (data == NULL)
-        return NULL;
-
-    ptr = data;
-    
-    for (;;) {
-        if ((ptr - data) == max) {
-            tmp = (char*) realloc(data, max *= 2);
-            if(tmp == 0) {
-                free(data);
-                return 0;
-            }
-        }
-        r = s.read(ptr, max - (ptr - data));
-        if (r == 0)
-            break;
-        if (r < 0) {
-            LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r);
-            break;
-        }
-        ptr += r;
-    }
-
-    if ((ptr - data) == max) {
-        tmp = (char*) realloc(data, max + 1);
-        if (tmp == NULL) {
-            free(data);
-            return NULL;
-        }
-    }
-    *ptr = '\0';
-    *sz = (ptr - data);
-    return data;
-}
-
-/*
- * Exercise the Socket class.
- */
-void android::TestSockets(void)
-{
-    printf("----- SOCKET TEST ------\n");
-    Socket::bootInit();
-
-    char* buf = NULL;
-    int len, cc;
-    const char* kTestStr =
-        "GET / HTTP/1.0\n"
-        "Connection: close\n"
-        "\n";
-
-    Socket sock;
-    if (sock.connect("www.google.com", 80) != 0) {
-        fprintf(stderr, "socket connected failed\n");
-        goto bail;
-    }
-
-    cc = sock.write(kTestStr, strlen(kTestStr));
-    if (cc != (int) strlen(kTestStr)) {
-        fprintf(stderr, "write failed, res=%d\n", cc);
-        goto bail;
-    }
-    buf = socketReadAll(sock, &len);
-
-    printf("GOT '%s'\n", buf);
-
-bail:
-    sock.close();
-    free(buf);
-}
-
diff --git a/libs/utils/StringArray.cpp b/libs/utils/StringArray.cpp
new file mode 100644
index 0000000..aa42d68
--- /dev/null
+++ b/libs/utils/StringArray.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Sortable array of strings.  STL-ish, but STL-free.
+//  
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/StringArray.h>
+
+namespace android {
+
+//
+// An expanding array of strings.  Add, get, sort, delete.
+//
+StringArray::StringArray()
+    : mMax(0), mCurrent(0), mArray(NULL)
+{
+}
+
+StringArray:: ~StringArray() {
+    for (int i = 0; i < mCurrent; i++)
+        delete[] mArray[i];
+    delete[] mArray;
+}
+
+//
+// Add a string.  A copy of the string is made.
+//
+bool StringArray::push_back(const char* str) {
+    if (mCurrent >= mMax) {
+        char** tmp;
+
+        if (mMax == 0)
+            mMax = 16;      // initial storage
+        else
+            mMax *= 2;
+
+        tmp = new char*[mMax];
+        if (tmp == NULL)
+            return false;
+
+        memcpy(tmp, mArray, mCurrent * sizeof(char*));
+        delete[] mArray;
+        mArray = tmp;
+    }
+
+    int len = strlen(str);
+    mArray[mCurrent] = new char[len+1];
+    memcpy(mArray[mCurrent], str, len+1);
+    mCurrent++;
+
+    return true;
+}
+
+//
+// Delete an entry.
+//
+void StringArray::erase(int idx) {
+    if (idx < 0 || idx >= mCurrent)
+        return;
+    delete[] mArray[idx];
+    if (idx < mCurrent-1) {
+        memmove(&mArray[idx], &mArray[idx+1],
+                (mCurrent-1 - idx) * sizeof(char*));
+    }
+    mCurrent--;
+}
+
+//
+// Sort the array.
+//
+void StringArray::sort(int (*compare)(const void*, const void*)) {
+    qsort(mArray, mCurrent, sizeof(char*), compare);
+}
+
+//
+// Pass this to the sort routine to do an ascending alphabetical sort.
+//
+int StringArray::cmpAscendingAlpha(const void* pstr1, const void* pstr2) {
+    return strcmp(*(const char**)pstr1, *(const char**)pstr2);
+}
+
+//
+// Set entry N to specified string.
+// [should use operator[] here]
+//
+void StringArray::setEntry(int idx, const char* str) {
+    if (idx < 0 || idx >= mCurrent)
+        return;
+    delete[] mArray[idx];
+    int len = strlen(str);
+    mArray[idx] = new char[len+1];
+    memcpy(mArray[idx], str, len+1);
+}
+
+
+}; // namespace android
diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp
index 33f535f..f92703e 100644
--- a/libs/utils/Unicode.cpp
+++ b/libs/utils/Unicode.cpp
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#include "utils/AndroidUnicode.h"
-#include "characterData.h"
+#include <utils/AndroidUnicode.h>
+#include "CharacterData.h"
 
 #define LOG_TAG "Unicode"
-#include "utils/Log.h"
+#include <utils/Log.h>
 
 // ICU headers for using macros
 #include <unicode/utf16.h>
diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp
deleted file mode 100644
index fbc9e67..0000000
--- a/libs/utils/ZipEntry.cpp
+++ /dev/null
@@ -1,696 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Access to entries in a Zip archive.
-//
-
-#define LOG_TAG "zip"
-
-#include "utils/ZipEntry.h"
-#include "utils/Log.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-
-using namespace android;
-
-/*
- * Initialize a new ZipEntry structure from a FILE* positioned at a
- * CentralDirectoryEntry.
- *
- * On exit, the file pointer will be at the start of the next CDE or
- * at the EOCD.
- */
-status_t ZipEntry::initFromCDE(FILE* fp)
-{
-    status_t result;
-    long posn;
-    bool hasDD;
-
-    //LOGV("initFromCDE ---\n");
-
-    /* read the CDE */
-    result = mCDE.read(fp);
-    if (result != NO_ERROR) {
-        LOGD("mCDE.read failed\n");
-        return result;
-    }
-
-    //mCDE.dump();
-
-    /* using the info in the CDE, go load up the LFH */
-    posn = ftell(fp);
-    if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
-        LOGD("local header seek failed (%ld)\n",
-            mCDE.mLocalHeaderRelOffset);
-        return UNKNOWN_ERROR;
-    }
-
-    result = mLFH.read(fp);
-    if (result != NO_ERROR) {
-        LOGD("mLFH.read failed\n");
-        return result;
-    }
-
-    if (fseek(fp, posn, SEEK_SET) != 0)
-        return UNKNOWN_ERROR;
-
-    //mLFH.dump();
-
-    /*
-     * We *might* need to read the Data Descriptor at this point and
-     * integrate it into the LFH.  If this bit is set, the CRC-32,
-     * compressed size, and uncompressed size will be zero.  In practice
-     * these seem to be rare.
-     */
-    hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
-    if (hasDD) {
-        // do something clever
-        //LOGD("+++ has data descriptor\n");
-    }
-
-    /*
-     * Sanity-check the LFH.  Note that this will fail if the "kUsesDataDescr"
-     * flag is set, because the LFH is incomplete.  (Not a problem, since we
-     * prefer the CDE values.)
-     */
-    if (!hasDD && !compareHeaders()) {
-        LOGW("WARNING: header mismatch\n");
-        // keep going?
-    }
-
-    /*
-     * If the mVersionToExtract is greater than 20, we may have an
-     * issue unpacking the record -- could be encrypted, compressed
-     * with something we don't support, or use Zip64 extensions.  We
-     * can defer worrying about that to when we're extracting data.
-     */
-
-    return NO_ERROR;
-}
-
-/*
- * Initialize a new entry.  Pass in the file name and an optional comment.
- *
- * Initializes the CDE and the LFH.
- */
-void ZipEntry::initNew(const char* fileName, const char* comment)
-{
-    assert(fileName != NULL && *fileName != '\0');  // name required
-
-    /* most fields are properly initialized by constructor */
-    mCDE.mVersionMadeBy = kDefaultMadeBy;
-    mCDE.mVersionToExtract = kDefaultVersion;
-    mCDE.mCompressionMethod = kCompressStored;
-    mCDE.mFileNameLength = strlen(fileName);
-    if (comment != NULL)
-        mCDE.mFileCommentLength = strlen(comment);
-    mCDE.mExternalAttrs = 0x81b60020;   // matches what WinZip does
-
-    if (mCDE.mFileNameLength > 0) {
-        mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
-        strcpy((char*) mCDE.mFileName, fileName);
-    }
-    if (mCDE.mFileCommentLength > 0) {
-        /* TODO: stop assuming null-terminated ASCII here? */
-        mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
-        strcpy((char*) mCDE.mFileComment, comment);
-    }
-
-    copyCDEtoLFH();
-}
-
-/*
- * Initialize a new entry, starting with the ZipEntry from a different
- * archive.
- *
- * Initializes the CDE and the LFH.
- */
-status_t ZipEntry::initFromExternal(const ZipFile* pZipFile,
-    const ZipEntry* pEntry)
-{
-    /*
-     * Copy everything in the CDE over, then fix up the hairy bits.
-     */
-    memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
-
-    if (mCDE.mFileNameLength > 0) {
-        mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
-        if (mCDE.mFileName == NULL)
-            return NO_MEMORY;
-        strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
-    }
-    if (mCDE.mFileCommentLength > 0) {
-        mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
-        if (mCDE.mFileComment == NULL)
-            return NO_MEMORY;
-        strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
-    }
-    if (mCDE.mExtraFieldLength > 0) {
-        /* we null-terminate this, though it may not be a string */
-        mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1];
-        if (mCDE.mExtraField == NULL)
-            return NO_MEMORY;
-        memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
-            mCDE.mExtraFieldLength+1);
-    }
-
-    /* construct the LFH from the CDE */
-    copyCDEtoLFH();
-
-    /*
-     * The LFH "extra" field is independent of the CDE "extra", so we
-     * handle it here.
-     */
-    assert(mLFH.mExtraField == NULL);
-    mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
-    if (mLFH.mExtraFieldLength > 0) {
-        mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1];
-        if (mLFH.mExtraField == NULL)
-            return NO_MEMORY;
-        memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
-            mLFH.mExtraFieldLength+1);
-    }
-
-    return NO_ERROR;
-}
-
-/*
- * Insert pad bytes in the LFH by tweaking the "extra" field.  This will
- * potentially confuse something that put "extra" data in here earlier,
- * but I can't find an actual problem.
- */
-status_t ZipEntry::addPadding(int padding)
-{
-    if (padding <= 0)
-        return INVALID_OPERATION;
-
-    //LOGI("HEY: adding %d pad bytes to existing %d in %s\n",
-    //    padding, mLFH.mExtraFieldLength, mCDE.mFileName);
-
-    if (mLFH.mExtraFieldLength > 0) {
-        /* extend existing field */
-        unsigned char* newExtra;
-
-        newExtra = new unsigned char[mLFH.mExtraFieldLength + padding];
-        if (newExtra == NULL)
-            return NO_MEMORY;
-        memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
-        memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
-
-        delete[] mLFH.mExtraField;
-        mLFH.mExtraField = newExtra;
-        mLFH.mExtraFieldLength += padding;
-    } else {
-        /* create new field */
-        mLFH.mExtraField = new unsigned char[padding];
-        memset(mLFH.mExtraField, 0, padding);
-        mLFH.mExtraFieldLength = padding;
-    }
-
-    return NO_ERROR;
-}
-
-/*
- * Set the fields in the LFH equal to the corresponding fields in the CDE.
- *
- * This does not touch the LFH "extra" field.
- */
-void ZipEntry::copyCDEtoLFH(void)
-{
-    mLFH.mVersionToExtract  = mCDE.mVersionToExtract;
-    mLFH.mGPBitFlag         = mCDE.mGPBitFlag;
-    mLFH.mCompressionMethod = mCDE.mCompressionMethod;
-    mLFH.mLastModFileTime   = mCDE.mLastModFileTime;
-    mLFH.mLastModFileDate   = mCDE.mLastModFileDate;
-    mLFH.mCRC32             = mCDE.mCRC32;
-    mLFH.mCompressedSize    = mCDE.mCompressedSize;
-    mLFH.mUncompressedSize  = mCDE.mUncompressedSize;
-    mLFH.mFileNameLength    = mCDE.mFileNameLength;
-    // the "extra field" is independent
-
-    delete[] mLFH.mFileName;
-    if (mLFH.mFileNameLength > 0) {
-        mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1];
-        strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
-    } else {
-        mLFH.mFileName = NULL;
-    }
-}
-
-/*
- * Set some information about a file after we add it.
- */
-void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
-    int compressionMethod)
-{
-    mCDE.mCompressionMethod = compressionMethod;
-    mCDE.mCRC32 = crc32;
-    mCDE.mCompressedSize = compLen;
-    mCDE.mUncompressedSize = uncompLen;
-    mCDE.mCompressionMethod = compressionMethod;
-    if (compressionMethod == kCompressDeflated) {
-        mCDE.mGPBitFlag |= 0x0002;      // indicates maximum compression used
-    }
-    copyCDEtoLFH();
-}
-
-/*
- * See if the data in mCDE and mLFH match up.  This is mostly useful for
- * debugging these classes, but it can be used to identify damaged
- * archives.
- *
- * Returns "false" if they differ.
- */
-bool ZipEntry::compareHeaders(void) const
-{
-    if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
-        LOGV("cmp: VersionToExtract\n");
-        return false;
-    }
-    if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
-        LOGV("cmp: GPBitFlag\n");
-        return false;
-    }
-    if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
-        LOGV("cmp: CompressionMethod\n");
-        return false;
-    }
-    if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
-        LOGV("cmp: LastModFileTime\n");
-        return false;
-    }
-    if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
-        LOGV("cmp: LastModFileDate\n");
-        return false;
-    }
-    if (mCDE.mCRC32 != mLFH.mCRC32) {
-        LOGV("cmp: CRC32\n");
-        return false;
-    }
-    if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
-        LOGV("cmp: CompressedSize\n");
-        return false;
-    }
-    if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
-        LOGV("cmp: UncompressedSize\n");
-        return false;
-    }
-    if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
-        LOGV("cmp: FileNameLength\n");
-        return false;
-    }
-#if 0       // this seems to be used for padding, not real data
-    if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
-        LOGV("cmp: ExtraFieldLength\n");
-        return false;
-    }
-#endif
-    if (mCDE.mFileName != NULL) {
-        if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
-            LOGV("cmp: FileName\n");
-            return false;
-        }
-    }
-
-    return true;
-}
-
-
-/*
- * Convert the DOS date/time stamp into a UNIX time stamp.
- */
-time_t ZipEntry::getModWhen(void) const
-{
-    struct tm parts;
-
-    parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
-    parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
-    parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
-    parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
-    parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
-    parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
-    parts.tm_wday = parts.tm_yday = 0;
-    parts.tm_isdst = -1;        // DST info "not available"
-
-    return mktime(&parts);
-}
-
-/*
- * Set the CDE/LFH timestamp from UNIX time.
- */
-void ZipEntry::setModWhen(time_t when)
-{
-#ifdef HAVE_LOCALTIME_R
-    struct tm tmResult;
-#endif
-    time_t even;
-    unsigned short zdate, ztime;
-
-    struct tm* ptm;
-
-    /* round up to an even number of seconds */
-    even = (time_t)(((unsigned long)(when) + 1) & (~1));
-
-    /* expand */
-#ifdef HAVE_LOCALTIME_R
-    ptm = localtime_r(&even, &tmResult);
-#else
-    ptm = localtime(&even);
-#endif
-
-    int year;
-    year = ptm->tm_year;
-    if (year < 80)
-        year = 80;
-
-    zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
-    ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
-
-    mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
-    mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
-}
-
-
-/*
- * ===========================================================================
- *      ZipEntry::LocalFileHeader
- * ===========================================================================
- */
-
-/*
- * Read a local file header.
- *
- * On entry, "fp" points to the signature at the start of the header.
- * On exit, "fp" points to the start of data.
- */
-status_t ZipEntry::LocalFileHeader::read(FILE* fp)
-{
-    status_t result = NO_ERROR;
-    unsigned char buf[kLFHLen];
-
-    assert(mFileName == NULL);
-    assert(mExtraField == NULL);
-
-    if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
-        result = UNKNOWN_ERROR;
-        goto bail;
-    }
-
-    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
-        LOGD("whoops: didn't find expected signature\n");
-        result = UNKNOWN_ERROR;
-        goto bail;
-    }
-
-    mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
-    mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
-    mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
-    mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
-    mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
-    mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
-    mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
-    mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
-    mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
-    mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
-
-    // TODO: validate sizes
-
-    /* grab filename */
-    if (mFileNameLength != 0) {
-        mFileName = new unsigned char[mFileNameLength+1];
-        if (mFileName == NULL) {
-            result = NO_MEMORY;
-            goto bail;
-        }
-        if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
-            result = UNKNOWN_ERROR;
-            goto bail;
-        }
-        mFileName[mFileNameLength] = '\0';
-    }
-
-    /* grab extra field */
-    if (mExtraFieldLength != 0) {
-        mExtraField = new unsigned char[mExtraFieldLength+1];
-        if (mExtraField == NULL) {
-            result = NO_MEMORY;
-            goto bail;
-        }
-        if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
-            result = UNKNOWN_ERROR;
-            goto bail;
-        }
-        mExtraField[mExtraFieldLength] = '\0';
-    }
-
-bail:
-    return result;
-}
-
-/*
- * Write a local file header.
- */
-status_t ZipEntry::LocalFileHeader::write(FILE* fp)
-{
-    unsigned char buf[kLFHLen];
-
-    ZipEntry::putLongLE(&buf[0x00], kSignature);
-    ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
-    ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
-    ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
-    ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
-    ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
-    ZipEntry::putLongLE(&buf[0x0e], mCRC32);
-    ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
-    ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
-    ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
-    ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
-
-    if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
-        return UNKNOWN_ERROR;
-
-    /* write filename */
-    if (mFileNameLength != 0) {
-        if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
-            return UNKNOWN_ERROR;
-    }
-
-    /* write "extra field" */
-    if (mExtraFieldLength != 0) {
-        if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
-            return UNKNOWN_ERROR;
-    }
-
-    return NO_ERROR;
-}
-
-
-/*
- * Dump the contents of a LocalFileHeader object.
- */
-void ZipEntry::LocalFileHeader::dump(void) const
-{
-    LOGD(" LocalFileHeader contents:\n");
-    LOGD("  versToExt=%u gpBits=0x%04x compression=%u\n",
-        mVersionToExtract, mGPBitFlag, mCompressionMethod);
-    LOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
-        mLastModFileTime, mLastModFileDate, mCRC32);
-    LOGD("  compressedSize=%lu uncompressedSize=%lu\n",
-        mCompressedSize, mUncompressedSize);
-    LOGD("  filenameLen=%u extraLen=%u\n",
-        mFileNameLength, mExtraFieldLength);
-    if (mFileName != NULL)
-        LOGD("  filename: '%s'\n", mFileName);
-}
-
-
-/*
- * ===========================================================================
- *      ZipEntry::CentralDirEntry
- * ===========================================================================
- */
-
-/*
- * Read the central dir entry that appears next in the file.
- *
- * On entry, "fp" should be positioned on the signature bytes for the
- * entry.  On exit, "fp" will point at the signature word for the next
- * entry or for the EOCD.
- */
-status_t ZipEntry::CentralDirEntry::read(FILE* fp)
-{
-    status_t result = NO_ERROR;
-    unsigned char buf[kCDELen];
-
-    /* no re-use */
-    assert(mFileName == NULL);
-    assert(mExtraField == NULL);
-    assert(mFileComment == NULL);
-
-    if (fread(buf, 1, kCDELen, fp) != kCDELen) {
-        result = UNKNOWN_ERROR;
-        goto bail;
-    }
-
-    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
-        LOGD("Whoops: didn't find expected signature\n");
-        result = UNKNOWN_ERROR;
-        goto bail;
-    }
-
-    mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
-    mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
-    mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
-    mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
-    mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
-    mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
-    mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
-    mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
-    mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
-    mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
-    mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
-    mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
-    mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
-    mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
-    mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
-    mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
-
-    // TODO: validate sizes and offsets
-
-    /* grab filename */
-    if (mFileNameLength != 0) {
-        mFileName = new unsigned char[mFileNameLength+1];
-        if (mFileName == NULL) {
-            result = NO_MEMORY;
-            goto bail;
-        }
-        if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
-            result = UNKNOWN_ERROR;
-            goto bail;
-        }
-        mFileName[mFileNameLength] = '\0';
-    }
-
-    /* read "extra field" */
-    if (mExtraFieldLength != 0) {
-        mExtraField = new unsigned char[mExtraFieldLength+1];
-        if (mExtraField == NULL) {
-            result = NO_MEMORY;
-            goto bail;
-        }
-        if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
-            result = UNKNOWN_ERROR;
-            goto bail;
-        }
-        mExtraField[mExtraFieldLength] = '\0';
-    }
-
-
-    /* grab comment, if any */
-    if (mFileCommentLength != 0) {
-        mFileComment = new unsigned char[mFileCommentLength+1];
-        if (mFileComment == NULL) {
-            result = NO_MEMORY;
-            goto bail;
-        }
-        if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
-        {
-            result = UNKNOWN_ERROR;
-            goto bail;
-        }
-        mFileComment[mFileCommentLength] = '\0';
-    }
-
-bail:
-    return result;
-}
-
-/*
- * Write a central dir entry.
- */
-status_t ZipEntry::CentralDirEntry::write(FILE* fp)
-{
-    unsigned char buf[kCDELen];
-
-    ZipEntry::putLongLE(&buf[0x00], kSignature);
-    ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
-    ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
-    ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
-    ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
-    ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
-    ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
-    ZipEntry::putLongLE(&buf[0x10], mCRC32);
-    ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
-    ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
-    ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
-    ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
-    ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
-    ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
-    ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
-    ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
-    ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
-
-    if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
-        return UNKNOWN_ERROR;
-
-    /* write filename */
-    if (mFileNameLength != 0) {
-        if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
-            return UNKNOWN_ERROR;
-    }
-
-    /* write "extra field" */
-    if (mExtraFieldLength != 0) {
-        if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
-            return UNKNOWN_ERROR;
-    }
-
-    /* write comment */
-    if (mFileCommentLength != 0) {
-        if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
-            return UNKNOWN_ERROR;
-    }
-
-    return NO_ERROR;
-}
-
-/*
- * Dump the contents of a CentralDirEntry object.
- */
-void ZipEntry::CentralDirEntry::dump(void) const
-{
-    LOGD(" CentralDirEntry contents:\n");
-    LOGD("  versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
-        mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
-    LOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
-        mLastModFileTime, mLastModFileDate, mCRC32);
-    LOGD("  compressedSize=%lu uncompressedSize=%lu\n",
-        mCompressedSize, mUncompressedSize);
-    LOGD("  filenameLen=%u extraLen=%u commentLen=%u\n",
-        mFileNameLength, mExtraFieldLength, mFileCommentLength);
-    LOGD("  diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
-        mDiskNumberStart, mInternalAttrs, mExternalAttrs,
-        mLocalHeaderRelOffset);
-
-    if (mFileName != NULL)
-        LOGD("  filename: '%s'\n", mFileName);
-    if (mFileComment != NULL)
-        LOGD("  comment: '%s'\n", mFileComment);
-}
-
diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp
deleted file mode 100644
index 2132d22..0000000
--- a/libs/utils/ZipFile.cpp
+++ /dev/null
@@ -1,1296 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Access to Zip archives.
-//
-
-#define LOG_TAG "zip"
-
-#include "utils/ZipFile.h"
-#include "utils/ZipUtils.h"
-#include "utils/Log.h"
-
-#include <zlib.h>
-#define DEF_MEM_LEVEL 8                // normally in zutil.h?
-
-#include <memory.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <assert.h>
-
-using namespace android;
-
-/*
- * Some environments require the "b", some choke on it.
- */
-#define FILE_OPEN_RO        "rb"
-#define FILE_OPEN_RW        "r+b"
-#define FILE_OPEN_RW_CREATE "w+b"
-
-/* should live somewhere else? */
-static status_t errnoToStatus(int err)
-{
-    if (err == ENOENT)
-        return NAME_NOT_FOUND;
-    else if (err == EACCES)
-        return PERMISSION_DENIED;
-    else
-        return UNKNOWN_ERROR;
-}
-
-/*
- * Open a file and parse its guts.
- */
-status_t ZipFile::open(const char* zipFileName, int flags)
-{
-    bool newArchive = false;
-
-    assert(mZipFp == NULL);     // no reopen
-
-    if ((flags & kOpenTruncate))
-        flags |= kOpenCreate;           // trunc implies create
-
-    if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite))
-        return INVALID_OPERATION;       // not both
-    if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite)))
-        return INVALID_OPERATION;       // not neither
-    if ((flags & kOpenCreate) && !(flags & kOpenReadWrite))
-        return INVALID_OPERATION;       // create requires write
-
-    if (flags & kOpenTruncate) {
-        newArchive = true;
-    } else {
-        newArchive = (access(zipFileName, F_OK) != 0);
-        if (!(flags & kOpenCreate) && newArchive) {
-            /* not creating, must already exist */
-            LOGD("File %s does not exist", zipFileName);
-            return NAME_NOT_FOUND;
-        }
-    }
-
-    /* open the file */
-    const char* openflags;
-    if (flags & kOpenReadWrite) {
-        if (newArchive)
-            openflags = FILE_OPEN_RW_CREATE;
-        else
-            openflags = FILE_OPEN_RW;
-    } else {
-        openflags = FILE_OPEN_RO;
-    }
-    mZipFp = fopen(zipFileName, openflags);
-    if (mZipFp == NULL) {
-        int err = errno;
-        LOGD("fopen failed: %d\n", err);
-        return errnoToStatus(err);
-    }
-
-    status_t result;
-    if (!newArchive) {
-        /*
-         * Load the central directory.  If that fails, then this probably
-         * isn't a Zip archive.
-         */
-        result = readCentralDir();
-    } else {
-        /*
-         * Newly-created.  The EndOfCentralDir constructor actually
-         * sets everything to be the way we want it (all zeroes).  We
-         * set mNeedCDRewrite so that we create *something* if the
-         * caller doesn't add any files.  (We could also just unlink
-         * the file if it's brand new and nothing was added, but that's
-         * probably doing more than we really should -- the user might
-         * have a need for empty zip files.)
-         */
-        mNeedCDRewrite = true;
-        result = NO_ERROR;
-    }
-
-    if (flags & kOpenReadOnly)
-        mReadOnly = true;
-    else
-        assert(!mReadOnly);
-
-    return result;
-}
-
-/*
- * Return the Nth entry in the archive.
- */
-ZipEntry* ZipFile::getEntryByIndex(int idx) const
-{
-    if (idx < 0 || idx >= (int) mEntries.size())
-        return NULL;
-
-    return mEntries[idx];
-}
-
-/*
- * Find an entry by name.
- */
-ZipEntry* ZipFile::getEntryByName(const char* fileName) const
-{
-    /*
-     * Do a stupid linear string-compare search.
-     *
-     * There are various ways to speed this up, especially since it's rare
-     * to intermingle changes to the archive with "get by name" calls.  We
-     * don't want to sort the mEntries vector itself, however, because
-     * it's used to recreate the Central Directory.
-     *
-     * (Hash table works, parallel list of pointers in sorted order is good.)
-     */
-    int idx;
-
-    for (idx = mEntries.size()-1; idx >= 0; idx--) {
-        ZipEntry* pEntry = mEntries[idx];
-        if (!pEntry->getDeleted() &&
-            strcmp(fileName, pEntry->getFileName()) == 0)
-        {
-            return pEntry;
-        }
-    }
-
-    return NULL;
-}
-
-/*
- * Empty the mEntries vector.
- */
-void ZipFile::discardEntries(void)
-{
-    int count = mEntries.size();
-
-    while (--count >= 0)
-        delete mEntries[count];
-
-    mEntries.clear();
-}
-
-
-/*
- * Find the central directory and read the contents.
- *
- * The fun thing about ZIP archives is that they may or may not be
- * readable from start to end.  In some cases, notably for archives
- * that were written to stdout, the only length information is in the
- * central directory at the end of the file.
- *
- * Of course, the central directory can be followed by a variable-length
- * comment field, so we have to scan through it backwards.  The comment
- * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
- * itself, plus apparently sometimes people throw random junk on the end
- * just for the fun of it.
- *
- * This is all a little wobbly.  If the wrong value ends up in the EOCD
- * area, we're hosed.  This appears to be the way that everbody handles
- * it though, so we're in pretty good company if this fails.
- */
-status_t ZipFile::readCentralDir(void)
-{
-    status_t result = NO_ERROR;
-    unsigned char* buf = NULL;
-    off_t fileLength, seekStart;
-    long readAmount;
-    int i;
-
-    fseek(mZipFp, 0, SEEK_END);
-    fileLength = ftell(mZipFp);
-    rewind(mZipFp);
-
-    /* too small to be a ZIP archive? */
-    if (fileLength < EndOfCentralDir::kEOCDLen) {
-        LOGD("Length is %ld -- too small\n", (long)fileLength);
-        result = INVALID_OPERATION;
-        goto bail;
-    }
-
-    buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch];
-    if (buf == NULL) {
-        LOGD("Failure allocating %d bytes for EOCD search",
-             EndOfCentralDir::kMaxEOCDSearch);
-        result = NO_MEMORY;
-        goto bail;
-    }
-
-    if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {
-        seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;
-        readAmount = EndOfCentralDir::kMaxEOCDSearch;
-    } else {
-        seekStart = 0;
-        readAmount = (long) fileLength;
-    }
-    if (fseek(mZipFp, seekStart, SEEK_SET) != 0) {
-        LOGD("Failure seeking to end of zip at %ld", (long) seekStart);
-        result = UNKNOWN_ERROR;
-        goto bail;
-    }
-
-    /* read the last part of the file into the buffer */
-    if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) {
-        LOGD("short file? wanted %ld\n", readAmount);
-        result = UNKNOWN_ERROR;
-        goto bail;
-    }
-
-    /* find the end-of-central-dir magic */
-    for (i = readAmount - 4; i >= 0; i--) {
-        if (buf[i] == 0x50 &&
-            ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
-        {
-            LOGV("+++ Found EOCD at buf+%d\n", i);
-            break;
-        }
-    }
-    if (i < 0) {
-        LOGD("EOCD not found, not Zip\n");
-        result = INVALID_OPERATION;
-        goto bail;
-    }
-
-    /* extract eocd values */
-    result = mEOCD.readBuf(buf + i, readAmount - i);
-    if (result != NO_ERROR) {
-        LOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
-        goto bail;
-    }
-    //mEOCD.dump();
-
-    if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||
-        mEOCD.mNumEntries != mEOCD.mTotalNumEntries)
-    {
-        LOGD("Archive spanning not supported\n");
-        result = INVALID_OPERATION;
-        goto bail;
-    }
-
-    /*
-     * So far so good.  "mCentralDirSize" is the size in bytes of the
-     * central directory, so we can just seek back that far to find it.
-     * We can also seek forward mCentralDirOffset bytes from the
-     * start of the file.
-     *
-     * We're not guaranteed to have the rest of the central dir in the
-     * buffer, nor are we guaranteed that the central dir will have any
-     * sort of convenient size.  We need to skip to the start of it and
-     * read the header, then the other goodies.
-     *
-     * The only thing we really need right now is the file comment, which
-     * we're hoping to preserve.
-     */
-    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
-        LOGD("Failure seeking to central dir offset %ld\n",
-             mEOCD.mCentralDirOffset);
-        result = UNKNOWN_ERROR;
-        goto bail;
-    }
-
-    /*
-     * Loop through and read the central dir entries.
-     */
-    LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries);
-    int entry;
-    for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
-        ZipEntry* pEntry = new ZipEntry;
-
-        result = pEntry->initFromCDE(mZipFp);
-        if (result != NO_ERROR) {
-            LOGD("initFromCDE failed\n");
-            delete pEntry;
-            goto bail;
-        }
-
-        mEntries.add(pEntry);
-    }
-
-
-    /*
-     * If all went well, we should now be back at the EOCD.
-     */
-    {
-        unsigned char checkBuf[4];
-        if (fread(checkBuf, 1, 4, mZipFp) != 4) {
-            LOGD("EOCD check read failed\n");
-            result = INVALID_OPERATION;
-            goto bail;
-        }
-        if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
-            LOGD("EOCD read check failed\n");
-            result = UNKNOWN_ERROR;
-            goto bail;
-        }
-        LOGV("+++ EOCD read check passed\n");
-    }
-
-bail:
-    delete[] buf;
-    return result;
-}
-
-
-/*
- * Add a new file to the archive.
- *
- * This requires creating and populating a ZipEntry structure, and copying
- * the data into the file at the appropriate position.  The "appropriate
- * position" is the current location of the central directory, which we
- * casually overwrite (we can put it back later).
- *
- * If we were concerned about safety, we would want to make all changes
- * in a temp file and then overwrite the original after everything was
- * safely written.  Not really a concern for us.
- */
-status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
-    const char* storageName, int sourceType, int compressionMethod,
-    ZipEntry** ppEntry)
-{
-    ZipEntry* pEntry = NULL;
-    status_t result = NO_ERROR;
-    long lfhPosn, startPosn, endPosn, uncompressedLen;
-    FILE* inputFp = NULL;
-    unsigned long crc;
-    time_t modWhen;
-
-    if (mReadOnly)
-        return INVALID_OPERATION;
-
-    assert(compressionMethod == ZipEntry::kCompressDeflated ||
-           compressionMethod == ZipEntry::kCompressStored);
-
-    /* make sure we're in a reasonable state */
-    assert(mZipFp != NULL);
-    assert(mEntries.size() == mEOCD.mTotalNumEntries);
-
-    /* make sure it doesn't already exist */
-    if (getEntryByName(storageName) != NULL)
-        return ALREADY_EXISTS;
-
-    if (!data) {
-        inputFp = fopen(fileName, FILE_OPEN_RO);
-        if (inputFp == NULL)
-            return errnoToStatus(errno);
-    }
-
-    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
-        result = UNKNOWN_ERROR;
-        goto bail;
-    }
-
-    pEntry = new ZipEntry;
-    pEntry->initNew(storageName, NULL);
-
-    /*
-     * From here on out, failures are more interesting.
-     */
-    mNeedCDRewrite = true;
-
-    /*
-     * Write the LFH, even though it's still mostly blank.  We need it
-     * as a place-holder.  In theory the LFH isn't necessary, but in
-     * practice some utilities demand it.
-     */
-    lfhPosn = ftell(mZipFp);
-    pEntry->mLFH.write(mZipFp);
-    startPosn = ftell(mZipFp);
-
-    /*
-     * Copy the data in, possibly compressing it as we go.
-     */
-    if (sourceType == ZipEntry::kCompressStored) {
-        if (compressionMethod == ZipEntry::kCompressDeflated) {
-            bool failed = false;
-            result = compressFpToFp(mZipFp, inputFp, data, size, &crc);
-            if (result != NO_ERROR) {
-                LOGD("compression failed, storing\n");
-                failed = true;
-            } else {
-                /*
-                 * Make sure it has compressed "enough".  This probably ought
-                 * to be set through an API call, but I don't expect our
-                 * criteria to change over time.
-                 */
-                long src = inputFp ? ftell(inputFp) : size;
-                long dst = ftell(mZipFp) - startPosn;
-                if (dst + (dst / 10) > src) {
-                    LOGD("insufficient compression (src=%ld dst=%ld), storing\n",
-                        src, dst);
-                    failed = true;
-                }
-            }
-
-            if (failed) {
-                compressionMethod = ZipEntry::kCompressStored;
-                if (inputFp) rewind(inputFp);
-                fseek(mZipFp, startPosn, SEEK_SET);
-                /* fall through to kCompressStored case */
-            }
-        }
-        /* handle "no compression" request, or failed compression from above */
-        if (compressionMethod == ZipEntry::kCompressStored) {
-            if (inputFp) {
-                result = copyFpToFp(mZipFp, inputFp, &crc);
-            } else {
-                result = copyDataToFp(mZipFp, data, size, &crc);
-            }
-            if (result != NO_ERROR) {
-                // don't need to truncate; happens in CDE rewrite
-                LOGD("failed copying data in\n");
-                goto bail;
-            }
-        }
-
-        // currently seeked to end of file
-        uncompressedLen = inputFp ? ftell(inputFp) : size;
-    } else if (sourceType == ZipEntry::kCompressDeflated) {
-        /* we should support uncompressed-from-compressed, but it's not
-         * important right now */
-        assert(compressionMethod == ZipEntry::kCompressDeflated);
-
-        bool scanResult;
-        int method;
-        long compressedLen;
-
-        scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen,
-                        &compressedLen, &crc);
-        if (!scanResult || method != ZipEntry::kCompressDeflated) {
-            LOGD("this isn't a deflated gzip file?");
-            result = UNKNOWN_ERROR;
-            goto bail;
-        }
-
-        result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL);
-        if (result != NO_ERROR) {
-            LOGD("failed copying gzip data in\n");
-            goto bail;
-        }
-    } else {
-        assert(false);
-        result = UNKNOWN_ERROR;
-        goto bail;
-    }
-
-    /*
-     * We could write the "Data Descriptor", but there doesn't seem to
-     * be any point since we're going to go back and write the LFH.
-     *
-     * Update file offsets.
-     */
-    endPosn = ftell(mZipFp);            // seeked to end of compressed data
-
-    /*
-     * Success!  Fill out new values.
-     */
-    pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc,
-        compressionMethod);
-    modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp));
-    pEntry->setModWhen(modWhen);
-    pEntry->setLFHOffset(lfhPosn);
-    mEOCD.mNumEntries++;
-    mEOCD.mTotalNumEntries++;
-    mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()
-    mEOCD.mCentralDirOffset = endPosn;
-
-    /*
-     * Go back and write the LFH.
-     */
-    if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) {
-        result = UNKNOWN_ERROR;
-        goto bail;
-    }
-    pEntry->mLFH.write(mZipFp);
-
-    /*
-     * Add pEntry to the list.
-     */
-    mEntries.add(pEntry);
-    if (ppEntry != NULL)
-        *ppEntry = pEntry;
-    pEntry = NULL;
-
-bail:
-    if (inputFp != NULL)
-        fclose(inputFp);
-    delete pEntry;
-    return result;
-}
-
-/*
- * Add an entry by copying it from another zip file.  If "padding" is
- * nonzero, the specified number of bytes will be added to the "extra"
- * field in the header.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
-status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
-    int padding, ZipEntry** ppEntry)
-{
-    ZipEntry* pEntry = NULL;
-    status_t result;
-    long lfhPosn, endPosn;
-
-    if (mReadOnly)
-        return INVALID_OPERATION;
-
-    /* make sure we're in a reasonable state */
-    assert(mZipFp != NULL);
-    assert(mEntries.size() == mEOCD.mTotalNumEntries);
-
-    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
-        result = UNKNOWN_ERROR;
-        goto bail;
-    }
-
-    pEntry = new ZipEntry;
-    if (pEntry == NULL) {
-        result = NO_MEMORY;
-        goto bail;
-    }
-
-    result = pEntry->initFromExternal(pSourceZip, pSourceEntry);
-    if (result != NO_ERROR)
-        goto bail;
-    if (padding != 0) {
-        result = pEntry->addPadding(padding);
-        if (result != NO_ERROR)
-            goto bail;
-    }
-
-    /*
-     * From here on out, failures are more interesting.
-     */
-    mNeedCDRewrite = true;
-
-    /*
-     * Write the LFH.  Since we're not recompressing the data, we already
-     * have all of the fields filled out.
-     */
-    lfhPosn = ftell(mZipFp);
-    pEntry->mLFH.write(mZipFp);
-
-    /*
-     * Copy the data over.
-     *
-     * If the "has data descriptor" flag is set, we want to copy the DD
-     * fields as well.  This is a fixed-size area immediately following
-     * the data.
-     */
-    if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0)
-    {
-        result = UNKNOWN_ERROR;
-        goto bail;
-    }
-
-    off_t copyLen;
-    copyLen = pSourceEntry->getCompressedLen();
-    if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)
-        copyLen += ZipEntry::kDataDescriptorLen;
-
-    if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
-        != NO_ERROR)
-    {
-        LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
-        result = UNKNOWN_ERROR;
-        goto bail;
-    }
-
-    /*
-     * Update file offsets.
-     */
-    endPosn = ftell(mZipFp);
-
-    /*
-     * Success!  Fill out new values.
-     */
-    pEntry->setLFHOffset(lfhPosn);      // sets mCDE.mLocalHeaderRelOffset
-    mEOCD.mNumEntries++;
-    mEOCD.mTotalNumEntries++;
-    mEOCD.mCentralDirSize = 0;      // mark invalid; set by flush()
-    mEOCD.mCentralDirOffset = endPosn;
-
-    /*
-     * Add pEntry to the list.
-     */
-    mEntries.add(pEntry);
-    if (ppEntry != NULL)
-        *ppEntry = pEntry;
-    pEntry = NULL;
-
-    result = NO_ERROR;
-
-bail:
-    delete pEntry;
-    return result;
-}
-
-/*
- * Copy all of the bytes in "src" to "dst".
- *
- * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
- * will be seeked immediately past the data.
- */
-status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32)
-{
-    unsigned char tmpBuf[32768];
-    size_t count;
-
-    *pCRC32 = crc32(0L, Z_NULL, 0);
-
-    while (1) {
-        count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp);
-        if (ferror(srcFp) || ferror(dstFp))
-            return errnoToStatus(errno);
-        if (count == 0)
-            break;
-
-        *pCRC32 = crc32(*pCRC32, tmpBuf, count);
-
-        if (fwrite(tmpBuf, 1, count, dstFp) != count) {
-            LOGD("fwrite %d bytes failed\n", (int) count);
-            return UNKNOWN_ERROR;
-        }
-    }
-
-    return NO_ERROR;
-}
-
-/*
- * Copy all of the bytes in "src" to "dst".
- *
- * On exit, "dstFp" will be seeked immediately past the data.
- */
-status_t ZipFile::copyDataToFp(FILE* dstFp,
-    const void* data, size_t size, unsigned long* pCRC32)
-{
-    size_t count;
-
-    *pCRC32 = crc32(0L, Z_NULL, 0);
-    if (size > 0) {
-        *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);
-        if (fwrite(data, 1, size, dstFp) != size) {
-            LOGD("fwrite %d bytes failed\n", (int) size);
-            return UNKNOWN_ERROR;
-        }
-    }
-
-    return NO_ERROR;
-}
-
-/*
- * Copy some of the bytes in "src" to "dst".
- *
- * If "pCRC32" is NULL, the CRC will not be computed.
- *
- * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
- * will be seeked immediately past the data just written.
- */
-status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
-    unsigned long* pCRC32)
-{
-    unsigned char tmpBuf[32768];
-    size_t count;
-
-    if (pCRC32 != NULL)
-        *pCRC32 = crc32(0L, Z_NULL, 0);
-
-    while (length) {
-        long readSize;
-        
-        readSize = sizeof(tmpBuf);
-        if (readSize > length)
-            readSize = length;
-
-        count = fread(tmpBuf, 1, readSize, srcFp);
-        if ((long) count != readSize) {     // error or unexpected EOF
-            LOGD("fread %d bytes failed\n", (int) readSize);
-            return UNKNOWN_ERROR;
-        }
-
-        if (pCRC32 != NULL)
-            *pCRC32 = crc32(*pCRC32, tmpBuf, count);
-
-        if (fwrite(tmpBuf, 1, count, dstFp) != count) {
-            LOGD("fwrite %d bytes failed\n", (int) count);
-            return UNKNOWN_ERROR;
-        }
-
-        length -= readSize;
-    }
-
-    return NO_ERROR;
-}
-
-/*
- * Compress all of the data in "srcFp" and write it to "dstFp".
- *
- * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
- * will be seeked immediately past the compressed data.
- */
-status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
-    const void* data, size_t size, unsigned long* pCRC32)
-{
-    status_t result = NO_ERROR;
-    const size_t kBufSize = 32768;
-    unsigned char* inBuf = NULL;
-    unsigned char* outBuf = NULL;
-    z_stream zstream;
-    bool atEof = false;     // no feof() aviailable yet
-    unsigned long crc;
-    int zerr;
-
-    /*
-     * Create an input buffer and an output buffer.
-     */
-    inBuf = new unsigned char[kBufSize];
-    outBuf = new unsigned char[kBufSize];
-    if (inBuf == NULL || outBuf == NULL) {
-        result = NO_MEMORY;
-        goto bail;
-    }
-
-    /*
-     * 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 = outBuf;
-    zstream.avail_out = kBufSize;
-    zstream.data_type = Z_UNKNOWN;
-
-    zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION,
-        Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
-    if (zerr != Z_OK) {
-        result = UNKNOWN_ERROR;
-        if (zerr == Z_VERSION_ERROR) {
-            LOGE("Installed zlib is not compatible with linked version (%s)\n",
-                ZLIB_VERSION);
-        } else {
-            LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
-        }
-        goto bail;
-    }
-
-    crc = crc32(0L, Z_NULL, 0);
-
-    /*
-     * Loop while we have data.
-     */
-    do {
-        size_t getSize;
-        int flush;
-
-        /* only read if the input buffer is empty */
-        if (zstream.avail_in == 0 && !atEof) {
-            LOGV("+++ reading %d bytes\n", (int)kBufSize);
-            if (data) {
-                getSize = size > kBufSize ? kBufSize : size;
-                memcpy(inBuf, data, getSize);
-                data = ((const char*)data) + getSize;
-                size -= getSize;
-            } else {
-                getSize = fread(inBuf, 1, kBufSize, srcFp);
-                if (ferror(srcFp)) {
-                    LOGD("deflate read failed (errno=%d)\n", errno);
-                    goto z_bail;
-                }
-            }
-            if (getSize < kBufSize) {
-                LOGV("+++  got %d bytes, EOF reached\n",
-                    (int)getSize);
-                atEof = true;
-            }
-
-            crc = crc32(crc, inBuf, getSize);
-
-            zstream.next_in = inBuf;
-            zstream.avail_in = getSize;
-        }
-
-        if (atEof)
-            flush = Z_FINISH;       /* tell zlib that we're done */
-        else
-            flush = Z_NO_FLUSH;     /* more to come! */
-
-        zerr = deflate(&zstream, flush);
-        if (zerr != Z_OK && zerr != Z_STREAM_END) {
-            LOGD("zlib deflate call failed (zerr=%d)\n", zerr);
-            result = UNKNOWN_ERROR;
-            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 != (uInt) kBufSize))
-        {
-            LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
-            if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) !=
-                (size_t)(zstream.next_out - outBuf))
-            {
-                LOGD("write %d failed in deflate\n",
-                    (int) (zstream.next_out - outBuf));
-                goto z_bail;
-            }
-
-            zstream.next_out = outBuf;
-            zstream.avail_out = kBufSize;
-        }
-    } while (zerr == Z_OK);
-
-    assert(zerr == Z_STREAM_END);       /* other errors should've been caught */
-
-    *pCRC32 = crc;
-
-z_bail:
-    deflateEnd(&zstream);        /* free up any allocated structures */
-
-bail:
-    delete[] inBuf;
-    delete[] outBuf;
-
-    return result;
-}
-
-/*
- * Mark an entry as deleted.
- *
- * We will eventually need to crunch the file down, but if several files
- * are being removed (perhaps as part of an "update" process) we can make
- * things considerably faster by deferring the removal to "flush" time.
- */
-status_t ZipFile::remove(ZipEntry* pEntry)
-{
-    /*
-     * Should verify that pEntry is actually part of this archive, and
-     * not some stray ZipEntry from a different file.
-     */
-
-    /* mark entry as deleted, and mark archive as dirty */
-    pEntry->setDeleted();
-    mNeedCDRewrite = true;
-    return NO_ERROR;
-}
-
-/*
- * Flush any pending writes.
- *
- * In particular, this will crunch out deleted entries, and write the
- * Central Directory and EOCD if we have stomped on them.
- */
-status_t ZipFile::flush(void)
-{
-    status_t result = NO_ERROR;
-    long eocdPosn;
-    int i, count;
-
-    if (mReadOnly)
-        return INVALID_OPERATION;
-    if (!mNeedCDRewrite)
-        return NO_ERROR;
-
-    assert(mZipFp != NULL);
-
-    result = crunchArchive();
-    if (result != NO_ERROR)
-        return result;
-
-    if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0)
-        return UNKNOWN_ERROR;
-
-    count = mEntries.size();
-    for (i = 0; i < count; i++) {
-        ZipEntry* pEntry = mEntries[i];
-        pEntry->mCDE.write(mZipFp);
-    }
-
-    eocdPosn = ftell(mZipFp);
-    mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset;
-
-    mEOCD.write(mZipFp);
-
-    /*
-     * If we had some stuff bloat up during compression and get replaced
-     * with plain files, or if we deleted some entries, there's a lot
-     * of wasted space at the end of the file.  Remove it now.
-     */
-    if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) {
-        LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
-        // not fatal
-    }
-
-    /* should we clear the "newly added" flag in all entries now? */
-
-    mNeedCDRewrite = false;
-    return NO_ERROR;
-}
-
-/*
- * Crunch deleted files out of an archive by shifting the later files down.
- *
- * Because we're not using a temp file, we do the operation inside the
- * current file.
- */
-status_t ZipFile::crunchArchive(void)
-{
-    status_t result = NO_ERROR;
-    int i, count;
-    long delCount, adjust;
-
-#if 0
-    printf("CONTENTS:\n");
-    for (i = 0; i < (int) mEntries.size(); i++) {
-        printf(" %d: lfhOff=%ld del=%d\n",
-            i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted());
-    }
-    printf("  END is %ld\n", (long) mEOCD.mCentralDirOffset);
-#endif
-
-    /*
-     * Roll through the set of files, shifting them as appropriate.  We
-     * could probably get a slight performance improvement by sliding
-     * multiple files down at once (because we could use larger reads
-     * when operating on batches of small files), but it's not that useful.
-     */
-    count = mEntries.size();
-    delCount = adjust = 0;
-    for (i = 0; i < count; i++) {
-        ZipEntry* pEntry = mEntries[i];
-        long span;
-
-        if (pEntry->getLFHOffset() != 0) {
-            long nextOffset;
-
-            /* Get the length of this entry by finding the offset
-             * of the next entry.  Directory entries don't have
-             * file offsets, so we need to find the next non-directory
-             * entry.
-             */
-            nextOffset = 0;
-            for (int ii = i+1; nextOffset == 0 && ii < count; ii++)
-                nextOffset = mEntries[ii]->getLFHOffset();
-            if (nextOffset == 0)
-                nextOffset = mEOCD.mCentralDirOffset;
-            span = nextOffset - pEntry->getLFHOffset();
-
-            assert(span >= ZipEntry::LocalFileHeader::kLFHLen);
-        } else {
-            /* This is a directory entry.  It doesn't have
-             * any actual file contents, so there's no need to
-             * move anything.
-             */
-            span = 0;
-        }
-
-        //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n",
-        //    i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count);
-
-        if (pEntry->getDeleted()) {
-            adjust += span;
-            delCount++;
-
-            delete pEntry;
-            mEntries.removeAt(i);
-
-            /* adjust loop control */
-            count--;
-            i--;
-        } else if (span != 0 && adjust > 0) {
-            /* shuffle this entry back */
-            //printf("+++ Shuffling '%s' back %ld\n",
-            //    pEntry->getFileName(), adjust);
-            result = filemove(mZipFp, pEntry->getLFHOffset() - adjust,
-                        pEntry->getLFHOffset(), span);
-            if (result != NO_ERROR) {
-                /* this is why you use a temp file */
-                LOGE("error during crunch - archive is toast\n");
-                return result;
-            }
-
-            pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust);
-        }
-    }
-
-    /*
-     * Fix EOCD info.  We have to wait until the end to do some of this
-     * because we use mCentralDirOffset to determine "span" for the
-     * last entry.
-     */
-    mEOCD.mCentralDirOffset -= adjust;
-    mEOCD.mNumEntries -= delCount;
-    mEOCD.mTotalNumEntries -= delCount;
-    mEOCD.mCentralDirSize = 0;  // mark invalid; set by flush()
-
-    assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries);
-    assert(mEOCD.mNumEntries == count);
-
-    return result;
-}
-
-/*
- * Works like memmove(), but on pieces of a file.
- */
-status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n)
-{
-    if (dst == src || n <= 0)
-        return NO_ERROR;
-
-    unsigned char readBuf[32768];
-
-    if (dst < src) {
-        /* shift stuff toward start of file; must read from start */
-        while (n != 0) {
-            size_t getSize = sizeof(readBuf);
-            if (getSize > n)
-                getSize = n;
-
-            if (fseek(fp, (long) src, SEEK_SET) != 0) {
-                LOGD("filemove src seek %ld failed\n", (long) src);
-                return UNKNOWN_ERROR;
-            }
-
-            if (fread(readBuf, 1, getSize, fp) != getSize) {
-                LOGD("filemove read %ld off=%ld failed\n",
-                    (long) getSize, (long) src);
-                return UNKNOWN_ERROR;
-            }
-
-            if (fseek(fp, (long) dst, SEEK_SET) != 0) {
-                LOGD("filemove dst seek %ld failed\n", (long) dst);
-                return UNKNOWN_ERROR;
-            }
-
-            if (fwrite(readBuf, 1, getSize, fp) != getSize) {
-                LOGD("filemove write %ld off=%ld failed\n",
-                    (long) getSize, (long) dst);
-                return UNKNOWN_ERROR;
-            }
-
-            src += getSize;
-            dst += getSize;
-            n -= getSize;
-        }
-    } else {
-        /* shift stuff toward end of file; must read from end */
-        assert(false);      // write this someday, maybe
-        return UNKNOWN_ERROR;
-    }
-
-    return NO_ERROR;
-}
-
-
-/*
- * Get the modification time from a file descriptor.
- */
-time_t ZipFile::getModTime(int fd)
-{
-    struct stat sb;
-
-    if (fstat(fd, &sb) < 0) {
-        LOGD("HEY: fstat on fd %d failed\n", fd);
-        return (time_t) -1;
-    }
-
-    return sb.st_mtime;
-}
-
-
-#if 0       /* this is a bad idea */
-/*
- * Get a copy of the Zip file descriptor.
- *
- * We don't allow this if the file was opened read-write because we tend
- * to leave the file contents in an uncertain state between calls to
- * flush().  The duplicated file descriptor should only be valid for reads.
- */
-int ZipFile::getZipFd(void) const
-{
-    if (!mReadOnly)
-        return INVALID_OPERATION;
-    assert(mZipFp != NULL);
-
-    int fd;
-    fd = dup(fileno(mZipFp));
-    if (fd < 0) {
-        LOGD("didn't work, errno=%d\n", errno);
-    }
-
-    return fd;
-}
-#endif
-
-
-#if 0
-/*
- * Expand data.
- */
-bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const
-{
-    return false;
-}
-#endif
-
-// free the memory when you're done
-void* ZipFile::uncompress(const ZipEntry* entry)
-{
-    size_t unlen = entry->getUncompressedLen();
-    size_t clen = entry->getCompressedLen();
-
-    void* buf = malloc(unlen);
-    if (buf == NULL) {
-        return NULL;
-    }
-
-    fseek(mZipFp, 0, SEEK_SET);
-
-    off_t offset = entry->getFileOffset();
-    if (fseek(mZipFp, offset, SEEK_SET) != 0) {
-        goto bail;
-    }
-
-    switch (entry->getCompressionMethod())
-    {
-        case ZipEntry::kCompressStored: {
-            ssize_t amt = fread(buf, 1, unlen, mZipFp);
-            if (amt != (ssize_t)unlen) {
-                goto bail;
-            }
-#if 0
-            printf("data...\n");
-            const unsigned char* p = (unsigned char*)buf;
-            const unsigned char* end = p+unlen;
-            for (int i=0; i<32 && p < end; i++) {
-                printf("0x%08x ", (int)(offset+(i*0x10)));
-                for (int j=0; j<0x10 && p < end; j++) {
-                    printf(" %02x", *p);
-                    p++;
-                }
-                printf("\n");
-            }
-#endif
-
-            }
-            break;
-        case ZipEntry::kCompressDeflated: {
-            if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) {
-                goto bail;
-            }
-            }
-            break;
-        default:
-            goto bail;
-    }
-    return buf;
-
-bail:
-    free(buf);
-    return NULL;
-}
-
-
-/*
- * ===========================================================================
- *      ZipFile::EndOfCentralDir
- * ===========================================================================
- */
-
-/*
- * Read the end-of-central-dir fields.
- *
- * "buf" should be positioned at the EOCD signature, and should contain
- * the entire EOCD area including the comment.
- */
-status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len)
-{
-    /* don't allow re-use */
-    assert(mComment == NULL);
-
-    if (len < kEOCDLen) {
-        /* looks like ZIP file got truncated */
-        LOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
-            kEOCDLen, len);
-        return INVALID_OPERATION;
-    }
-
-    /* this should probably be an assert() */
-    if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
-        return UNKNOWN_ERROR;
-
-    mDiskNumber = ZipEntry::getShortLE(&buf[0x04]);
-    mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);
-    mNumEntries = ZipEntry::getShortLE(&buf[0x08]);
-    mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);
-    mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]);
-    mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);
-    mCommentLen = ZipEntry::getShortLE(&buf[0x14]);
-
-    // TODO: validate mCentralDirOffset
-
-    if (mCommentLen > 0) {
-        if (kEOCDLen + mCommentLen > len) {
-            LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n",
-                kEOCDLen, mCommentLen, len);
-            return UNKNOWN_ERROR;
-        }
-        mComment = new unsigned char[mCommentLen];
-        memcpy(mComment, buf + kEOCDLen, mCommentLen);
-    }
-
-    return NO_ERROR;
-}
-
-/*
- * Write an end-of-central-directory section.
- */
-status_t ZipFile::EndOfCentralDir::write(FILE* fp)
-{
-    unsigned char buf[kEOCDLen];
-
-    ZipEntry::putLongLE(&buf[0x00], kSignature);
-    ZipEntry::putShortLE(&buf[0x04], mDiskNumber);
-    ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir);
-    ZipEntry::putShortLE(&buf[0x08], mNumEntries);
-    ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries);
-    ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize);
-    ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset);
-    ZipEntry::putShortLE(&buf[0x14], mCommentLen);
-
-    if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen)
-        return UNKNOWN_ERROR;
-    if (mCommentLen > 0) {
-        assert(mComment != NULL);
-        if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen)
-            return UNKNOWN_ERROR;
-    }
-
-    return NO_ERROR;
-}
-
-/*
- * Dump the contents of an EndOfCentralDir object.
- */
-void ZipFile::EndOfCentralDir::dump(void) const
-{
-    LOGD(" EndOfCentralDir contents:\n");
-    LOGD("  diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n",
-        mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);
-    LOGD("  centDirSize=%lu centDirOff=%lu commentLen=%u\n",
-        mCentralDirSize, mCentralDirOffset, mCommentLen);
-}
-
diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp
index d312daf..45f6c8b 100644
--- a/libs/utils/ZipFileCRO.cpp
+++ b/libs/utils/ZipFileCRO.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include "utils/ZipFileCRO.h"
-#include "utils/ZipFileRO.h"
+#include <utils/ZipFileCRO.h>
+#include <utils/ZipFileRO.h>
 
 using namespace android;
 
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index ae8c719..6c701dd 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -19,9 +19,9 @@
 //
 #define LOG_TAG "zipro"
 //#define LOG_NDEBUG 0
-#include "utils/ZipFileRO.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
+#include <utils/ZipFileRO.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
 
 #include <zlib.h>
 
diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp
index bfbacfe..5df94cb 100644
--- a/libs/utils/ZipUtils.cpp
+++ b/libs/utils/ZipUtils.cpp
@@ -20,9 +20,9 @@
 
 #define LOG_TAG "ziputil"
 
-#include "utils/ZipUtils.h"
-#include "utils/ZipFileRO.h"
-#include "utils/Log.h"
+#include <utils/ZipUtils.h>
+#include <utils/ZipFileRO.h>
+#include <utils/Log.h>
 
 #include <stdlib.h>
 #include <string.h>
diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp
index 6d2cc91..cffe880 100644
--- a/opengl/libagl/array.cpp
+++ b/opengl/libagl/array.cpp
@@ -960,6 +960,8 @@
     v->index = first;
     first &= vertex_cache_t::INDEX_MASK;
     const GLubyte* vp = c->arrays.vertex.element(first);
+    v->obj.z = 0;
+    v->obj.w = 0x10000;
     c->arrays.vertex.fetch(c, v->obj.v, vp);
     c->arrays.mvp_transform(&c->transforms.mvp, &v->clip, &v->obj);
     c->arrays.perspective(c, v);
@@ -975,6 +977,8 @@
     do {
         v->flags = 0;
         v->index = first++;
+        v->obj.z = 0;
+        v->obj.w = 0x10000;
         c->arrays.vertex.fetch(c, v->obj.v, vp);
         c->arrays.mvp_transform(mvp, &v->clip, &v->obj);
         c->arrays.perspective(c, v);
diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp
index 25c41d0..8ae32cc 100644
--- a/opengl/libagl/light.cpp
+++ b/opengl/libagl/light.cpp
@@ -38,13 +38,14 @@
 static void lightVertexMaterial(ogles_context_t* c, vertex_t* v);
 
 static inline void vscale3(GLfixed* d, const GLfixed* m, GLfixed s);
-static inline void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b);
 
 static __attribute__((noinline))
 void vnorm3(GLfixed* d, const GLfixed* a);
 
 static inline void vsa3(GLfixed* d,
     const GLfixed* m, GLfixed s, const GLfixed* a);
+static inline void vss3(GLfixed* d,
+    const GLfixed* m, GLfixed s, const GLfixed* a);
 static inline void vmla3(GLfixed* d,
     const GLfixed* m0, const GLfixed* m1, const GLfixed* a);
 static inline void vmul3(GLfixed* d,
@@ -151,18 +152,10 @@
 }
 
 static inline
-void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b) {
-    const GLfixed wa = a[3];
-    const GLfixed wb = b[3];
-    if (ggl_likely(wa == wb)) {
-        d[0] = a[0] - b[0];
-        d[1] = a[1] - b[1];
-        d[2] = a[2] - b[2];
-    } else {
-        d[0] = gglMulSubx(a[0], wb, gglMulx(b[0], wa));
-        d[1] = gglMulSubx(a[1], wb, gglMulx(b[1], wa));
-        d[2] = gglMulSubx(a[2], wb, gglMulx(b[2], wa));
-    }
+void vss3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) {
+    d[0] = gglMulSubx(m[0], s, a[0]);
+    d[1] = gglMulSubx(m[1], s, a[1]);
+    d[2] = gglMulSubx(m[2], s, a[2]);
 }
 
 static inline
@@ -227,7 +220,7 @@
         const int i = 31 - gglClz(en);
         en &= ~(1<<i);
         light_t& l = c->lighting.lights[i];
-        c->transforms.mvui.point3(&c->transforms.mvui,
+        c->transforms.mvui.point4(&c->transforms.mvui,
                 &l.objPosition, &l.position);
         vnorm3(l.normalizedObjPosition.v, l.objPosition.v);
     }
@@ -318,6 +311,11 @@
         vmul3(l.implicitAmbient.v,  material.ambient.v,  l.ambient.v);
         vmul3(l.implicitDiffuse.v,  material.diffuse.v,  l.diffuse.v);
         vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v);
+        // this is just a flag to tell if we have a specular component
+        l.implicitSpecular.v[3] =
+                l.implicitSpecular.r |
+                l.implicitSpecular.g |
+                l.implicitSpecular.b;
     }
     // emission and ambient for the whole scene
     vmla3(  c->lighting.implicitSceneEmissionAndAmbient.v,
@@ -343,7 +341,11 @@
         vec4_t n;
         c->arrays.normal.fetch(c, n.v,
             c->arrays.normal.element(v->index & vertex_cache_t::INDEX_MASK));
-        if (c->transforms.rescaleNormals == GL_NORMALIZE)
+
+        // TODO: right now we handle GL_RESCALE_NORMALS as if ti were
+        // GL_NORMALIZE. We could optimize this by  scaling mvui 
+        // appropriately instead.
+        if (c->transforms.rescaleNormals)
             vnorm3(n.v, n.v);
 
         const material_t& material = c->lighting.front;
@@ -360,7 +362,8 @@
 
             // compute vertex-to-light vector
             if (ggl_unlikely(l.position.w)) {
-                vsub3w(d.v, l.objPosition.v, v->obj.v);
+                // lightPos/1.0 - vertex/vertex.w == lightPos*vertex.w - vertex
+                vss3(d.v, l.objPosition.v, v->obj.w, v->obj.v);
                 sqDist = dot3(d.v, d.v);
                 vscale3(d.v, d.v, gglSqrtRecipx(sqDist));
             } else {
diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp
index f175cda..0b68dc0 100644
--- a/opengl/libagl/matrix.cpp
+++ b/opengl/libagl/matrix.cpp
@@ -55,7 +55,7 @@
 static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o);
 static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o);
 static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o);
-static void normal__generic(transform_t const*, vec4_t* c, vec4_t const* o);
+static void point4__mvui(transform_t const*, vec4_t* c, vec4_t const* o);
 
 // ----------------------------------------------------------------------------
 #if 0
@@ -209,7 +209,8 @@
 {
     flags = 0;
     ops = OP_ALL;
-    point3 = normal__generic;
+    point3 = point4__mvui;
+    point4 = point4__mvui;
 }
 
 void transform_t::dump(const char* what)
@@ -596,66 +597,19 @@
 
 void transform_state_t::update_mvui()
 {
+    GLfloat r[16];
     const GLfloat* const mv = modelview.top().elements();
-
-    /*
-    When transforming normals, we can use the upper 3x3 matrix, see:
-    http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node26.html
-    */
     
-    // Also note that:
-    //      l(obj) =  tr(M).l(eye) for infinite light
-    //      l(obj) = inv(M).l(eye) for local light
-
-    const uint32_t ops = modelview.top_ops() & ~OP_TRANSLATE;
-    if (ggl_likely((!(ops & ~OP_ROTATE)) ||
-        (rescaleNormals && modelview.isRigidBody()))) {
-        // if the modelview matrix is a rigid body transformation
-        // (translation, rotation, uniform scaling), then we can bypass
-        // the inverse by transposing the matrix.
-        GLfloat rescale = 1.0f;
-        if (rescaleNormals == GL_RESCALE_NORMAL) {
-            if (!(ops & ~OP_UNIFORM_SCALE)) {
-                rescale = reciprocalf(mv[I(0,0)]);
-            } else {
-                rescale = rsqrtf(
-                        sqrf(mv[I(2,0)]) + sqrf(mv[I(2,1)]) + sqrf(mv[I(2,2)]));
-            }
-        }
-        GLfixed* const x = mvui.matrix.m;
-        for (int i=0 ; i<3 ; i++) {
-            x[I(i,0)] = gglFloatToFixed(mv[I(0,i)] * rescale);
-            x[I(i,1)] = gglFloatToFixed(mv[I(1,i)] * rescale);
-            x[I(i,2)] = gglFloatToFixed(mv[I(2,i)] * rescale);
-        }
-        mvui.picker();
-        return;
-    }
-
-    GLfloat r[3][3];
-    r[0][0] = det22(mv[I(1,1)], mv[I(2,1)], mv[I(1,2)], mv[I(2,2)]);
-    r[0][1] =ndet22(mv[I(0,1)], mv[I(2,1)], mv[I(0,2)], mv[I(2,2)]);
-    r[0][2] = det22(mv[I(0,1)], mv[I(1,1)], mv[I(0,2)], mv[I(1,2)]);
-    r[1][0] =ndet22(mv[I(1,0)], mv[I(2,0)], mv[I(1,2)], mv[I(2,2)]);
-    r[1][1] = det22(mv[I(0,0)], mv[I(2,0)], mv[I(0,2)], mv[I(2,2)]);
-    r[1][2] =ndet22(mv[I(0,0)], mv[I(1,0)], mv[I(0,2)], mv[I(1,2)]);
-    r[2][0] = det22(mv[I(1,0)], mv[I(2,0)], mv[I(1,1)], mv[I(2,1)]);
-    r[2][1] =ndet22(mv[I(0,0)], mv[I(2,0)], mv[I(0,1)], mv[I(2,1)]);
-    r[2][2] = det22(mv[I(0,0)], mv[I(1,0)], mv[I(0,1)], mv[I(1,1)]);        
-
-    GLfloat rdet;
-    if (rescaleNormals == GL_RESCALE_NORMAL) {
-        rdet = rsqrtf(sqrf(r[0][2]) + sqrf(r[1][2]) + sqrf(r[2][2]));
-    } else {
-        rdet = reciprocalf( 
-            r[0][0]*mv[I(0,0)] + r[0][1]*mv[I(1,0)] + r[0][2]*mv[I(2,0)]);
-    }
+    // TODO: we need a faster invert, especially for when the modelview
+    // is a rigid-body matrix
+    invert(r, mv);
 
     GLfixed* const x = mvui.matrix.m;
-    for (int i=0 ; i<3 ; i++) {
-        x[I(i,0)] = gglFloatToFixed(r[i][0] * rdet);
-        x[I(i,1)] = gglFloatToFixed(r[i][1] * rdet);
-        x[I(i,2)] = gglFloatToFixed(r[i][2] * rdet);
+    for (int i=0 ; i<4 ; i++) {
+        x[I(i,0)] = gglFloatToFixed(r[I(i,0)]);
+        x[I(i,1)] = gglFloatToFixed(r[I(i,1)]);
+        x[I(i,2)] = gglFloatToFixed(r[I(i,2)]);
+        x[I(i,4)] = gglFloatToFixed(r[I(i,3)]);
     }
     mvui.picker();
 }
@@ -783,14 +737,19 @@
     lhs->w = mla4(rx, m[ 3], ry, m[ 7], rz, m[11], rw, m[15]);
 }
 
-void normal__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+void point4__mvui(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+    // this used for transforming light positions back to object space.
+    // Lights have 3 components positions, so w is always 1.
+    // however, it is used as a switch for directional lights, so we need
+    // to preserve it.
     const GLfixed* const m = mx->matrix.m;
     const GLfixed rx = rhs->x;
     const GLfixed ry = rhs->y;
     const GLfixed rz = rhs->z;
-    lhs->x = mla3(rx, m[ 0], ry, m[ 4], rz, m[ 8]); 
-    lhs->y = mla3(rx, m[ 1], ry, m[ 5], rz, m[ 9]);
-    lhs->z = mla3(rx, m[ 2], ry, m[ 6], rz, m[10]);
+    lhs->x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); 
+    lhs->y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]);
+    lhs->z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]);
+    lhs->w = rhs->w;
 }
 
 
diff --git a/opengl/tests/lighting1709/Android.mk b/opengl/tests/lighting1709/Android.mk
new file mode 100644
index 0000000..9563e61
--- /dev/null
+++ b/opengl/tests/lighting1709/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := LightingTest
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/opengl/tests/lighting1709/AndroidManifest.xml b/opengl/tests/lighting1709/AndroidManifest.xml
new file mode 100644
index 0000000..6c23d42
--- /dev/null
+++ b/opengl/tests/lighting1709/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.lightingtest">
+
+    <application>
+        <activity android:name="ClearActivity" android:label="LightingTest">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java b/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java
new file mode 100644
index 0000000..3ae8c5c
--- /dev/null
+++ b/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.lightingtest;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.app.Activity;
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.MotionEvent;
+
+public class ClearActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mGLView = new ClearGLSurfaceView(this);
+        setContentView(mGLView);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mGLView.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mGLView.onResume();
+    }
+    private GLSurfaceView mGLView;
+}
+
+class ClearGLSurfaceView extends GLSurfaceView {
+    public ClearGLSurfaceView(Context context) {
+        super(context);
+        mRenderer = new ClearRenderer();
+        setRenderer(mRenderer);
+    }
+
+    ClearRenderer mRenderer;
+}
+
+class ClearRenderer implements GLSurfaceView.Renderer {
+    public ClearRenderer() {
+    }
+
+    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+        // Do nothing special.
+    }
+
+    public void onSurfaceChanged(GL10 gl, int w, int h) {
+        // Compute the projection matrix
+        gl.glMatrixMode(GL10.GL_PROJECTION);
+        gl.glLoadIdentity();
+
+        // Compute the boundaries of the frustum
+        float fl = (float) (-(w / 2)) / 288;
+        float fr = (float) (w / 2) / 288;
+        float ft = (float) (h / 2) / 288;
+        float fb = (float) (-(h / 2)) / 288;
+
+        // Set the view frustum
+        gl.glFrustumf(fl, fr, fb, ft, 1.0f, 2000.0f);
+
+        // Set the viewport dimensions
+        gl.glMatrixMode(GL10.GL_MODELVIEW);
+        gl.glLoadIdentity();
+        gl.glViewport(0, 0, w, h);
+    }
+
+    public void onDrawFrame(GL10 gl) {
+        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+
+        final float lightOff[]        = {0.0f, 0.0f,  0.0f, 1.0f};
+        final float lightAmbient[]    = {5.0f, 0.0f,  0.0f, 1.0f};
+        final float lightDiffuse[]    = {0.0f, 2.0f,  0.0f, 0.0f};
+        final float lightPosSpot[]    = {0.0f, 0.0f, -8.0f, 1.0f};
+
+        final float pos[] = {
+                    -5.0f, -1.5f, 0.0f,
+                     0.0f, -1.5f, 0.0f,
+                     5.0f, -1.5f, 0.0f,
+                };
+        
+        final float v[] = new float[9];
+        ByteBuffer vbb = ByteBuffer.allocateDirect(v.length*4);
+        vbb.order(ByteOrder.nativeOrder());
+        FloatBuffer vb = vbb.asFloatBuffer();
+
+        gl.glDisable(GL10.GL_DITHER);
+
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuse, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, lightOff, 0);
+        gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPosSpot, 0);
+        gl.glEnable(GL10.GL_LIGHT0);
+        
+        gl.glEnable(GL10.GL_LIGHTING);
+
+
+        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+        gl.glNormal3f(0, 0, 1);
+        
+
+        // draw first 3 triangles, without using transforms
+        for (int i=0 ; i<3 ; i++) {
+            v[0] = -1; v[1] =-1; v[2] = -10;
+            v[3] =  0; v[4] = 1; v[5] = -10;
+            v[6] =  1; v[7] =-1; v[8] = -10;
+            for (int j=0 ; j<3 ; j++) {
+                v[j*3+0] -= pos[i*3+0];
+                v[j*3+1] -= pos[i*3+1];
+                v[j*3+2] -= pos[i*3+2];
+            }
+            vb.put(v).position(0);
+            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
+            gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        }
+        
+        // draw the 2nd batch this time with transforms
+        v[0] = -1; v[1] =-1; v[2] = -10;
+        v[3] =  0; v[4] = 1; v[5] = -10;
+        v[6] =  1; v[7] =-1; v[8] = -10;
+        vb.put(v).position(0);
+        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
+
+        // draw lower left triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[0], pos[1], pos[2]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();
+
+        // draw lower middle triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[3], pos[4], pos[5]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();
+
+        // draw lower right triangle
+        gl.glPushMatrix();
+        gl.glTranslatef(pos[6], pos[7], pos[8]);
+        gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+        gl.glPopMatrix();      
+    }
+
+    public int[] getConfigSpec() {
+        int[] configSpec = { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
+        return configSpec;      
+    }
+}
+
diff --git a/tts/java/android/tts/SynthProxy.java b/tts/java/android/tts/SynthProxy.java
new file mode 100755
index 0000000..e065f40
--- /dev/null
+++ b/tts/java/android/tts/SynthProxy.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.tts;
+
+import android.util.Log;
+import java.lang.ref.WeakReference;
+
+/**
+ * @hide
+ *
+ * The SpeechSynthesis class provides a high-level api to create and play
+ * synthesized speech. This class is used internally to talk to a native
+ * TTS library that implements the interface defined in
+ * frameworks/base/include/tts/TtsEngine.h
+ *
+ */
+@SuppressWarnings("unused")
+public class SynthProxy {
+
+    //
+    // External API
+    //
+
+    /**
+     * Constructor; pass the location of the native TTS .so to use.
+     */
+    public SynthProxy(String nativeSoLib) {
+        Log.e("TTS is loading", nativeSoLib);
+        native_setup(new WeakReference<SynthProxy>(this), nativeSoLib);
+    }
+
+    /**
+     * Stops and clears the AudioTrack.
+     */
+    public void stop() {
+        native_stop(mJniData);
+    }
+
+    /**
+     * Synthesize speech and speak it directly using AudioTrack.
+     */
+    public void speak(String text) {
+        native_speak(mJniData, text);
+    }
+
+    /**
+     * Synthesize speech to a file. The current implementation writes a valid
+     * WAV file to the given path, assuming it is writable. Something like
+     * "/sdcard/???.wav" is recommended.
+     */
+    public void synthesizeToFile(String text, String filename) {
+        native_synthesizeToFile(mJniData, text, filename);
+    }
+
+    // TODO add IPA methods
+
+    /**
+     * Sets the language
+     */
+    public void setLanguage(String language) {
+        native_setLanguage(mJniData, language);
+    }
+
+    /**
+     * Sets the speech rate
+     */
+    public final void setSpeechRate(int speechRate) {
+        native_setSpeechRate(mJniData, speechRate);
+    }
+
+
+    /**
+     * Plays the given audio buffer
+     */
+    public void playAudioBuffer(int bufferPointer, int bufferSize) {
+        native_playAudioBuffer(mJniData, bufferPointer, bufferSize);
+    }
+
+    /**
+     * Gets the currently set language
+     */
+    public String getLanguage() {
+        return native_getLanguage(mJniData);
+    }
+
+    /**
+     * Gets the currently set rate
+     */
+    public int getRate() {
+        return native_getRate(mJniData);
+    }
+
+    /**
+     * Shuts down the native synthesizer
+     */
+    public void shutdown()  {
+        native_shutdown(mJniData);
+    }
+
+    //
+    // Internal
+    //
+
+    protected void finalize() {
+        native_finalize(mJniData);
+        mJniData = 0;
+    }
+
+    static {
+        System.loadLibrary("ttssynthproxy");
+    }
+
+    private final static String TAG = "SynthProxy";
+
+    /**
+     * Accessed by native methods
+     */
+    private int mJniData = 0;
+
+    private native final void native_setup(Object weak_this,
+            String nativeSoLib);
+
+    private native final void native_finalize(int jniData);
+
+    private native final void native_stop(int jniData);
+
+    private native final void native_speak(int jniData, String text);
+
+    private native final void native_synthesizeToFile(int jniData, String text, String filename);
+
+    private native final void native_setLanguage(int jniData, String language);
+
+    private native final void native_setSpeechRate(int jniData, int speechRate);
+
+    // TODO add buffer format
+    private native final void native_playAudioBuffer(int jniData, int bufferPointer, int bufferSize);
+
+    private native final String native_getLanguage(int jniData);
+
+    private native final int native_getRate(int jniData);
+
+    private native final void native_shutdown(int jniData);
+
+
+    /**
+     * Callback from the C layer
+     */
+    @SuppressWarnings("unused")
+    private static void postNativeSpeechSynthesizedInJava(Object tts_ref,
+            int bufferPointer, int bufferSize) {
+
+        Log.i("TTS plugin debug", "bufferPointer: " + bufferPointer
+                + " bufferSize: " + bufferSize);
+
+        SynthProxy nativeTTS = (SynthProxy)((WeakReference)tts_ref).get();
+        // TODO notify TTS service of synthesis/playback completion,
+        //      method definition to be changed.
+    }
+}
diff --git a/tts/java/android/tts/TtsService.java b/tts/java/android/tts/TtsService.java
new file mode 100755
index 0000000..d317181
--- /dev/null
+++ b/tts/java/android/tts/TtsService.java
@@ -0,0 +1,783 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.tts;
+
+import android.tts.ITts.Stub;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * @hide Synthesizes speech from text. This is implemented as a service so that
+ *       other applications can call the TTS without needing to bundle the TTS
+ *       in the build.
+ *
+ */
+public class TtsService extends Service implements OnCompletionListener {
+
+    private static class SpeechItem {
+        public static final int SPEECH = 0;
+        public static final int EARCON = 1;
+        public static final int SILENCE = 2;
+        public String mText = null;
+        public ArrayList<String> mParams = null;
+        public int mType = SPEECH;
+        public long mDuration = 0;
+
+        public SpeechItem(String text, ArrayList<String> params, int itemType) {
+            mText = text;
+            mParams = params;
+            mType = itemType;
+        }
+
+        public SpeechItem(long silenceTime) {
+            mDuration = silenceTime;
+        }
+    }
+
+    /**
+     * Contains the information needed to access a sound resource; the name of
+     * the package that contains the resource and the resID of the resource
+     * within that package.
+     */
+    private static class SoundResource {
+        public String mSourcePackageName = null;
+        public int mResId = -1;
+        public String mFilename = null;
+
+        public SoundResource(String packageName, int id) {
+            mSourcePackageName = packageName;
+            mResId = id;
+            mFilename = null;
+        }
+
+        public SoundResource(String file) {
+            mSourcePackageName = null;
+            mResId = -1;
+            mFilename = file;
+        }
+    }
+
+    private static final String ACTION = "android.intent.action.USE_TTS";
+    private static final String CATEGORY = "android.intent.category.TTS";
+    private static final String PKGNAME = "android.tts";
+
+    final RemoteCallbackList<ITtsCallback> mCallbacks = new RemoteCallbackList<ITtsCallback>();
+
+    private Boolean mIsSpeaking;
+    private ArrayList<SpeechItem> mSpeechQueue;
+    private HashMap<String, SoundResource> mEarcons;
+    private HashMap<String, SoundResource> mUtterances;
+    private MediaPlayer mPlayer;
+    private TtsService mSelf;
+
+    private SharedPreferences prefs;
+
+    private final ReentrantLock speechQueueLock = new ReentrantLock();
+    private final ReentrantLock synthesizerLock = new ReentrantLock();
+
+    // TODO support multiple SpeechSynthesis objects
+    private SynthProxy nativeSynth;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i("TTS", "TTS starting");
+
+
+        // TODO: Make this work when the settings are done in the main Settings
+        // app.
+        prefs = PreferenceManager.getDefaultSharedPreferences(this);
+
+        // TODO: This should be changed to work by requesting the path
+        // from the default engine.
+        nativeSynth = new SynthProxy(prefs.getString("engine_pref", ""));
+
+
+        mSelf = this;
+        mIsSpeaking = false;
+
+        mEarcons = new HashMap<String, SoundResource>();
+        mUtterances = new HashMap<String, SoundResource>();
+
+        mSpeechQueue = new ArrayList<SpeechItem>();
+        mPlayer = null;
+
+        setLanguage(prefs.getString("lang_pref", "en-rUS"));
+        setSpeechRate(Integer.parseInt(prefs.getString("rate_pref", "140")));
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        // Don't hog the media player
+        cleanUpPlayer();
+
+        nativeSynth.shutdown();
+
+        // Unregister all callbacks.
+        mCallbacks.kill();
+    }
+
+    private void setSpeechRate(int rate) {
+        if (prefs.getBoolean("override_pref", false)) {
+            // This is set to the default here so that the preview in the prefs
+            // activity will show the change without a restart, even if apps are
+            // not allowed to change the defaults.
+            rate = Integer.parseInt(prefs.getString("rate_pref", "140"));
+        }
+        nativeSynth.setSpeechRate(rate);
+    }
+
+    private void setLanguage(String lang) {
+        if (prefs.getBoolean("override_pref", false)) {
+            // This is set to the default here so that the preview in the prefs
+            // activity will show the change without a restart, even if apps are
+            // not
+            // allowed to change the defaults.
+            lang = prefs.getString("lang_pref", "en-rUS");
+        }
+        nativeSynth.setLanguage(lang);
+    }
+
+    private void setEngine(String engineName, String[] requestedLanguages,
+            int strictness) {
+        // TODO: Implement engine selection code here.
+        Intent engineIntent = new Intent(
+        "android.intent.action.START_TTS_ENGINE");
+        if (engineName != null) {
+            engineIntent.addCategory("android.intent.action.tts_engine."
+                    + engineName);
+        }
+        for (int i = 0; i < requestedLanguages.length; i++) {
+            engineIntent.addCategory("android.intent.action.tts_lang."
+                    + requestedLanguages[i]);
+        }
+        ResolveInfo[] enginesArray = new ResolveInfo[0];
+        PackageManager pm = getPackageManager();
+        enginesArray = pm.queryIntentActivities(engineIntent, 0).toArray(
+                enginesArray);
+    }
+
+    private void setEngine(Intent engineIntent) {
+        // TODO: Implement engine selection code here.
+    }
+
+    private int getEngineStatus() {
+        // TODO: Proposal - add a sanity check method that
+        // TTS engine plugins must implement.
+        return 0;
+    }
+
+    /**
+     * Adds a sound resource to the TTS.
+     *
+     * @param text
+     *            The text that should be associated with the sound resource
+     * @param packageName
+     *            The name of the package which has the sound resource
+     * @param resId
+     *            The resource ID of the sound within its package
+     */
+    private void addSpeech(String text, String packageName, int resId) {
+        mUtterances.put(text, new SoundResource(packageName, resId));
+    }
+
+    /**
+     * Adds a sound resource to the TTS.
+     *
+     * @param text
+     *            The text that should be associated with the sound resource
+     * @param filename
+     *            The filename of the sound resource. This must be a complete
+     *            path like: (/sdcard/mysounds/mysoundbite.mp3).
+     */
+    private void addSpeech(String text, String filename) {
+        mUtterances.put(text, new SoundResource(filename));
+    }
+
+    /**
+     * Adds a sound resource to the TTS as an earcon.
+     *
+     * @param earcon
+     *            The text that should be associated with the sound resource
+     * @param packageName
+     *            The name of the package which has the sound resource
+     * @param resId
+     *            The resource ID of the sound within its package
+     */
+    private void addEarcon(String earcon, String packageName, int resId) {
+        mEarcons.put(earcon, new SoundResource(packageName, resId));
+    }
+
+    /**
+     * Adds a sound resource to the TTS as an earcon.
+     *
+     * @param earcon
+     *            The text that should be associated with the sound resource
+     * @param filename
+     *            The filename of the sound resource. This must be a complete
+     *            path like: (/sdcard/mysounds/mysoundbite.mp3).
+     */
+    private void addEarcon(String earcon, String filename) {
+        mEarcons.put(earcon, new SoundResource(filename));
+    }
+
+    /**
+     * Speaks the given text using the specified queueing mode and parameters.
+     *
+     * @param text
+     *            The text that should be spoken
+     * @param queueMode
+     *            0 for no queue (interrupts all previous utterances), 1 for
+     *            queued
+     * @param params
+     *            An ArrayList of parameters. This is not implemented for all
+     *            engines.
+     */
+    private void speak(String text, int queueMode, ArrayList<String> params) {
+        if (queueMode == 0) {
+            stop();
+        }
+        mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.SPEECH));
+        if (!mIsSpeaking) {
+            processSpeechQueue();
+        }
+    }
+
+    /**
+     * Plays the earcon using the specified queueing mode and parameters.
+     *
+     * @param earcon
+     *            The earcon that should be played
+     * @param queueMode
+     *            0 for no queue (interrupts all previous utterances), 1 for
+     *            queued
+     * @param params
+     *            An ArrayList of parameters. This is not implemented for all
+     *            engines.
+     */
+    private void playEarcon(String earcon, int queueMode,
+            ArrayList<String> params) {
+        if (queueMode == 0) {
+            stop();
+        }
+        mSpeechQueue.add(new SpeechItem(earcon, params, SpeechItem.EARCON));
+        if (!mIsSpeaking) {
+            processSpeechQueue();
+        }
+    }
+
+    /**
+     * Stops all speech output and removes any utterances still in the queue.
+     */
+    private void stop() {
+        Log.i("TTS", "Stopping");
+        mSpeechQueue.clear();
+
+        nativeSynth.stop();
+        mIsSpeaking = false;
+        if (mPlayer != null) {
+            try {
+                mPlayer.stop();
+            } catch (IllegalStateException e) {
+                // Do nothing, the player is already stopped.
+            }
+        }
+        Log.i("TTS", "Stopped");
+    }
+
+    public void onCompletion(MediaPlayer arg0) {
+        processSpeechQueue();
+    }
+
+    private void playSilence(long duration, int queueMode,
+            ArrayList<String> params) {
+        if (queueMode == 0) {
+            stop();
+        }
+        mSpeechQueue.add(new SpeechItem(duration));
+        if (!mIsSpeaking) {
+            processSpeechQueue();
+        }
+    }
+
+    private void silence(final long duration) {
+        class SilenceThread implements Runnable {
+            public void run() {
+                try {
+                    Thread.sleep(duration);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                } finally {
+                    processSpeechQueue();
+                }
+            }
+        }
+        Thread slnc = (new Thread(new SilenceThread()));
+        slnc.setPriority(Thread.MIN_PRIORITY);
+        slnc.start();
+    }
+
+    private void speakInternalOnly(final String text,
+            final ArrayList<String> params) {
+        class SynthThread implements Runnable {
+            public void run() {
+                boolean synthAvailable = false;
+                try {
+                    synthAvailable = synthesizerLock.tryLock();
+                    if (!synthAvailable) {
+                        Thread.sleep(100);
+                        Thread synth = (new Thread(new SynthThread()));
+                        synth.setPriority(Thread.MIN_PRIORITY);
+                        synth.start();
+                        return;
+                    }
+                    nativeSynth.speak(text);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                } finally {
+                    // This check is needed because finally will always run;
+                    // even if the
+                    // method returns somewhere in the try block.
+                    if (synthAvailable) {
+                        synthesizerLock.unlock();
+                    }
+                }
+            }
+        }
+        Thread synth = (new Thread(new SynthThread()));
+        synth.setPriority(Thread.MIN_PRIORITY);
+        synth.start();
+    }
+
+    private SoundResource getSoundResource(SpeechItem speechItem) {
+        SoundResource sr = null;
+        String text = speechItem.mText;
+        if (speechItem.mType == SpeechItem.SILENCE) {
+            // Do nothing if this is just silence
+        } else if (speechItem.mType == SpeechItem.EARCON) {
+            sr = mEarcons.get(text);
+        } else {
+            sr = mUtterances.get(text);
+        }
+        return sr;
+    }
+
+    private void dispatchSpeechCompletedCallbacks(String mark) {
+        Log.i("TTS callback", "dispatch started");
+        // Broadcast to all clients the new value.
+        final int N = mCallbacks.beginBroadcast();
+        for (int i = 0; i < N; i++) {
+            try {
+                mCallbacks.getBroadcastItem(i).markReached(mark);
+            } catch (RemoteException e) {
+                // The RemoteCallbackList will take care of removing
+                // the dead object for us.
+            }
+        }
+        mCallbacks.finishBroadcast();
+        Log.i("TTS callback", "dispatch completed to " + N);
+    }
+
+    private void processSpeechQueue() {
+        boolean speechQueueAvailable = false;
+        try {
+            speechQueueAvailable = speechQueueLock.tryLock();
+            if (!speechQueueAvailable) {
+                return;
+            }
+            if (mSpeechQueue.size() < 1) {
+                mIsSpeaking = false;
+                // Dispatch a completion here as this is the
+                // only place where speech completes normally.
+                // Nothing left to say in the queue is a special case
+                // that is always a "mark" - associated text is null.
+                dispatchSpeechCompletedCallbacks("");
+                return;
+            }
+
+            SpeechItem currentSpeechItem = mSpeechQueue.get(0);
+            mIsSpeaking = true;
+            SoundResource sr = getSoundResource(currentSpeechItem);
+            // Synth speech as needed - synthesizer should call
+            // processSpeechQueue to continue running the queue
+            Log.i("TTS processing: ", currentSpeechItem.mText);
+            if (sr == null) {
+                if (currentSpeechItem.mType == SpeechItem.SPEECH) {
+                    // TODO: Split text up into smaller chunks before accepting
+                    // them
+                    // for processing.
+                    speakInternalOnly(currentSpeechItem.mText,
+                            currentSpeechItem.mParams);
+                } else {
+                    // This is either silence or an earcon that was missing
+                    silence(currentSpeechItem.mDuration);
+                }
+            } else {
+                cleanUpPlayer();
+                if (sr.mSourcePackageName == PKGNAME) {
+                    // Utterance is part of the TTS library
+                    mPlayer = MediaPlayer.create(this, sr.mResId);
+                } else if (sr.mSourcePackageName != null) {
+                    // Utterance is part of the app calling the library
+                    Context ctx;
+                    try {
+                        ctx = this.createPackageContext(sr.mSourcePackageName,
+                                0);
+                    } catch (NameNotFoundException e) {
+                        e.printStackTrace();
+                        mSpeechQueue.remove(0); // Remove it from the queue and
+                        // move on
+                        mIsSpeaking = false;
+                        return;
+                    }
+                    mPlayer = MediaPlayer.create(ctx, sr.mResId);
+                } else {
+                    // Utterance is coming from a file
+                    mPlayer = MediaPlayer.create(this, Uri.parse(sr.mFilename));
+                }
+
+                // Check if Media Server is dead; if it is, clear the queue and
+                // give up for now - hopefully, it will recover itself.
+                if (mPlayer == null) {
+                    mSpeechQueue.clear();
+                    mIsSpeaking = false;
+                    return;
+                }
+                mPlayer.setOnCompletionListener(this);
+                try {
+                    mPlayer.start();
+                } catch (IllegalStateException e) {
+                    mSpeechQueue.clear();
+                    mIsSpeaking = false;
+                    cleanUpPlayer();
+                    return;
+                }
+            }
+            if (mSpeechQueue.size() > 0) {
+                mSpeechQueue.remove(0);
+            }
+        } finally {
+            // This check is needed because finally will always run; even if the
+            // method returns somewhere in the try block.
+            if (speechQueueAvailable) {
+                speechQueueLock.unlock();
+            }
+        }
+    }
+
+    private void cleanUpPlayer() {
+        if (mPlayer != null) {
+            mPlayer.release();
+            mPlayer = null;
+        }
+    }
+
+    /**
+     * Synthesizes the given text using the specified queuing mode and
+     * parameters.
+     *
+     * @param text
+     *            The String of text that should be synthesized
+     * @param params
+     *            An ArrayList of parameters. The first element of this array
+     *            controls the type of voice to use.
+     * @param filename
+     *            The string that gives the full output filename; it should be
+     *            something like "/sdcard/myappsounds/mysound.wav".
+     * @return A boolean that indicates if the synthesis succeeded
+     */
+    private boolean synthesizeToFile(String text, ArrayList<String> params,
+            String filename, boolean calledFromApi) {
+        // Only stop everything if this is a call made by an outside app trying
+        // to
+        // use the API. Do NOT stop if this is a call from within the service as
+        // clearing the speech queue here would be a mistake.
+        if (calledFromApi) {
+            stop();
+        }
+        Log.i("TTS", "Synthesizing to " + filename);
+        boolean synthAvailable = false;
+        try {
+            synthAvailable = synthesizerLock.tryLock();
+            if (!synthAvailable) {
+                return false;
+            }
+            // Don't allow a filename that is too long
+            // TODO use platform constant
+            if (filename.length() > 250) {
+                return false;
+            }
+            nativeSynth.synthesizeToFile(text, filename);
+        } finally {
+            // This check is needed because finally will always run; even if the
+            // method returns somewhere in the try block.
+            if (synthAvailable) {
+                synthesizerLock.unlock();
+            }
+        }
+        Log.i("TTS", "Completed synthesis for " + filename);
+        return true;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (ACTION.equals(intent.getAction())) {
+            for (String category : intent.getCategories()) {
+                if (category.equals(CATEGORY)) {
+                    return mBinder;
+                }
+            }
+        }
+        return null;
+    }
+
+    private final ITts.Stub mBinder = new Stub() {
+
+        public void registerCallback(ITtsCallback cb) {
+            if (cb != null)
+                mCallbacks.register(cb);
+        }
+
+        public void unregisterCallback(ITtsCallback cb) {
+            if (cb != null)
+                mCallbacks.unregister(cb);
+        }
+
+        /**
+         * Gives a hint about the type of engine that is preferred.
+         *
+         * @param selectedEngine
+         *            The TTS engine that should be used
+         */
+        public void setEngine(String engineName, String[] supportedLanguages,
+                int strictness) {
+            mSelf.setEngine(engineName, supportedLanguages, strictness);
+        }
+
+        /**
+         * Specifies exactly what the engine has to support. Will always be
+         * considered "strict"; can be used for implementing
+         * optional/experimental features that are not supported by all engines.
+         *
+         * @param engineIntent
+         *            An intent that specifies exactly what the engine has to
+         *            support.
+         */
+        public void setEngineWithIntent(Intent engineIntent) {
+            mSelf.setEngine(engineIntent);
+        }
+
+        /**
+         * Speaks the given text using the specified queueing mode and
+         * parameters.
+         *
+         * @param text
+         *            The text that should be spoken
+         * @param queueMode
+         *            0 for no queue (interrupts all previous utterances), 1 for
+         *            queued
+         * @param params
+         *            An ArrayList of parameters. The first element of this
+         *            array controls the type of voice to use.
+         */
+        public void speak(String text, int queueMode, String[] params) {
+            ArrayList<String> speakingParams = new ArrayList<String>();
+            if (params != null) {
+                speakingParams = new ArrayList<String>(Arrays.asList(params));
+            }
+            mSelf.speak(text, queueMode, speakingParams);
+        }
+
+        /**
+         * Plays the earcon using the specified queueing mode and parameters.
+         *
+         * @param earcon
+         *            The earcon that should be played
+         * @param queueMode
+         *            0 for no queue (interrupts all previous utterances), 1 for
+         *            queued
+         * @param params
+         *            An ArrayList of parameters.
+         */
+        public void playEarcon(String earcon, int queueMode, String[] params) {
+            ArrayList<String> speakingParams = new ArrayList<String>();
+            if (params != null) {
+                speakingParams = new ArrayList<String>(Arrays.asList(params));
+            }
+            mSelf.playEarcon(earcon, queueMode, speakingParams);
+        }
+
+        /**
+         * Plays the silence using the specified queueing mode and parameters.
+         *
+         * @param duration
+         *            The duration of the silence that should be played
+         * @param queueMode
+         *            0 for no queue (interrupts all previous utterances), 1 for
+         *            queued
+         * @param params
+         *            An ArrayList of parameters.
+         */
+        public void playSilence(long duration, int queueMode, String[] params) {
+            ArrayList<String> speakingParams = new ArrayList<String>();
+            if (params != null) {
+                speakingParams = new ArrayList<String>(Arrays.asList(params));
+            }
+            mSelf.playSilence(duration, queueMode, speakingParams);
+        }
+
+
+        /**
+         * Stops all speech output and removes any utterances still in the
+         * queue.
+         */
+        public void stop() {
+            mSelf.stop();
+        }
+
+        /**
+         * Returns whether or not the TTS is speaking.
+         *
+         * @return Boolean to indicate whether or not the TTS is speaking
+         */
+        public boolean isSpeaking() {
+            return (mSelf.mIsSpeaking && (mSpeechQueue.size() < 1));
+        }
+
+        /**
+         * Adds a sound resource to the TTS.
+         *
+         * @param text
+         *            The text that should be associated with the sound resource
+         * @param packageName
+         *            The name of the package which has the sound resource
+         * @param resId
+         *            The resource ID of the sound within its package
+         */
+        public void addSpeech(String text, String packageName, int resId) {
+            mSelf.addSpeech(text, packageName, resId);
+        }
+
+        /**
+         * Adds a sound resource to the TTS.
+         *
+         * @param text
+         *            The text that should be associated with the sound resource
+         * @param filename
+         *            The filename of the sound resource. This must be a
+         *            complete path like: (/sdcard/mysounds/mysoundbite.mp3).
+         */
+        public void addSpeechFile(String text, String filename) {
+            mSelf.addSpeech(text, filename);
+        }
+
+        /**
+         * Adds a sound resource to the TTS as an earcon.
+         *
+         * @param earcon
+         *            The text that should be associated with the sound resource
+         * @param packageName
+         *            The name of the package which has the sound resource
+         * @param resId
+         *            The resource ID of the sound within its package
+         */
+        public void addEarcon(String earcon, String packageName, int resId) {
+            mSelf.addEarcon(earcon, packageName, resId);
+        }
+
+        /**
+         * Adds a sound resource to the TTS as an earcon.
+         *
+         * @param earcon
+         *            The text that should be associated with the sound resource
+         * @param filename
+         *            The filename of the sound resource. This must be a
+         *            complete path like: (/sdcard/mysounds/mysoundbite.mp3).
+         */
+        public void addEarconFile(String earcon, String filename) {
+            mSelf.addEarcon(earcon, filename);
+        }
+
+        /**
+         * Sets the speech rate for the TTS. Note that this will only have an
+         * effect on synthesized speech; it will not affect pre-recorded speech.
+         *
+         * @param speechRate
+         *            The speech rate that should be used
+         */
+        public void setSpeechRate(int speechRate) {
+            mSelf.setSpeechRate(speechRate);
+        }
+
+        // TODO: Fix comment about language
+        /**
+         * Sets the speech rate for the TTS. Note that this will only have an
+         * effect on synthesized speech; it will not affect pre-recorded speech.
+         *
+         * @param language
+         *            The language to be used. The languages are specified by
+         *            their IETF language tags as defined by BCP 47. This is the
+         *            same standard used for the lang attribute in HTML. See:
+         *            http://en.wikipedia.org/wiki/IETF_language_tag
+         */
+        public void setLanguage(String language) {
+            mSelf.setLanguage(language);
+        }
+
+        /**
+         * Speaks the given text using the specified queueing mode and
+         * parameters.
+         *
+         * @param text
+         *            The String of text that should be synthesized
+         * @param params
+         *            An ArrayList of parameters. The first element of this
+         *            array controls the type of voice to use.
+         * @param filename
+         *            The string that gives the full output filename; it should
+         *            be something like "/sdcard/myappsounds/mysound.wav".
+         * @return A boolean that indicates if the synthesis succeeded
+         */
+        public boolean synthesizeToFile(String text, String[] params,
+                String filename) {
+            ArrayList<String> speakingParams = new ArrayList<String>();
+            if (params != null) {
+                speakingParams = new ArrayList<String>(Arrays.asList(params));
+            }
+            return mSelf.synthesizeToFile(text, speakingParams, filename, true);
+        }
+    };
+
+}
diff --git a/tts/jni/Android.mk b/tts/jni/Android.mk
new file mode 100755
index 0000000..665d6d2
--- /dev/null
+++ b/tts/jni/Android.mk
@@ -0,0 +1,31 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	android_tts_SynthProxy.cpp
+
+LOCAL_C_INCLUDES += \
+	$(JNI_H_INCLUDE)
+
+LOCAL_SHARED_LIBRARIES := \
+	libandroid_runtime \
+	libnativehelper \
+	libmedia \
+	libutils \
+	libcutils
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+
+LOCAL_MODULE:= libttssynthproxy
+
+LOCAL_ARM_MODE := arm
+
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/tts/jni/android_tts_SynthProxy.cpp b/tts/jni/android_tts_SynthProxy.cpp
new file mode 100755
index 0000000..d8f1bf3
--- /dev/null
+++ b/tts/jni/android_tts_SynthProxy.cpp
@@ -0,0 +1,595 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <stdio.h>
+#include <unistd.h>
+
+#define LOG_TAG "SynthProxy"
+
+#include <utils/Log.h>
+#include <nativehelper/jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <tts/TtsEngine.h>
+#include <media/AudioTrack.h>
+
+#include <dlfcn.h>
+
+#define DEFAULT_TTS_RATE        16000
+#define DEFAULT_TTS_FORMAT      AudioSystem::PCM_16_BIT
+#define DEFAULT_TTS_NB_CHANNELS 1
+
+#define USAGEMODE_PLAY_IMMEDIATELY 0
+#define USAGEMODE_WRITE_TO_FILE    1
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+struct fields_t {
+    jfieldID    synthProxyFieldJniData;
+    jclass      synthProxyClass;
+    jmethodID   synthProxyMethodPost;
+};
+
+struct afterSynthData_t {
+    jint jniStorage;
+    int  usageMode;
+    FILE* outputFile;
+};
+
+// ----------------------------------------------------------------------------
+static fields_t javaTTSFields;
+
+// ----------------------------------------------------------------------------
+class SynthProxyJniStorage {
+    public :
+        //jclass                    tts_class;
+        jobject                   tts_ref;
+        TtsEngine*                mNativeSynthInterface;
+        AudioTrack*               mAudioOut;
+        uint32_t                  mSampleRate;
+        AudioSystem::audio_format mAudFormat;
+        int                       mNbChannels;
+
+        SynthProxyJniStorage() {
+            //tts_class = NULL;
+            tts_ref = NULL;
+            mNativeSynthInterface = NULL;
+            mAudioOut = NULL;
+            mSampleRate = DEFAULT_TTS_RATE;
+            mAudFormat  = DEFAULT_TTS_FORMAT;
+            mNbChannels = DEFAULT_TTS_NB_CHANNELS;
+        }
+
+        ~SynthProxyJniStorage() {
+            killAudio();
+            if (mNativeSynthInterface) {
+                mNativeSynthInterface->shutdown();
+                mNativeSynthInterface = NULL;
+            }
+        }
+
+        void killAudio() {
+            if (mAudioOut) {
+                mAudioOut->stop();
+                delete mAudioOut;
+                mAudioOut = NULL;
+            }
+        }
+
+        void createAudioOut(uint32_t rate, AudioSystem::audio_format format,
+                int channel) {
+            mSampleRate = rate;
+            mAudFormat  = format;
+            mNbChannels = channel;
+
+            // TODO use the TTS stream type
+            int streamType = AudioSystem::MUSIC;
+
+            // retrieve system properties to ensure successful creation of the
+            // AudioTrack object for playback
+            int afSampleRate;
+            if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
+                afSampleRate = 44100;
+            }
+            int afFrameCount;
+            if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
+                afFrameCount = 2048;
+            }
+            uint32_t afLatency;
+            if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) {
+                afLatency = 500;
+            }
+            uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
+            if (minBufCount < 2) minBufCount = 2;
+            int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate;
+
+            mAudioOut = new AudioTrack(streamType, rate, format, channel,
+                    minFrameCount > 4096 ? minFrameCount : 4096,
+                    0, 0, 0, 0); // not using an AudioTrack callback
+
+            if (mAudioOut->initCheck() != NO_ERROR) {
+              LOGI("AudioTrack error");
+              delete mAudioOut;
+              mAudioOut = NULL;
+            } else {
+              LOGI("AudioTrack OK");
+              mAudioOut->start();
+              LOGI("AudioTrack started");
+            }
+        }
+};
+
+
+// ----------------------------------------------------------------------------
+void prepAudioTrack(SynthProxyJniStorage* pJniData,
+        uint32_t rate, AudioSystem::audio_format format, int channel)
+{
+    // Don't bother creating a new audiotrack object if the current
+    // object is already set.
+    if ( pJniData->mAudioOut &&
+         (rate == pJniData->mSampleRate) &&
+         (format == pJniData->mAudFormat) &&
+         (channel == pJniData->mNbChannels) ){
+        return;
+    }
+    if (pJniData->mAudioOut){
+        pJniData->killAudio();
+    }
+    pJniData->createAudioOut(rate, format, channel);
+}
+
+
+// ----------------------------------------------------------------------------
+/*
+ * Callback from TTS engine.
+ * Directly speaks using AudioTrack or write to file
+ */
+static void ttsSynthDoneCB(void * userdata, uint32_t rate,
+                           AudioSystem::audio_format format, int channel,
+                           int8_t *wav, size_t bufferSize) {
+    LOGI("ttsSynthDoneCallback: %d bytes", bufferSize);
+
+    afterSynthData_t* pForAfter = (afterSynthData_t*)userdata;
+
+    if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY){
+        LOGI("Direct speech");
+
+        if (wav == NULL) {
+            LOGI("Null: speech has completed");
+        }
+
+        if (bufferSize > 0) {
+            SynthProxyJniStorage* pJniData =
+                    (SynthProxyJniStorage*)(pForAfter->jniStorage);
+            prepAudioTrack(pJniData, rate, format, channel);
+            if (pJniData->mAudioOut) {
+                pJniData->mAudioOut->write(wav, bufferSize);
+                LOGI("AudioTrack wrote: %d bytes", bufferSize);
+            } else {
+                LOGI("Can't play, null audiotrack");
+            }
+        }
+    } else  if (pForAfter->usageMode == USAGEMODE_WRITE_TO_FILE) {
+        LOGI("Save to file");
+        if (wav == NULL) {
+            LOGI("Null: speech has completed");
+        }
+        if (bufferSize > 0){
+            fwrite(wav, 1, bufferSize, pForAfter->outputFile);
+        }
+    }
+    // TODO update to call back into the SynthProxy class through the
+    //      javaTTSFields.synthProxyMethodPost methode to notify
+    //      playback has completed
+
+    delete pForAfter;
+    return;
+}
+
+
+// ----------------------------------------------------------------------------
+static void
+android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
+        jobject weak_this, jstring nativeSoLib)
+{
+    SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage();
+
+    prepAudioTrack(pJniStorage,
+            DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
+
+    const char *nativeSoLibNativeString =
+            env->GetStringUTFChars(nativeSoLib, 0);
+
+    void *engine_lib_handle = dlopen(nativeSoLibNativeString,
+            RTLD_NOW | RTLD_LOCAL);
+    if (engine_lib_handle==NULL) {
+       LOGI("engine_lib_handle==NULL");
+       // TODO report error so the TTS can't be used
+    } else {
+        TtsEngine *(*get_TtsEngine)() =
+            reinterpret_cast<TtsEngine* (*)()>(dlsym(engine_lib_handle, "getTtsEngine"));
+        pJniStorage->mNativeSynthInterface = (*get_TtsEngine)();
+        if (pJniStorage->mNativeSynthInterface) {
+            pJniStorage->mNativeSynthInterface->init(ttsSynthDoneCB);
+        }
+    }
+
+    // we use a weak reference so the SynthProxy object can be garbage collected.
+    pJniStorage->tts_ref = env->NewGlobalRef(weak_this);
+
+    // save the JNI resources so we can use them (and free them) later
+    env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData,
+            (int)pJniStorage);
+
+    env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString);
+}
+
+
+static void
+android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData) {
+        SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+        delete pSynthData;
+    }
+}
+
+
+static void
+android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
+        jstring language)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_setLanguage(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    const char *langNativeString = env->GetStringUTFChars(language, 0);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->setLanguage(langNativeString,
+                strlen(langNativeString));
+    }
+    env->ReleaseStringUTFChars(language, langNativeString);
+}
+
+
+static void
+android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData,
+        int speechRate)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_setSpeechRate(): invalid JNI data");
+        return;
+    }
+
+    int bufSize = 10;
+    char buffer [bufSize];
+    sprintf(buffer, "%d", speechRate);
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    LOGI("setting speech rate to %d", speechRate);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->setProperty("rate", buffer, bufSize);
+    }
+}
+
+
+// TODO: Refactor this to get rid of any assumptions about sample rate, etc.
+static void
+android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
+        jstring textJavaString, jstring filenameJavaString)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+
+    const char *filenameNativeString =
+            env->GetStringUTFChars(filenameJavaString, 0);
+    const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
+
+    afterSynthData_t* pForAfter = new (afterSynthData_t);
+    pForAfter->jniStorage = jniData;
+    pForAfter->usageMode  = USAGEMODE_WRITE_TO_FILE;
+
+    pForAfter->outputFile = fopen(filenameNativeString, "wb");
+
+    // Write 44 blank bytes for WAV header, then come back and fill them in
+    // after we've written the audio data
+    char header[44];
+    fwrite(header, 1, 44, pForAfter->outputFile);
+
+    unsigned int unique_identifier;
+
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->synthesizeText(textNativeString,
+                (void *)pForAfter);
+    }
+
+    long filelen = ftell(pForAfter->outputFile);
+
+    int samples = (((int)filelen) - 44) / 2;
+    header[0] = 'R';
+    header[1] = 'I';
+    header[2] = 'F';
+    header[3] = 'F';
+    ((uint32_t *)(&header[4]))[0] = filelen - 8;
+    header[8] = 'W';
+    header[9] = 'A';
+    header[10] = 'V';
+    header[11] = 'E';
+
+    header[12] = 'f';
+    header[13] = 'm';
+    header[14] = 't';
+    header[15] = ' ';
+
+    ((uint32_t *)(&header[16]))[0] = 16;  // size of fmt
+
+    ((unsigned short *)(&header[20]))[0] = 1;  // format
+    ((unsigned short *)(&header[22]))[0] = 1;  // channels
+    ((uint32_t *)(&header[24]))[0] = 22050;  // samplerate
+    ((uint32_t *)(&header[28]))[0] = 44100;  // byterate
+    ((unsigned short *)(&header[32]))[0] = 2;  // block align
+    ((unsigned short *)(&header[34]))[0] = 16;  // bits per sample
+
+    header[36] = 'd';
+    header[37] = 'a';
+    header[38] = 't';
+    header[39] = 'a';
+
+    ((uint32_t *)(&header[40]))[0] = samples * 2;  // size of data
+
+    // Skip back to the beginning and rewrite the header
+    fseek(pForAfter->outputFile, 0, SEEK_SET);
+    fwrite(header, 1, 44, pForAfter->outputFile);
+
+    fflush(pForAfter->outputFile);
+    fclose(pForAfter->outputFile);
+
+    env->ReleaseStringUTFChars(textJavaString, textNativeString);
+    env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString);
+}
+
+
+static void
+android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
+        jstring textJavaString)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_speak(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+
+    if (pSynthData->mAudioOut) {
+        pSynthData->mAudioOut->stop();
+        pSynthData->mAudioOut->start();
+    }
+
+    afterSynthData_t* pForAfter = new (afterSynthData_t);
+    pForAfter->jniStorage = jniData;
+    pForAfter->usageMode  = USAGEMODE_PLAY_IMMEDIATELY;
+
+    if (pSynthData->mNativeSynthInterface) {
+        const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
+        pSynthData->mNativeSynthInterface->synthesizeText(textNativeString,
+                (void *)pForAfter);
+        env->ReleaseStringUTFChars(textJavaString, textNativeString);
+    }
+}
+
+
+static void
+android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->stop();
+    }
+    if (pSynthData->mAudioOut) {
+        pSynthData->mAudioOut->stop();
+    }
+}
+
+
+static void
+android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_shutdown(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->shutdown();
+        pSynthData->mNativeSynthInterface = NULL;
+    }
+}
+
+
+// TODO add buffer format
+static void
+android_tts_SynthProxy_playAudioBuffer(JNIEnv *env, jobject thiz, jint jniData,
+        int bufferPointer, int bufferSize)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_playAudioBuffer(): invalid JNI data");
+        return;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    short* wav = (short*) bufferPointer;
+    pSynthData->mAudioOut->write(wav, bufferSize);
+    LOGI("AudioTrack wrote: %d bytes", bufferSize);
+}
+
+
+JNIEXPORT jstring JNICALL
+android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_getLanguage(): invalid JNI data");
+        return env->NewStringUTF("");
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    size_t bufSize = 100;
+    char buf[bufSize];
+    memset(buf, 0, bufSize);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->getLanguage(buf, &bufSize);
+    }
+    return env->NewStringUTF(buf);
+}
+
+JNIEXPORT int JNICALL
+android_tts_SynthProxy_getRate(JNIEnv *env, jobject thiz, jint jniData)
+{
+    if (jniData == 0) {
+        LOGE("android_tts_SynthProxy_getRate(): invalid JNI data");
+        return 0;
+    }
+
+    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
+    size_t bufSize = 100;
+
+    char buf[bufSize];
+    memset(buf, 0, bufSize);
+    // TODO check return codes
+    if (pSynthData->mNativeSynthInterface) {
+        pSynthData->mNativeSynthInterface->getProperty("rate", buf, &bufSize);
+    }
+    return atoi(buf);
+}
+
+// Dalvik VM type signatures
+static JNINativeMethod gMethods[] = {
+    {   "native_stop",
+        "(I)V",
+        (void*)android_tts_SynthProxy_stop
+    },
+    {   "native_speak",
+        "(ILjava/lang/String;)V",
+        (void*)android_tts_SynthProxy_speak
+    },
+    {   "native_synthesizeToFile",
+        "(ILjava/lang/String;Ljava/lang/String;)V",
+        (void*)android_tts_SynthProxy_synthesizeToFile
+    },
+    {   "native_setLanguage",
+        "(ILjava/lang/String;)V",
+        (void*)android_tts_SynthProxy_setLanguage
+    },
+    {   "native_setSpeechRate",
+        "(II)V",
+        (void*)android_tts_SynthProxy_setSpeechRate
+    },
+    {   "native_playAudioBuffer",
+        "(III)V",
+        (void*)android_tts_SynthProxy_playAudioBuffer
+    },
+    {   "native_getLanguage",
+        "(I)Ljava/lang/String;",
+        (void*)android_tts_SynthProxy_getLanguage
+    },
+    {   "native_getRate",
+        "(I)I",
+        (void*)android_tts_SynthProxy_getRate
+    },
+    {   "native_shutdown",
+        "(I)V",
+        (void*)android_tts_SynthProxy_shutdown
+    },
+    {   "native_setup",
+        "(Ljava/lang/Object;Ljava/lang/String;)V",
+        (void*)android_tts_SynthProxy_native_setup
+    },
+    {   "native_finalize",
+        "(I)V",
+        (void*)android_tts_SynthProxy_native_finalize
+    }
+};
+
+#define SP_JNIDATA_FIELD_NAME                "mJniData"
+#define SP_POSTSPEECHSYNTHESIZED_METHOD_NAME "postNativeSpeechSynthesizedInJava"
+
+// TODO: verify this is the correct path
+static const char* const kClassPathName = "android/tts/SynthProxy";
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+    JNIEnv* env = NULL;
+    jint result = -1;
+    jclass clazz;
+
+    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        LOGE("ERROR: GetEnv failed\n");
+        goto bail;
+    }
+    assert(env != NULL);
+
+    clazz = env->FindClass(kClassPathName);
+    if (clazz == NULL) {
+        LOGE("Can't find %s", kClassPathName);
+        goto bail;
+    }
+
+    javaTTSFields.synthProxyClass = clazz;
+    javaTTSFields.synthProxyFieldJniData = NULL;
+    javaTTSFields.synthProxyMethodPost = NULL;
+
+    javaTTSFields.synthProxyFieldJniData = env->GetFieldID(clazz,
+            SP_JNIDATA_FIELD_NAME, "I");
+    if (javaTTSFields.synthProxyFieldJniData == NULL) {
+        LOGE("Can't find %s.%s field", kClassPathName, SP_JNIDATA_FIELD_NAME);
+        goto bail;
+    }
+
+    javaTTSFields.synthProxyMethodPost = env->GetStaticMethodID(clazz,
+            SP_POSTSPEECHSYNTHESIZED_METHOD_NAME, "(Ljava/lang/Object;II)V");
+    if (javaTTSFields.synthProxyMethodPost == NULL) {
+        LOGE("Can't find %s.%s method", kClassPathName, SP_POSTSPEECHSYNTHESIZED_METHOD_NAME);
+        goto bail;
+    }
+
+    if (jniRegisterNativeMethods(
+            env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
+        goto bail;
+
+    /* success -- return valid version number */
+    result = JNI_VERSION_1_4;
+
+ bail:
+    return result;
+}