Merge change 1575 into donut

* changes:
  Change _TOKENIZE tests to test the new token_index column
diff --git a/NOTICE b/NOTICE
index 267a6aa..bb9c5f2 100644
--- a/NOTICE
+++ b/NOTICE
@@ -220,3 +220,54 @@
 
    END OF TERMS AND CONDITIONS
 
+
+
+UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
+
+Unicode Data Files include all data files under the directories
+http://www.unicode.org/Public/, http://www.unicode.org/reports/,
+and http://www.unicode.org/cldr/data/ . Unicode Software includes any
+source code published in the Unicode Standard or under the directories
+http://www.unicode.org/Public/, http://www.unicode.org/reports/, and
+http://www.unicode.org/cldr/data/.
+
+NOTICE TO USER: Carefully read the following legal agreement. BY
+DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA
+FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY
+ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF
+THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY,
+DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright © 1991-2008 Unicode, Inc. All rights reserved. Distributed
+under the Terms of Use in http://www.unicode.org/copyright.html.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation (the
+"Data Files") or Unicode software and any associated documentation (the
+"Software") to deal in the Data Files or Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, and/or sell copies of the Data Files or Software,
+and to permit persons to whom the Data Files or Software are furnished to
+do so, provided that (a) the above copyright notice(s) and this permission
+notice appear with all copies of the Data Files or Software, (b) both the
+above copyright notice(s) and this permission notice appear in associated
+documentation, and (c) there is clear notice in each modified Data File
+or in the Software as well as in the documentation associated with the
+Data File(s) or Software that the data or software has been modified.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
+INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
+OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in these Data Files or Software without prior written
+authorization of the copyright holder.
diff --git a/include/utils/ByteOrder.h b/include/utils/ByteOrder.h
index 4c06067..baa3a83 100644
--- a/include/utils/ByteOrder.h
+++ b/include/utils/ByteOrder.h
@@ -38,6 +38,16 @@
  * intent is to allow us to avoid byte swapping on the device.
  */
 
+static inline uint32_t android_swap_long(uint32_t v)
+{
+    return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24);
+}
+
+static inline uint16_t android_swap_short(uint16_t v)
+{
+    return (v<<8) | (v>>8);
+}
+
 #define DEVICE_BYTE_ORDER LITTLE_ENDIAN
 
 #if BYTE_ORDER == DEVICE_BYTE_ORDER
@@ -49,16 +59,6 @@
 
 #else
 
-static inline uint32_t android_swap_long(uint32_t v)
-{
-    return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24);
-}
-
-static inline uint16_t android_swap_short(uint16_t v)
-{
-    return (v<<8) | (v>>8);
-}
-
 #define	dtohl(x)	(android_swap_long(x))
 #define	dtohs(x)	(android_swap_short(x))
 #define	htodl(x)	(android_swap_long(x))
@@ -66,4 +66,16 @@
 
 #endif
 
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define fromlel(x) (x)
+#define fromles(x) (x)
+#define tolel(x) (x)
+#define toles(x) (x)
+#else
+#define fromlel(x) (android_swap_long(x))
+#define fromles(x) (android_swap_short(x))
+#define tolel(x) (android_swap_long(x))
+#define toles(x) (android_swap_short(x))
+#endif
+
 #endif // _LIBS_UTILS_BYTE_ORDER_H
diff --git a/include/utils/backup_helpers.h b/include/utils/backup_helpers.h
index 137c5f1..0c59fec 100644
--- a/include/utils/backup_helpers.h
+++ b/include/utils/backup_helpers.h
@@ -1,15 +1,128 @@
+/*
+ * 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.
+ */
+
 #ifndef _UTILS_BACKUP_HELPERS_H
 #define _UTILS_BACKUP_HELPERS_H
 
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+namespace android {
+
 int back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD,
         char const* fileBase, char const* const* files, int fileCount);
 
+// the sizes of all of these match.
+typedef struct {
+    int type; // == APP_MAGIC_V1
+    int packageLen; // length of the name of the package that follows, not including the null.
+    int cookie;
+} 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
+    int cookie;
+} app_footer_v1;
+
+
+/**
+ * Writes the data.
+ *
+ * If an error occurs, it poisons this object and all write calls will fail
+ * with the error that occurred.
+ */
+class BackupDataWriter
+{
+public:
+    BackupDataWriter(int fd);
+    // does not close fd
+    ~BackupDataWriter();
+
+    status_t WriteAppHeader(const String8& packageName, int cookie);
+
+    status_t WriteEntityHeader(const String8& key, size_t dataSize);
+    status_t WriteEntityData(const void* data, size_t size);
+
+    status_t WriteAppFooter(int cookie);
+
+private:
+    explicit BackupDataWriter();
+    status_t write_padding_for(int n);
+    
+    int m_fd;
+    status_t m_status;
+    ssize_t m_pos;
+    int m_entityCount;
+};
+
+/**
+ * Reads the data.
+ *
+ * If an error occurs, it poisons this object and all write calls will fail
+ * with the error that occurred.
+ */
+class BackupDataReader
+{
+public:
+    BackupDataReader(int fd);
+    // does not close fd
+    ~BackupDataReader();
+
+    status_t Status();
+    status_t ReadNextHeader();
+
+    status_t ReadAppHeader(String8* packageName, int* cookie);
+    bool HasEntities();
+    status_t ReadEntityHeader(String8* key, size_t* dataSize);
+    status_t ReadEntityData(void* data, size_t size);
+    status_t ReadAppFooter(int* cookie);
+
+private:
+    explicit BackupDataReader();
+    status_t skip_padding();
+    
+    int m_fd;
+    status_t m_status;
+    ssize_t m_pos;
+    int m_entityCount;
+    union {
+        int type;
+        app_header_v1 app;
+        entity_header_v1 entity;
+        app_footer_v1 footer;
+    } m_header;
+};
+
 #define TEST_BACKUP_HELPERS 0
 
 #if TEST_BACKUP_HELPERS
 int backup_helper_test_empty();
 int backup_helper_test_four();
 int backup_helper_test_files();
+int backup_helper_test_data_writer();
+int backup_helper_test_data_reader();
 #endif
 
+} // namespace android
+
 #endif // _UTILS_BACKUP_HELPERS_H
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index f9fb780..5a1a89b 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -117,7 +117,8 @@
 	IPermissionController.cpp \
 	IServiceManager.cpp \
 	Unicode.cpp \
-	file_backup_helper.cpp
+    backup_data.cpp \
+	backup_helper_file.cpp
 
 ifeq ($(TARGET_SIMULATOR),true)
 LOCAL_SRC_FILES += $(hostSources)
diff --git a/libs/utils/backup_data.cpp b/libs/utils/backup_data.cpp
new file mode 100644
index 0000000..dd04449
--- /dev/null
+++ b/libs/utils/backup_data.cpp
@@ -0,0 +1,428 @@
+/*
+ * 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.
+ */
+
+#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 {
+
+/*
+ * File Format (v1):
+ *
+ * All ints are stored little-endian.
+ *
+ *  - An app_header_v1 struct.
+ *  - The name of the package, utf-8, null terminated, padded to 4-byte boundary.
+ *  - A sequence of zero or more key/value paires (entities), each with
+ *      - A entity_header_v1 struct
+ *      - The key, utf-8, null terminated, padded to 4-byte boundary.
+ *      - The value, padded to 4 byte boundary
+ */
+
+#define APP_MAGIC_V1 0x31707041 // App1 (little endian)
+#define ENTITY_MAGIC_V1 0x61746144 // Data (little endian)
+#define FOOTER_MAGIC_V1 0x746f6f46 // Foot (little endian)
+
+const static int ROUND_UP[4] = { 0, 3, 2, 1 };
+
+static inline size_t
+round_up(size_t n)
+{
+    return n + ROUND_UP[n % 4];
+}
+
+static inline size_t
+padding_extra(size_t n)
+{
+    return ROUND_UP[n % 4];
+}
+
+BackupDataWriter::BackupDataWriter(int fd)
+    :m_fd(fd),
+     m_status(NO_ERROR),
+     m_pos(0),
+     m_entityCount(0)
+{
+}
+
+BackupDataWriter::~BackupDataWriter()
+{
+}
+
+// Pad out anything they've previously written to the next 4 byte boundary.
+status_t
+BackupDataWriter::write_padding_for(int n)
+{
+    ssize_t amt;
+    ssize_t paddingSize;
+
+    paddingSize = padding_extra(n);
+    if (paddingSize > 0) {
+        uint32_t padding = 0xbcbcbcbc;
+        amt = write(m_fd, &padding, paddingSize);
+        if (amt != paddingSize) {
+            m_status = errno;
+            return m_status;
+        }
+        m_pos += amt;
+    }
+    return NO_ERROR;
+}
+
+status_t
+BackupDataWriter::WriteAppHeader(const String8& packageName, int cookie)
+{
+    if (m_status != NO_ERROR) {
+        return m_status;
+    }
+
+    ssize_t amt;
+
+    amt = write_padding_for(m_pos);
+    if (amt != 0) {
+        return amt;
+    }
+
+    app_header_v1 header;
+    ssize_t nameLen;
+
+    nameLen = packageName.length();
+
+    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)) {
+        m_status = errno;
+        return m_status;
+    }
+    m_pos += amt;
+
+    amt = write(m_fd, packageName.string(), nameLen+1);
+    if (amt != nameLen+1) {
+        m_status = errno;
+        return m_status;
+    }
+    m_pos += amt;
+
+    return NO_ERROR;
+}
+
+status_t
+BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize)
+{
+    if (m_status != NO_ERROR) {
+        return m_status;
+    }
+
+    ssize_t amt;
+
+    amt = write_padding_for(m_pos);
+    if (amt != 0) {
+        return amt;
+    }
+
+    entity_header_v1 header;
+    ssize_t keyLen;
+
+    keyLen = key.length();
+
+    header.type = tolel(ENTITY_MAGIC_V1);
+    header.keyLen = tolel(keyLen);
+    header.dataSize = tolel(dataSize);
+
+    amt = write(m_fd, &header, sizeof(entity_header_v1));
+    if (amt != sizeof(entity_header_v1)) {
+        m_status = errno;
+        return m_status;
+    }
+    m_pos += amt;
+
+    amt = write(m_fd, key.string(), keyLen+1);
+    if (amt != keyLen+1) {
+        m_status = errno;
+        return m_status;
+    }
+    m_pos += amt;
+
+    amt = write_padding_for(keyLen+1);
+
+    m_entityCount++;
+
+    return amt;
+}
+
+status_t
+BackupDataWriter::WriteEntityData(const void* data, size_t size)
+{
+    if (m_status != NO_ERROR) {
+        return m_status;
+    }
+
+    // We don't write padding here, because they're allowed to call this several
+    // times with smaller buffers.  We write it at the end of WriteEntityHeader
+    // instead.
+    ssize_t amt = write(m_fd, data, size);
+    if (amt != (ssize_t)size) {
+        m_status = errno;
+        return m_status;
+    }
+    m_pos += amt;
+    return NO_ERROR;
+}
+
+status_t
+BackupDataWriter::WriteAppFooter(int cookie)
+{
+    if (m_status != NO_ERROR) {
+        return m_status;
+    }
+
+    ssize_t amt;
+
+    amt = write_padding_for(m_pos);
+    if (amt != 0) {
+        return amt;
+    }
+
+    app_footer_v1 footer;
+    ssize_t nameLen;
+
+    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)) {
+        m_status = errno;
+        return m_status;
+    }
+    m_pos += amt;
+
+    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
diff --git a/libs/utils/file_backup_helper.cpp b/libs/utils/backup_helper_file.cpp
similarity index 66%
rename from libs/utils/file_backup_helper.cpp
rename to libs/utils/backup_helper_file.cpp
index 453084a..bf56945 100644
--- a/libs/utils/file_backup_helper.cpp
+++ b/libs/utils/backup_helper_file.cpp
@@ -1,3 +1,19 @@
+/*
+ * 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.
+ */
+
 #define LOG_TAG "file_backup_helper"
 
 #include <utils/backup_helpers.h>
@@ -19,13 +35,16 @@
 
 #include <cutils/log.h>
 
-using namespace android;
+namespace android {
 
 #define MAGIC0 0x70616e53 // Snap
 #define MAGIC1 0x656c6946 // File
 
+#if TEST_BACKUP_HELPERS
+#define LOGP(x...) printf(x)
+#else
 #define LOGP(x...) LOGD(x)
-//#define LOGP(x...) printf(x)
+#endif
 
 struct SnapshotHeader {
     int magic0;
@@ -118,6 +137,8 @@
         bytesWritten += sizeof(FileState) + round_up(name.length());
     }
 
+    LOGP("write_snapshot_file fd=%d\n", fd);
+
     int amt;
     SnapshotHeader header = { MAGIC0, N, MAGIC1, bytesWritten };
 
@@ -248,11 +269,13 @@
             // file added
             String8 realFilename(base);
             realFilename.appendPath(q);
+            LOGP("file added: %s\n", realFilename.string());
             write_update_file(realFilename, q);
             m++;
         }
         else if (cmp < 0) {
             // file removed
+            LOGP("file removed: %s\n", p.string());
             write_delete_file(p);
             n++;
         }
@@ -573,6 +596,266 @@
     return matched ? 0 : 1;
 }
 
+// hexdump -v -e '"    " 8/1 " 0x%02x," "\n"' data_writer.data
+const unsigned char DATA_GOLDEN_FILE[] = {
+     0x41, 0x70, 0x70, 0x31, 0x0b, 0x00, 0x00, 0x00,
+     0xdd, 0xcc, 0xbb, 0xaa, 0x6e, 0x6f, 0x5f, 0x70,
+     0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00,
+     0x44, 0x61, 0x74, 0x61, 0x0b, 0x00, 0x00, 0x00,
+     0x0c, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x5f, 0x70,
+     0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00,
+     0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69,
+     0x6e, 0x67, 0x5f, 0x00, 0x41, 0x70, 0x70, 0x31,
+     0x0c, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa,
+     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
+     0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc,
+     0x44, 0x61, 0x74, 0x61, 0x0c, 0x00, 0x00, 0x00,
+     0x0d, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64,
+     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33,
+     0x00, 0xbc, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64,
+     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33,
+     0x00, 0xbc, 0xbc, 0xbc, 0x41, 0x70, 0x70, 0x31,
+     0x0d, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa,
+     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
+     0x6f, 0x5f, 0x32, 0x5f, 0x5f, 0x00, 0xbc, 0xbc,
+     0x44, 0x61, 0x74, 0x61, 0x0d, 0x00, 0x00, 0x00,
+     0x0e, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64,
+     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
+     0x5f, 0x00, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64,
+     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f,
+     0x5f, 0x00, 0xbc, 0xbc, 0x41, 0x70, 0x70, 0x31,
+     0x0a, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa,
+     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
+     0x6f, 0x31, 0x00, 0xbc, 0x44, 0x61, 0x74, 0x61,
+     0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+     0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74,
+     0x6f, 0x31, 0x00, 0xbc, 0x70, 0x61, 0x64, 0x64,
+     0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00, 0xbc,
+     0x46, 0x6f, 0x6f, 0x74, 0x04, 0x00, 0x00, 0x00,
+     0x99, 0x99, 0x77, 0x77
+};
+const int DATA_GOLDEN_FILE_SIZE = sizeof(DATA_GOLDEN_FILE);
+
+static int
+test_write_header_and_entity(BackupDataWriter& writer, const char* str)
+{
+    int err;
+    String8 text(str);
+
+    err = writer.WriteAppHeader(text, 0xaabbccdd);
+    if (err != 0) {
+        fprintf(stderr, "WriteAppHeader failed with %s\n", strerror(err));
+        return err;
+    }
+
+    err = writer.WriteEntityHeader(text, text.length()+1);
+    if (err != 0) {
+        fprintf(stderr, "WriteEntityHeader failed with %s\n", strerror(err));
+        return err;
+    }
+
+    err = writer.WriteEntityData(text.string(), text.length()+1);
+    if (err != 0) {
+        fprintf(stderr, "write failed for data '%s'\n", text.string());
+        return errno;
+    }
+
+    return err;
+}
+
+int
+backup_helper_test_data_writer()
+{
+    int err;
+    int fd;
+    const char* filename = SCRATCH_DIR "data_writer.data";
+
+    system("rm -r " SCRATCH_DIR);
+    mkdir(SCRATCH_DIR, 0777);
+    mkdir(SCRATCH_DIR "data", 0777);
+    
+    fd = creat(filename, 0666);
+    if (fd == -1) {
+        fprintf(stderr, "error creating: %s\n", strerror(errno));
+        return errno;
+    }
+
+    BackupDataWriter writer(fd);
+
+    err = 0;
+    err |= test_write_header_and_entity(writer, "no_padding_");
+    err |= test_write_header_and_entity(writer, "padded_to__3");
+    err |= test_write_header_and_entity(writer, "padded_to_2__");
+    err |= test_write_header_and_entity(writer, "padded_to1");
+
+    writer.WriteAppFooter(0x77779999);
+
+    close(fd);
+
+    err = compare_file(filename, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
+    if (err != 0) {
+        return err;
+    }
+
+    return err;
+}
+
+int
+test_read_header_and_entity(BackupDataReader& reader, const char* str)
+{
+    int err;
+    int bufSize = strlen(str)+1;
+    char* buf = (char*)malloc(bufSize);
+    String8 string;
+    int cookie = 0x11111111;
+    size_t actualSize;
+
+    // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str);
+
+    err = reader.ReadNextHeader();
+    if (err != 0) {
+        fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err));
+        goto done;
+    }
+
+    err = reader.ReadAppHeader(&string, &cookie);
+    if (err != 0) {
+        fprintf(stderr, "ReadAppHeader failed with %s\n", strerror(err));
+        goto done;
+    }
+    if (string != str) {
+        fprintf(stderr, "ReadAppHeader expected packageName '%s' got '%s'\n", str, string.string());
+        err = EINVAL;
+        goto done;
+    }
+    if (cookie != (int)0xaabbccdd) {
+        fprintf(stderr, "ReadAppHeader expected cookie 0x%08x got 0x%08x\n", 0xaabbccdd, cookie);
+        err = EINVAL;
+        goto done;
+    }
+
+    err = reader.ReadNextHeader();
+    if (err != 0) {
+        fprintf(stderr, "ReadNextHeader (for entity header) failed with %s\n", strerror(err));
+        goto done;
+    }
+
+    err = reader.ReadEntityHeader(&string, &actualSize);
+    if (err != 0) {
+        fprintf(stderr, "ReadEntityHeader failed with %s\n", strerror(err));
+        goto done;
+    }
+    if (string != str) {
+        fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.string());
+        err = EINVAL;
+        goto done;
+    }
+    if ((int)actualSize != bufSize) {
+        fprintf(stderr, "ReadEntityHeader expected dataSize 0x%08x got 0x%08x\n", bufSize,
+                actualSize);
+        err = EINVAL;
+        goto done;
+    }
+
+    err = reader.ReadEntityData(buf, bufSize);
+    if (err != NO_ERROR) {
+        fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err));
+        goto done;
+    }
+
+    if (0 != memcmp(buf, str, bufSize)) {
+        fprintf(stderr, "ReadEntityData expected '%s' but got something starting with "
+                "%02x %02x %02x %02x\n", str, buf[0], buf[1], buf[2], buf[3]);
+        err = EINVAL;
+        goto done;
+    }
+
+    // The next read will confirm whether it got the right amount of data.
+
+done:
+    if (err != NO_ERROR) {
+        fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err));
+    }
+    free(buf);
+    return err;
+}
+
+int
+backup_helper_test_data_reader()
+{
+    int err;
+    int fd;
+    const char* filename = SCRATCH_DIR "data_reader.data";
+
+    system("rm -r " SCRATCH_DIR);
+    mkdir(SCRATCH_DIR, 0777);
+    mkdir(SCRATCH_DIR "data", 0777);
+    
+    fd = creat(filename, 0666);
+    if (fd == -1) {
+        fprintf(stderr, "error creating: %s\n", strerror(errno));
+        return errno;
+    }
+
+    err = write(fd, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE);
+    if (err != DATA_GOLDEN_FILE_SIZE) {
+        fprintf(stderr, "Error \"%s\" writing golden file %s\n", strerror(errno), filename);
+        return errno;
+    }
+
+    close(fd);
+
+    fd = open(filename, O_RDONLY);
+    if (fd == -1) {
+        fprintf(stderr, "Error \"%s\" opening golden file %s for read\n", strerror(errno),
+                filename);
+        return errno;
+    }
+
+    {
+        BackupDataReader reader(fd);
+
+        err = 0;
+
+        if (err == NO_ERROR) {
+            err = test_read_header_and_entity(reader, "no_padding_");
+        }
+
+        if (err == NO_ERROR) {
+            err = test_read_header_and_entity(reader, "padded_to__3");
+        }
+
+        if (err == NO_ERROR) {
+            err = test_read_header_and_entity(reader, "padded_to_2__");
+        }
+
+        if (err == NO_ERROR) {
+            err = test_read_header_and_entity(reader, "padded_to1");
+        }
+
+        if (err == NO_ERROR) {
+            err = reader.ReadNextHeader();
+            if (err != 0) {
+                fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err));
+            }
+
+            if (err == NO_ERROR) {
+                int cookie;
+                err |= reader.ReadAppFooter(&cookie);
+                if (cookie != 0x77779999) {
+                    fprintf(stderr, "app footer cookie expected=0x%08x actual=0x%08x\n",
+                        0x77779999, cookie);
+                    err = EINVAL;
+                }
+            }
+        }
+    }
+
+    close(fd);
+
+    return err;
+}
+
 static int
 get_mod_time(const char* filename, struct timeval times[2])
 {
@@ -594,8 +877,9 @@
 backup_helper_test_files()
 {
     int err;
-    int newSnapshotFD;
     int oldSnapshotFD;
+    int dataStreamFD;
+    int newSnapshotFD;
 
     system("rm -r " SCRATCH_DIR);
     mkdir(SCRATCH_DIR, 0777);
@@ -616,17 +900,24 @@
         "data/f"
     };
 
+    dataStreamFD = creat(SCRATCH_DIR "1.data", 0666);
+    if (dataStreamFD == -1) {
+        fprintf(stderr, "error creating: %s\n", strerror(errno));
+        return errno;
+    }
+
     newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666);
     if (newSnapshotFD == -1) {
         fprintf(stderr, "error creating: %s\n", strerror(errno));
         return errno;
     }
-
-    err = back_up_files(-1, newSnapshotFD, 0, SCRATCH_DIR, files_before, 5);
+    
+    err = back_up_files(-1, dataStreamFD, newSnapshotFD, SCRATCH_DIR, files_before, 5);
     if (err != 0) {
         return err;
     }
 
+    close(dataStreamFD);
     close(newSnapshotFD);
 
     sleep(3);
@@ -665,21 +956,30 @@
         return errno;
     }
 
+    dataStreamFD = creat(SCRATCH_DIR "2.data", 0666);
+    if (dataStreamFD == -1) {
+        fprintf(stderr, "error creating: %s\n", strerror(errno));
+        return errno;
+    }
+
     newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666);
     if (newSnapshotFD == -1) {
         fprintf(stderr, "error creating: %s\n", strerror(errno));
         return errno;
     }
 
-    err = back_up_files(oldSnapshotFD, newSnapshotFD, 0, SCRATCH_DIR, files_after, 6);
+    err = back_up_files(oldSnapshotFD, dataStreamFD, newSnapshotFD, SCRATCH_DIR, files_after, 6);
     if (err != 0) {
         return err;
     }
 
     close(oldSnapshotFD);
+    close(dataStreamFD);
     close(newSnapshotFD);
     
     return 0;
 }
 
 #endif // TEST_BACKUP_HELPERS
+
+}