FileRestoreHelper and RestoreHelperDispatcher work.
diff --git a/core/java/android/backup/BackupDataInputStream.java b/core/java/android/backup/BackupDataInputStream.java
index 52b1675..b705c4c 100644
--- a/core/java/android/backup/BackupDataInputStream.java
+++ b/core/java/android/backup/BackupDataInputStream.java
@@ -16,6 +16,8 @@
 
 package android.backup;
 
+import android.util.Log;
+
 import java.io.InputStream;
 import java.io.IOException;
 
diff --git a/core/java/android/backup/FileRestoreHelper.java b/core/java/android/backup/FileRestoreHelper.java
new file mode 100644
index 0000000..b7e3625
--- /dev/null
+++ b/core/java/android/backup/FileRestoreHelper.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.backup;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.io.File;
+
+/** @hide */
+public class FileRestoreHelper extends RestoreHelperBase implements RestoreHelper {
+    private static final String TAG = "FileRestoreHelper";
+
+    File mFilesDir;
+
+    public FileRestoreHelper(Context context) {
+        super(context);
+        mFilesDir = context.getFilesDir();
+    }
+
+    public void restoreEntity(BackupDataInputStream data) {
+        Log.d(TAG, "got entity '" + data.getKey() + "' size=" + data.size()); // TODO: turn this off before ship
+        File f = new File(mFilesDir, data.getKey());
+        writeFile(f, data);
+    }
+}
+
diff --git a/core/java/android/backup/RestoreHelper.java b/core/java/android/backup/RestoreHelper.java
index ee8bedd..ac7d8ee 100644
--- a/core/java/android/backup/RestoreHelper.java
+++ b/core/java/android/backup/RestoreHelper.java
@@ -26,6 +26,6 @@
      * Do not close the <code>data</code> stream.  Do not read more than
      * <code>dataSize</code> bytes from <code>data</code>.
      */
-    public void performRestore(BackupDataInputStream data);
+    public void restoreEntity(BackupDataInputStream data);
 }
 
diff --git a/core/java/android/backup/RestoreHelperBase.java b/core/java/android/backup/RestoreHelperBase.java
new file mode 100644
index 0000000..894c9af
--- /dev/null
+++ b/core/java/android/backup/RestoreHelperBase.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+package android.backup;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.io.InputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+class RestoreHelperBase {
+    private static final String TAG = "RestoreHelperBase";
+    private static final int BUF_SIZE = 8 * 1024;
+
+    Context mContext;
+    byte[] mBuf = new byte[BUF_SIZE];
+    boolean mExceptionLogged;
+    
+    RestoreHelperBase(Context context) {
+        mContext = context;
+    }
+
+    void writeFile(File f, InputStream in) {
+        boolean success = false;
+        FileOutputStream out = null;
+        try {
+            // Create the enclosing directory.
+            File parent = f.getParentFile();
+            parent.mkdirs();
+
+            // 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;
+            }
+
+            // TODO: Set the permissions of the file.
+
+            // We're done
+            success = true;
+            out = null;
+        } catch (IOException ex) {
+            // Bail on this entity.  Only log one exception per helper object.
+            if (!mExceptionLogged) {
+                Log.e(TAG, "Failed restoring file '" + f + "' for app '"
+                    + mContext.getPackageName() + '\'', ex);
+                mExceptionLogged = true;
+            }
+        }
+        finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException ex) {
+                }
+            }
+            if (!success) {
+                // Something didn't work out, delete the file
+                f.delete();
+            }
+        }
+    }
+}
+
+
diff --git a/core/java/android/backup/RestoreHelperDispatcher.java b/core/java/android/backup/RestoreHelperDispatcher.java
index cbfefdc..8fcade4 100644
--- a/core/java/android/backup/RestoreHelperDispatcher.java
+++ b/core/java/android/backup/RestoreHelperDispatcher.java
@@ -21,7 +21,7 @@
 
 /** @hide */
 public class RestoreHelperDispatcher {
-    HashMap<String,RestoreHelper> mHelpers;
+    HashMap<String,RestoreHelper> mHelpers = new HashMap<String,RestoreHelper>();
 
     public void addHelper(String keyPrefix, RestoreHelper helper) {
         mHelpers.put(keyPrefix, helper);
@@ -38,7 +38,7 @@
                 if (helper != null) {
                     stream.dataSize = input.getDataSize();
                     stream.key = rawKey.substring(pos+1);
-                    helper.performRestore(stream);
+                    helper.restoreEntity(stream);
                 }
             }
             input.skipEntityData(); // In case they didn't consume the data.
diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h
index 3ca8ad2..fa7f8d5 100644
--- a/include/utils/BackupHelpers.h
+++ b/include/utils/BackupHelpers.h
@@ -78,7 +78,7 @@
     bool HasEntities();
     status_t ReadEntityHeader(String8* key, size_t* dataSize);
     status_t SkipEntityData(); // must be called with the pointer at the begining of the data.
-    status_t ReadEntityData(void* data, size_t size);
+    ssize_t ReadEntityData(void* data, size_t size);
 
 private:
     explicit BackupDataReader();
diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp
index 16ff1e5..34b37ed 100644
--- a/libs/utils/BackupData.cpp
+++ b/libs/utils/BackupData.cpp
@@ -281,16 +281,16 @@
     }
 }
 
-status_t
+ssize_t
 BackupDataReader::ReadEntityData(void* data, size_t size)
 {
     if (m_status != NO_ERROR) {
         return m_status;
     }
     int remaining = m_dataEndPos - m_pos;
+    //LOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n",
+    //        size, m_pos, m_dataEndPos, remaining);
     if (size > remaining) {
-        printf("size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n",
-                size, m_pos, m_dataEndPos, remaining);
         size = remaining;
     }
     if (remaining <= 0) {
@@ -299,7 +299,7 @@
     int amt = read(m_fd, data, size);
     CHECK_SIZE(amt, (int)size);
     m_pos += size;
-    return NO_ERROR;
+    return amt;
 }
 
 status_t
diff --git a/tests/backup/src/com/android/backuptest/BackupTestActivity.java b/tests/backup/src/com/android/backuptest/BackupTestActivity.java
index af7dfd4..aa940ae 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestActivity.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestActivity.java
@@ -17,14 +17,17 @@
 package com.android.backuptest;
 
 import android.app.ListActivity;
+import android.backup.BackupDataInput;
+import android.backup.BackupDataOutput;
 import android.backup.BackupManager;
+import android.backup.FileBackupHelper;
+import android.backup.FileRestoreHelper;
+import android.backup.RestoreHelperDispatcher;
 import android.content.Intent;
 import android.content.SharedPreferences;
-import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.PowerManager;
-import android.os.SystemClock;
+import android.os.ParcelFileDescriptor;
 import android.util.Log;
 import android.view.View;
 import android.widget.ArrayAdapter;
@@ -32,6 +35,10 @@
 import android.widget.Toast;
 
 import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.InputStreamReader;
 import java.io.IOException;
 import java.io.PrintStream;
@@ -123,6 +130,44 @@
                 BackupManager bm = new BackupManager(BackupTestActivity.this);
                 bm.dataChanged();
             }
+        },
+        new Test("Backup Helpers") {
+            void run() {
+                try {
+                    writeFile("a", "a\naa", MODE_PRIVATE);
+                    writeFile("empty", "", MODE_PRIVATE);
+
+                    ParcelFileDescriptor state = ParcelFileDescriptor.open(
+                            new File(getFilesDir(), "state"),
+                            ParcelFileDescriptor.MODE_READ_WRITE|ParcelFileDescriptor.MODE_CREATE|
+                            ParcelFileDescriptor.MODE_TRUNCATE);
+                    FileBackupHelper h = new FileBackupHelper(BackupTestActivity.this,
+                            "FileBackupHelper");
+                    FileOutputStream dataFile = openFileOutput("backup_test", MODE_WORLD_READABLE);
+                    BackupDataOutput data = new BackupDataOutput(BackupTestActivity.this,
+                            dataFile.getFD());
+                    h.performBackup(null, data, state, new String[] { "a", "empty" });
+                    dataFile.close();
+                    state.close();
+                } catch (IOException ex) {
+                    throw new RuntimeException(ex);
+                }
+            }
+        },
+        new Test("Restore Helpers") {
+            void run() {
+                try {
+                    RestoreHelperDispatcher dispatch = new RestoreHelperDispatcher();
+                    dispatch.addHelper("FileBackupHelper",
+                            new FileRestoreHelper(BackupTestActivity.this));
+                    FileInputStream dataFile = openFileInput("backup_test");
+                    BackupDataInput data = new BackupDataInput(dataFile.getFD());
+                    dispatch.dispatch(data);
+                    dataFile.close();
+                } catch (IOException ex) {
+                    throw new RuntimeException(ex);
+                }
+            }
         }
     };
 
@@ -154,5 +199,14 @@
         t.run();
     }
     
+    void writeFile(String name, String contents, int mode) {
+        try {
+            PrintStream out = new PrintStream(openFileOutput(name, mode));
+            out.print(contents);
+            out.close();
+        } catch (FileNotFoundException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
 }