Add the backup data file writer C++ class.
diff --git a/libs/utils/backup_data.cpp b/libs/utils/backup_data.cpp
index c7f1fdb..dd04449 100644
--- a/libs/utils/backup_data.cpp
+++ b/libs/utils/backup_data.cpp
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "backup_data"
+
 #include <utils/backup_helpers.h>
 #include <utils/ByteOrder.h>
 
 #include <stdio.h>
 #include <unistd.h>
 
+#include <cutils/log.h>
+
 namespace android {
 
 /*
@@ -39,22 +43,6 @@
 #define ENTITY_MAGIC_V1 0x61746144 // Data (little endian)
 #define FOOTER_MAGIC_V1 0x746f6f46 // Foot (little endian)
 
-typedef struct {
-    int type; // == APP_MAGIC_V1
-    int packageLen; // length of the name of the package that follows, not including the null.
-} app_header_v1;
-
-typedef struct {
-    int type; // ENTITY_MAGIC_V1
-    int keyLen; // length of the key name, not including the null terminator
-    int dataSize; // size of the data, not including the padding
-} entity_header_v1;
-
-typedef struct {
-    int type; // FOOTER_MAGIC_V1
-    int entityCount; // the number of entities that were written
-} app_footer_v1;
-
 const static int ROUND_UP[4] = { 0, 3, 2, 1 };
 
 static inline size_t
@@ -102,7 +90,7 @@
 }
 
 status_t
-BackupDataWriter::WriteAppHeader(const String8& packageName)
+BackupDataWriter::WriteAppHeader(const String8& packageName, int cookie)
 {
     if (m_status != NO_ERROR) {
         return m_status;
@@ -122,6 +110,7 @@
 
     header.type = tolel(APP_MAGIC_V1);
     header.packageLen = tolel(nameLen);
+    header.cookie = cookie;
 
     amt = write(m_fd, &header, sizeof(app_header_v1));
     if (amt != sizeof(app_header_v1)) {
@@ -204,7 +193,7 @@
 }
 
 status_t
-BackupDataWriter::WriteAppFooter()
+BackupDataWriter::WriteAppFooter(int cookie)
 {
     if (m_status != NO_ERROR) {
         return m_status;
@@ -222,6 +211,7 @@
 
     footer.type = tolel(FOOTER_MAGIC_V1);
     footer.entityCount = tolel(m_entityCount);
+    footer.cookie = cookie;
 
     amt = write(m_fd, &footer, sizeof(app_footer_v1));
     if (amt != sizeof(app_footer_v1)) {
@@ -233,4 +223,206 @@
     return NO_ERROR;
 }
 
+
+BackupDataReader::BackupDataReader(int fd)
+    :m_fd(fd),
+     m_status(NO_ERROR),
+     m_pos(0),
+     m_entityCount(0)
+{
+    memset(&m_header, 0, sizeof(m_header));
+}
+
+BackupDataReader::~BackupDataReader()
+{
+}
+
+status_t
+BackupDataReader::Status()
+{
+    return m_status;
+}
+
+#define CHECK_SIZE(actual, expected) \
+    do { \
+        if ((actual) != (expected)) { \
+            if ((actual) == 0) { \
+                m_status = EIO; \
+            } else { \
+                m_status = errno; \
+            } \
+            return m_status; \
+        } \
+    } while(0)
+#define SKIP_PADDING() \
+    do { \
+        status_t err = skip_padding(); \
+        if (err != NO_ERROR) { \
+            m_status = err; \
+            return err; \
+        } \
+    } while(0)
+
+status_t
+BackupDataReader::ReadNextHeader()
+{
+    if (m_status != NO_ERROR) {
+        return m_status;
+    }
+
+    int amt;
+
+    SKIP_PADDING();
+    amt = read(m_fd, &m_header, sizeof(m_header));
+    CHECK_SIZE(amt, sizeof(m_header));
+
+    // validate and fix up the fields.
+    m_header.type = fromlel(m_header.type);
+    switch (m_header.type)
+    {
+        case APP_MAGIC_V1:
+            m_header.app.packageLen = fromlel(m_header.app.packageLen);
+            if (m_header.app.packageLen < 0) {
+                LOGD("App header at %d has packageLen<0: 0x%08x\n", (int)m_pos,
+                    (int)m_header.app.packageLen);
+                m_status = EINVAL;
+            }
+            m_header.app.cookie = m_header.app.cookie;
+            break;
+        case ENTITY_MAGIC_V1:
+            m_header.entity.keyLen = fromlel(m_header.entity.keyLen);
+            if (m_header.entity.keyLen <= 0) {
+                LOGD("Entity header at %d has keyLen<=0: 0x%08x\n", (int)m_pos,
+                        (int)m_header.entity.keyLen);
+                m_status = EINVAL;
+            }
+            m_header.entity.dataSize = fromlel(m_header.entity.dataSize);
+            if (m_header.entity.dataSize < 0) {
+                LOGD("Entity header at %d has dataSize<0: 0x%08x\n", (int)m_pos,
+                        (int)m_header.entity.dataSize);
+                m_status = EINVAL;
+            }
+            m_entityCount++;
+            break;
+        case FOOTER_MAGIC_V1:
+            m_header.footer.entityCount = fromlel(m_header.footer.entityCount);
+            if (m_header.footer.entityCount < 0) {
+                LOGD("Entity header at %d has entityCount<0: 0x%08x\n", (int)m_pos,
+                        (int)m_header.footer.entityCount);
+                m_status = EINVAL;
+            }
+            m_header.footer.cookie = m_header.footer.cookie;
+            break;
+        default:
+            LOGD("Chunk header at %d has invalid type: 0x%08x", (int)m_pos, (int)m_header.type);
+            m_status = EINVAL;
+    }
+    m_pos += sizeof(m_header);
+    
+    return m_status;
+}
+
+status_t
+BackupDataReader::ReadAppHeader(String8* packageName, int* cookie)
+{
+    if (m_status != NO_ERROR) {
+        return m_status;
+    }
+    if (m_header.type != APP_MAGIC_V1) {
+        return EINVAL;
+    }
+    size_t size = m_header.app.packageLen;
+    char* buf = packageName->lockBuffer(size);
+    if (packageName == NULL) {
+        packageName->unlockBuffer();
+        m_status = ENOMEM;
+        return m_status;
+    }
+    int amt = read(m_fd, buf, size+1);
+    CHECK_SIZE(amt, (int)size+1);
+    packageName->unlockBuffer(size);
+    m_pos += size+1;
+    *cookie = m_header.app.cookie;
+    return NO_ERROR;
+}
+
+bool
+BackupDataReader::HasEntities()
+{
+    return m_status == NO_ERROR && m_header.type == ENTITY_MAGIC_V1;
+}
+
+status_t
+BackupDataReader::ReadEntityHeader(String8* key, size_t* dataSize)
+{
+    if (m_status != NO_ERROR) {
+        return m_status;
+    }
+    if (m_header.type != ENTITY_MAGIC_V1) {
+        return EINVAL;
+    }
+    size_t size = m_header.app.packageLen;
+    char* buf = key->lockBuffer(size);
+    if (key == NULL) {
+        key->unlockBuffer();
+        m_status = ENOMEM;
+        return m_status;
+    }
+    int amt = read(m_fd, buf, size+1);
+    CHECK_SIZE(amt, (int)size+1);
+    key->unlockBuffer(size);
+    m_pos += size+1;
+    *dataSize = m_header.entity.dataSize;
+    SKIP_PADDING();
+    return NO_ERROR;
+}
+
+status_t
+BackupDataReader::ReadEntityData(void* data, size_t size)
+{
+    if (m_status != NO_ERROR) {
+        return m_status;
+    }
+    int amt = read(m_fd, data, size);
+    CHECK_SIZE(amt, (int)size);
+    m_pos += size;
+    return NO_ERROR;
+}
+
+status_t
+BackupDataReader::ReadAppFooter(int* cookie)
+{
+    if (m_status != NO_ERROR) {
+        return m_status;
+    }
+    if (m_header.type != FOOTER_MAGIC_V1) {
+        return EINVAL;
+    }
+    if (m_header.footer.entityCount != m_entityCount) {
+        LOGD("entity count mismatch actual=%d expected=%d", m_entityCount,
+                m_header.footer.entityCount);
+        m_status = EINVAL;
+        return m_status;
+    }
+    *cookie = m_header.footer.cookie;
+    return NO_ERROR;
+}
+
+status_t
+BackupDataReader::skip_padding()
+{
+    ssize_t amt;
+    ssize_t paddingSize;
+
+    paddingSize = padding_extra(m_pos);
+    if (paddingSize > 0) {
+        uint32_t padding;
+        amt = read(m_fd, &padding, paddingSize);
+        CHECK_SIZE(amt, paddingSize);
+        m_pos += amt;
+    }
+    return NO_ERROR;
+}
+
+
 } // namespace android