Make RestoreHelper and friends also write out the snapshot state.
diff --git a/core/java/android/backup/RestoreHelper.java b/core/java/android/backup/RestoreHelper.java
index ac7d8ee..e47869c 100644
--- a/core/java/android/backup/RestoreHelper.java
+++ b/core/java/android/backup/RestoreHelper.java
@@ -16,6 +16,8 @@
 
 package android.backup;
 
+import android.os.ParcelFileDescriptor;
+
 import java.io.InputStream;
 
 /** @hide */
@@ -27,5 +29,6 @@
      * <code>dataSize</code> bytes from <code>data</code>.
      */
     public void restoreEntity(BackupDataInputStream data);
+    public void writeSnapshot(ParcelFileDescriptor fd);
 }
 
diff --git a/core/java/android/backup/RestoreHelperBase.java b/core/java/android/backup/RestoreHelperBase.java
index 894c9af..93a8fef 100644
--- a/core/java/android/backup/RestoreHelperBase.java
+++ b/core/java/android/backup/RestoreHelperBase.java
@@ -17,69 +17,66 @@
 package android.backup;
 
 import android.content.Context;
+import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
 import java.io.InputStream;
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileOutputStream;
-import java.io.IOException;
 
 class RestoreHelperBase {
     private static final String TAG = "RestoreHelperBase";
-    private static final int BUF_SIZE = 8 * 1024;
 
+    int mPtr;
     Context mContext;
-    byte[] mBuf = new byte[BUF_SIZE];
     boolean mExceptionLogged;
     
     RestoreHelperBase(Context context) {
+        mPtr = ctor();
         mContext = context;
     }
 
-    void writeFile(File f, InputStream in) {
-        boolean success = false;
-        FileOutputStream out = null;
+    protected void finalize() throws Throwable {
         try {
-            // Create the enclosing directory.
-            File parent = f.getParentFile();
-            parent.mkdirs();
+            dtor(mPtr);
+        } finally {
+            super.finalize();
+        }
+    }
 
-            // Copy the file.
-            int sum = 0;
-            out = new FileOutputStream(f);
-            byte[] buf = mBuf;
-            int amt;
-            while ((amt = in.read(buf)) > 0) {
-                out.write(buf, 0, amt);
-                sum += amt;
-            }
+    void writeFile(File f, InputStream in) {
+        if (!(in instanceof BackupDataInputStream)) {
+            throw new IllegalStateException("input stream must be a BackupDataInputStream");
+        }
+        int result = -1;
 
-            // TODO: Set the permissions of the file.
+        // Create the enclosing directory.
+        File parent = f.getParentFile();
+        parent.mkdirs();
 
-            // We're done
-            success = true;
-            out = null;
-        } catch (IOException ex) {
-            // Bail on this entity.  Only log one exception per helper object.
+        result = writeFile_native(mPtr, f.getAbsolutePath(),
+                ((BackupDataInputStream)in).mData.mBackupReader);
+        if (result != 0) {
+            // Bail on this entity.  Only log one failure per helper object.
             if (!mExceptionLogged) {
                 Log.e(TAG, "Failed restoring file '" + f + "' for app '"
-                    + mContext.getPackageName() + '\'', ex);
+                        + mContext.getPackageName() + "\' result=0x"
+                        + Integer.toHexString(result));
                 mExceptionLogged = true;
             }
         }
-        finally {
-            if (out != null) {
-                try {
-                    out.close();
-                } catch (IOException ex) {
-                }
-            }
-            if (!success) {
-                // Something didn't work out, delete the file
-                f.delete();
-            }
-        }
     }
+
+    public void writeSnapshot(ParcelFileDescriptor fd) {
+        int result = writeSnapshot_native(mPtr, fd.getFileDescriptor());
+        // TODO: Do something with the error.
+    }
+
+    private static native int ctor();
+    private static native void dtor(int ptr);
+    private static native int writeFile_native(int ptr, String filename, int backupReader);
+    private static native int writeSnapshot_native(int ptr, FileDescriptor fd);
 }
 
 
diff --git a/core/java/android/backup/RestoreHelperDispatcher.java b/core/java/android/backup/RestoreHelperDispatcher.java
index 5928914..4861775 100644
--- a/core/java/android/backup/RestoreHelperDispatcher.java
+++ b/core/java/android/backup/RestoreHelperDispatcher.java
@@ -16,10 +16,12 @@
 
 package android.backup;
 
+import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
 import java.io.IOException;
 import java.util.HashMap;
+import java.util.Map;
 
 /** @hide */
 public class RestoreHelperDispatcher {
@@ -31,7 +33,7 @@
         mHelpers.put(keyPrefix, helper);
     }
 
-    public void dispatch(BackupDataInput input) throws IOException {
+    public void dispatch(BackupDataInput input, ParcelFileDescriptor newState) throws IOException {
         boolean alreadyComplained = false;
 
         BackupDataInputStream stream = new BackupDataInputStream(input);
@@ -60,5 +62,17 @@
             }
             input.skipEntityData(); // In case they didn't consume the data.
         }
+
+        if (mHelpers.size() > 1) {
+            throw new RuntimeException("RestoreHelperDispatcher won't get your your"
+                    + " data in the right order yet.");
+        }
+        
+        // Write out the state files
+        for (RestoreHelper helper: mHelpers.values()) {
+            // TODO: Write a header for the state
+            helper.writeSnapshot(newState);
+        }
     }
 }
+
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index fee8396..faa04f7 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -119,7 +119,8 @@
 	com_android_internal_graphics_NativeUtils.cpp \
 	android_backup_BackupDataInput.cpp \
 	android_backup_BackupDataOutput.cpp \
-	android_backup_FileBackupHelper.cpp
+	android_backup_FileBackupHelper.cpp \
+	android_backup_RestoreHelperBase.cpp
 
 LOCAL_C_INCLUDES += \
 	$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 6d829fc..18f6d5f9 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -158,6 +158,7 @@
 extern int register_android_backup_BackupDataInput(JNIEnv *env);
 extern int register_android_backup_BackupDataOutput(JNIEnv *env);
 extern int register_android_backup_FileBackupHelper(JNIEnv *env);
+extern int register_android_backup_RestoreHelperBase(JNIEnv *env);
 
 static AndroidRuntime* gCurRuntime = NULL;
 
@@ -1131,6 +1132,7 @@
     REG_JNI(register_android_backup_BackupDataInput),
     REG_JNI(register_android_backup_BackupDataOutput),
     REG_JNI(register_android_backup_FileBackupHelper),
+    REG_JNI(register_android_backup_RestoreHelperBase),
 };
 
 /*
diff --git a/core/jni/android_backup_RestoreHelperBase.cpp b/core/jni/android_backup_RestoreHelperBase.cpp
new file mode 100644
index 0000000..3173420
--- /dev/null
+++ b/core/jni/android_backup_RestoreHelperBase.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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 "FileBackupHelper_native"
+#include <utils/Log.h>
+
+#include "JNIHelp.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include <utils/BackupHelpers.h>
+
+namespace android
+{
+
+// java.io.FileDescriptor
+static jfieldID s_descriptorField = 0;
+
+static int
+ctor(JNIEnv* env, jobject clazz)
+{
+    return (int)new RestoreHelperBase();
+}
+
+static void
+dtor(JNIEnv* env, jobject clazz, jint ptr)
+{
+    delete (RestoreHelperBase*)ptr;
+}
+
+static int
+writeFile_native(JNIEnv* env, jobject clazz, jint ptr, jstring filenameObj, int backupReaderPtr)
+{
+    int err;
+    RestoreHelperBase* restore = (RestoreHelperBase*)ptr;
+    BackupDataReader* reader = (BackupDataReader*)backupReaderPtr;
+    char const* filename;
+
+    filename = env->GetStringUTFChars(filenameObj, NULL);
+
+    err = restore->WriteFile(String8(filename), reader);
+
+    env->ReleaseStringUTFChars(filenameObj, filename);
+
+    return err;
+}
+
+static int
+writeSnapshot_native(JNIEnv* env, jobject clazz, jint ptr, jobject fileDescriptor)
+{
+    int err;
+
+    RestoreHelperBase* restore = (RestoreHelperBase*)ptr;
+    int fd = env->GetIntField(fileDescriptor, s_descriptorField);
+
+    err = restore->WriteSnapshot(fd);
+
+    return err;
+}
+
+static const JNINativeMethod g_methods[] = {
+    { "ctor", "()I", (void*)ctor },
+    { "dtor", "(I)V", (void*)dtor },
+    { "writeFile_native", "(ILjava/lang/String;I)I", (void*)writeFile_native },
+    { "writeSnapshot_native", "(ILjava/io/FileDescriptor;)I", (void*)writeSnapshot_native },
+};
+
+int register_android_backup_RestoreHelperBase(JNIEnv* env)
+{
+    jclass clazz;
+
+    clazz = env->FindClass("java/io/FileDescriptor");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
+    s_descriptorField = env->GetFieldID(clazz, "descriptor", "I");
+    LOG_FATAL_IF(s_descriptorField == NULL,
+            "Unable to find descriptor field in java.io.FileDescriptor");
+    
+    return AndroidRuntime::registerNativeMethods(env, "android/backup/RestoreHelperBase",
+            g_methods, NELEM(g_methods));
+}
+
+}
diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h
index fa7f8d5..fc701fd 100644
--- a/include/utils/BackupHelpers.h
+++ b/include/utils/BackupHelpers.h
@@ -19,6 +19,7 @@
 
 #include <utils/Errors.h>
 #include <utils/String8.h>
+#include <utils/KeyedVector.h>
 
 namespace android {
 
@@ -32,6 +33,27 @@
     int dataSize; // size of the data, not including the padding, -1 means delete
 } entity_header_v1;
 
+struct SnapshotHeader {
+    int magic0;
+    int fileCount;
+    int magic1;
+    int totalSize;
+};
+
+struct FileState {
+    int modTime_sec;
+    int modTime_nsec;
+    int size;
+    int crc32;
+    int nameLen;
+};
+
+struct FileRec {
+    String8 file;
+    bool deleted;
+    FileState s;
+};
+
 
 /**
  * Writes the data.
@@ -99,6 +121,19 @@
 int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD,
         char const* const* files, char const* const *keys, int fileCount);
 
+class RestoreHelperBase
+{
+public:
+    RestoreHelperBase();
+    ~RestoreHelperBase();
+
+    status_t WriteFile(const String8& filename, BackupDataReader* in);
+    status_t WriteSnapshot(int fd);
+
+private:
+    void* m_buf;
+    KeyedVector<String8,FileRec> m_files;
+};
 
 #define TEST_BACKUP_HELPERS 1
 
diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp
index c1d5404..99687bc 100644
--- a/libs/utils/BackupHelpers.cpp
+++ b/libs/utils/BackupHelpers.cpp
@@ -47,27 +47,6 @@
 #define LOGP(x...) LOGD(x)
 #endif
 
-struct SnapshotHeader {
-    int magic0;
-    int fileCount;
-    int magic1;
-    int totalSize;
-};
-
-struct FileState {
-    int modTime_sec;
-    int modTime_nsec;
-    int size;
-    int crc32;
-    int nameLen;
-};
-
-struct FileRec {
-    char const* file; // this object does not own this string
-    bool deleted;
-    FileState s;
-};
-
 const static int ROUND_UP[4] = { 0, 3, 2, 1 };
 
 static inline int
@@ -310,7 +289,8 @@
     for (int i=0; i<fileCount; i++) {
         String8 key(keys[i]);
         FileRec r;
-        char const* file = r.file = files[i];
+        char const* file = files[i];
+        r.file = file;
         struct stat st;
 
         err = stat(file, &st);
@@ -351,20 +331,20 @@
         }
         else if (cmp > 0) {
             // file added
-            LOGP("file added: %s", g.file);
-            write_update_file(dataStream, q, g.file);
+            LOGP("file added: %s", g.file.string());
+            write_update_file(dataStream, q, g.file.string());
             m++;
         }
         else {
             // both files exist, check them
             const FileState& f = oldSnapshot.valueAt(n);
 
-            int fd = open(g.file, O_RDONLY);
+            int fd = open(g.file.string(), O_RDONLY);
             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", g.file);
+                LOGP("Unable to open file %s - skipping", g.file.string());
             } else {
                 g.s.crc32 = compute_crc32(fd);
 
@@ -375,7 +355,7 @@
                         g.s.modTime_sec, g.s.modTime_nsec, g.s.size, g.s.crc32);
                 if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec
                         || f.size != g.s.size || f.crc32 != g.s.crc32) {
-                    write_update_file(dataStream, fd, p, g.file);
+                    write_update_file(dataStream, fd, p, g.file.string());
                 }
 
                 close(fd);
@@ -395,7 +375,7 @@
     while (m<fileCount) {
         const String8& q = newSnapshot.keyAt(m);
         FileRec& g = newSnapshot.editValueAt(m);
-        write_update_file(dataStream, q, g.file);
+        write_update_file(dataStream, q, g.file.string());
         m++;
     }
 
@@ -404,6 +384,84 @@
     return 0;
 }
 
+#define RESTORE_BUF_SIZE (8*1024)
+
+RestoreHelperBase::RestoreHelperBase()
+{
+    m_buf = malloc(RESTORE_BUF_SIZE);
+}
+
+RestoreHelperBase::~RestoreHelperBase()
+{
+    free(m_buf);
+}
+
+status_t
+RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in)
+{
+    ssize_t err;
+    size_t dataSize;
+    String8 key;
+    int fd;
+    void* buf = m_buf;
+    ssize_t amt;
+    int mode;
+    int crc;
+    struct stat st;
+    FileRec r;
+
+    err = in->ReadEntityHeader(&key, &dataSize);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    
+    // TODO: World readable/writable for now.
+    mode = 0666;
+
+    // Write the file and compute the crc
+    crc = crc32(0L, Z_NULL, 0);
+    fd = open(filename.string(), O_CREAT|O_RDWR, mode);
+    if (fd != -1) {
+        return errno;
+    }
+    
+    while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) {
+        err = write(fd, buf, amt);
+        if (err != amt) {
+            close(fd);
+            return errno;
+        }
+        crc = crc32(crc, (Bytef*)buf, amt);
+    }
+
+    close(fd);
+
+    // Record for the snapshot
+    err = stat(filename.string(), &st);
+    if (err != 0) {
+        LOGW("Error stating file that we just created %s", filename.string());
+        return errno;
+    }
+
+    r.file = filename;
+    r.deleted = false;
+    r.s.modTime_sec = st.st_mtime;
+    r.s.modTime_nsec = 0; // workaround sim breakage
+    //r.s.modTime_nsec = st.st_mtime_nsec;
+    r.s.size = st.st_size;
+    r.s.crc32 = crc;
+
+    m_files.add(key, r);
+
+    return NO_ERROR;
+}
+
+status_t
+RestoreHelperBase::WriteSnapshot(int fd)
+{
+    return write_snapshot_file(fd, m_files);;
+}
+
 #if TEST_BACKUP_HELPERS
 
 #define SCRATCH_DIR "/data/backup_helper_test/"
@@ -560,7 +618,6 @@
     FileState states[4];
     FileRec r;
     r.deleted = false;
-    r.file = NULL;
 
     states[0].modTime_sec = 0xfedcba98;
     states[0].modTime_nsec = 0xdeadbeef;
diff --git a/tests/backup/src/com/android/backuptest/BackupTestActivity.java b/tests/backup/src/com/android/backuptest/BackupTestActivity.java
index d87e85c..f0c3f93 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestActivity.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestActivity.java
@@ -161,8 +161,13 @@
                             new FileRestoreHelper(BackupTestActivity.this));
                     FileInputStream dataFile = openFileInput("backup_test");
                     BackupDataInput data = new BackupDataInput(dataFile.getFD());
-                    dispatch.dispatch(data);
+                    ParcelFileDescriptor state = ParcelFileDescriptor.open(
+                            new File(getFilesDir(), "restore_state"),
+                            ParcelFileDescriptor.MODE_READ_WRITE|ParcelFileDescriptor.MODE_CREATE|
+                            ParcelFileDescriptor.MODE_TRUNCATE);
+                    dispatch.dispatch(data, state);
                     dataFile.close();
+                    state.close();
                 } catch (IOException ex) {
                     throw new RuntimeException(ex);
                 }
diff --git a/tests/backup/src/com/android/backuptest/BackupTestAgent.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
index e3566ec..0da4151 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestAgent.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
@@ -55,7 +55,7 @@
         // dispatch.addHelper(SHARED_PREFS, new SharedPrefsRestoreHelper(this));
         dispatch.addHelper(DATA_FILES, new FileRestoreHelper(this));
 
-        dispatch.dispatch(data);
+        dispatch.dispatch(data, newState);
     }
 }