Add RestoreFileHelper, BackupDataInput, and add java wrappers for the methods on BackupDataOutput.
diff --git a/core/java/android/app/FullBackupAgent.java b/core/java/android/app/FullBackupAgent.java
index 18d62e3..bf5cb5d 100644
--- a/core/java/android/app/FullBackupAgent.java
+++ b/core/java/android/app/FullBackupAgent.java
@@ -47,12 +47,11 @@
         }
 
         // That's the file set; now back it all up
-        FileBackupHelper.performBackup(this, oldState, data, newState,
-                (String[]) allFiles.toArray());
+        FileBackupHelper helper = new FileBackupHelper(this);
+        helper.performBackup(oldState, data, newState, (String[])allFiles.toArray());
     }
 
     @Override
     public void onRestore(ParcelFileDescriptor data, ParcelFileDescriptor newState) {
     }
-
 }
diff --git a/core/java/android/backup/BackupDataInput.java b/core/java/android/backup/BackupDataInput.java
new file mode 100644
index 0000000..609dd90
--- /dev/null
+++ b/core/java/android/backup/BackupDataInput.java
@@ -0,0 +1,103 @@
+/*
+ * 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 java.io.FileDescriptor;
+import java.io.IOException;
+
+/** @hide */
+public class BackupDataInput {
+    int mBackupReader;
+
+    private EntityHeader mHeader = new EntityHeader();
+    private boolean mHeaderReady;
+
+    private static class EntityHeader {
+        String key;
+        int dataSize;
+    }
+
+    public BackupDataInput(FileDescriptor fd) {
+        if (fd == null) throw new NullPointerException();
+        mBackupReader = ctor(fd);
+        if (mBackupReader == 0) {
+            throw new RuntimeException("Native initialization failed with fd=" + fd);
+        }
+    }
+
+    protected void finalize() throws Throwable {
+        try {
+            dtor(mBackupReader);
+        } finally {
+            super.finalize();
+        }
+    }
+
+    public boolean readNextHeader() throws IOException {
+        int result = readNextHeader_native(mBackupReader, mHeader);
+        if (result == 0) {
+            // read successfully
+            mHeaderReady = true;
+            return true;
+        } else if (result > 0) {
+            // done
+            mHeaderReady = false;
+            return false;
+        } else {
+            // error
+            mHeaderReady = false;
+            throw new IOException("result=0x" + Integer.toHexString(result));
+        }
+    }
+
+    public String getKey() {
+        if (mHeaderReady) {
+            return mHeader.key;
+        } else {
+            throw new IllegalStateException("mHeaderReady=false");
+        }
+    }
+
+    public int getDataSize() {
+        if (mHeaderReady) {
+            return mHeader.dataSize;
+        } else {
+            throw new IllegalStateException("mHeaderReady=false");
+        }
+    }
+
+    public int readEntityData(byte[] data, int size) throws IOException {
+        if (mHeaderReady) {
+            int result = readEntityData_native(mBackupReader, data, size);
+            if (result >= 0) {
+                return result;
+            } else {
+                throw new IOException("result=0x" + Integer.toHexString(result));
+            }
+        } else {
+            throw new IllegalStateException("mHeaderReady=false");
+        }
+    }
+
+    private native static int ctor(FileDescriptor fd);
+    private native static void dtor(int mBackupReader);
+
+    private native int readNextHeader_native(int mBackupReader, EntityHeader entity);
+    private native int readEntityData_native(int mBackupReader, byte[] data, int size);
+}
diff --git a/core/java/android/backup/BackupDataOutput.java b/core/java/android/backup/BackupDataOutput.java
index 25ae15b..1348d81 100644
--- a/core/java/android/backup/BackupDataOutput.java
+++ b/core/java/android/backup/BackupDataOutput.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 
 import java.io.FileDescriptor;
+import java.io.IOException;
 
 /** @hide */
 public class BackupDataOutput {
@@ -37,6 +38,24 @@
         }
     }
 
+    public int writeEntityHeader(String key, int dataSize) throws IOException {
+        int result = writeEntityHeader_native(mBackupWriter, key, dataSize);
+        if (result >= 0) {
+            return result;
+        } else {
+            throw new IOException("result=0x" + Integer.toHexString(result));
+        }
+    }
+
+    public int writeEntityData(byte[] data, int size) throws IOException {
+        int result = writeEntityData_native(mBackupWriter, data, size);
+        if (result >= 0) {
+            return result;
+        } else {
+            throw new IOException("result=0x" + Integer.toHexString(result));
+        }
+    }
+
     protected void finalize() throws Throwable {
         try {
             dtor(mBackupWriter);
@@ -47,5 +66,8 @@
         
     private native static int ctor(FileDescriptor fd);
     private native static void dtor(int mBackupWriter);
+
+    private native static int writeEntityHeader_native(int mBackupWriter, String key, int dataSize);
+    private native static int writeEntityData_native(int mBackupWriter, byte[] data, int size);
 }
 
diff --git a/core/java/android/backup/FileBackupHelper.java b/core/java/android/backup/FileBackupHelper.java
index 99051bf..ed840bb 100644
--- a/core/java/android/backup/FileBackupHelper.java
+++ b/core/java/android/backup/FileBackupHelper.java
@@ -27,21 +27,56 @@
 public class FileBackupHelper {
     private static final String TAG = "FileBackupHelper";
 
+    Context mContext;
+    String mKeyPrefix;
+
+    public FileBackupHelper(Context context) {
+        mContext = context;
+    }
+
+    public FileBackupHelper(Context context, String keyPrefix) {
+        mContext = context;
+        mKeyPrefix = keyPrefix;
+    }
+
     /**
      * Based on oldState, determine which of the files from the application's data directory
      * need to be backed up, write them to the data stream, and fill in newState with the
      * state as it exists now.
      */
-    public static void performBackup(Context context,
-            ParcelFileDescriptor oldState, BackupDataOutput data,
+    public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
             ParcelFileDescriptor newState, String[] files) {
-        File base = context.getFilesDir();
+        // file names
+        File base = mContext.getFilesDir();
         final int N = files.length;
         String[] fullPaths = new String[N];
         for (int i=0; i<N; i++) {
             fullPaths[i] = (new File(base, files[i])).getAbsolutePath();
         }
-        performBackup_checked(oldState, data, newState, fullPaths, files);
+
+        // keys
+        String[] keys = makeKeys(mKeyPrefix, files);
+
+        // go
+        performBackup_checked(oldState, data, newState, fullPaths, keys);
+    }
+
+    /**
+     * If keyPrefix is not null, prepend it to each of the strings in <code>original</code>;
+     * otherwise, return original.
+     */
+    static String[] makeKeys(String keyPrefix, String[] original) {
+        if (keyPrefix != null) {
+            String[] keys;
+            final int N = original.length;
+            keys = new String[N];
+            for (int i=0; i<N; i++) {
+                keys[i] = keyPrefix + ':' + original[i];
+            }
+            return keys;
+        } else {
+            return original;
+        }
     }
 
     /**
diff --git a/core/java/android/backup/RestoreHelper.java b/core/java/android/backup/RestoreHelper.java
new file mode 100644
index 0000000..ebd9906
--- /dev/null
+++ b/core/java/android/backup/RestoreHelper.java
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+/** @hide */
+public interface RestoreHelper {
+    public void performRestore();
+}
+
diff --git a/core/java/android/backup/RestoreHelperDistributor.java b/core/java/android/backup/RestoreHelperDistributor.java
new file mode 100644
index 0000000..555ca79
--- /dev/null
+++ b/core/java/android/backup/RestoreHelperDistributor.java
@@ -0,0 +1,28 @@
+/*
+ * 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 java.util.HashMap;
+
+/** @hide */
+public class RestoreHelperDistributor {
+    HashMap<String,RestoreHelper> mHelpers;
+
+    public void addHelper(String keyPrefix, RestoreHelper helper) {
+        mHelpers.put(keyPrefix, helper);
+    }
+}
diff --git a/core/java/android/backup/SharedPreferencesBackupHelper.java b/core/java/android/backup/SharedPreferencesBackupHelper.java
index 923dc1b..cad79df 100644
--- a/core/java/android/backup/SharedPreferencesBackupHelper.java
+++ b/core/java/android/backup/SharedPreferencesBackupHelper.java
@@ -23,16 +23,33 @@
 
 /** @hide */
 public class SharedPreferencesBackupHelper {
-    public static void performBackup(Context context,
-            ParcelFileDescriptor oldSnapshot, ParcelFileDescriptor newSnapshot,
+    private Context mContext;
+    private String mKeyPrefix;
+
+    public SharedPreferencesBackupHelper(Context context) {
+        mContext = context;
+    }
+
+    public SharedPreferencesBackupHelper(Context context, String keyPrefix) {
+        mContext = context;
+        mKeyPrefix = keyPrefix;
+    }
+    
+    public void performBackup(ParcelFileDescriptor oldSnapshot, ParcelFileDescriptor newSnapshot,
             BackupDataOutput data, String[] prefGroups) {
+        Context context = mContext;
+        
         // make filenames for the prefGroups
         final int N = prefGroups.length;
         String[] files = new String[N];
         for (int i=0; i<N; i++) {
-            files[i] = context.getSharedPrefsFile(prefGroups[i]).toString();
+            files[i] = context.getSharedPrefsFile(prefGroups[i]).getAbsolutePath();
         }
 
+        // make keys if necessary
+        String[] keys = FileBackupHelper.makeKeys(mKeyPrefix, prefGroups);
+
+        // go
         FileBackupHelper.performBackup_checked(oldSnapshot, data, newSnapshot, files, prefGroups);
     }
 }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index ed8deb1..fee8396 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -117,6 +117,7 @@
 	android_location_GpsLocationProvider.cpp \
 	com_android_internal_os_ZygoteInit.cpp \
 	com_android_internal_graphics_NativeUtils.cpp \
+	android_backup_BackupDataInput.cpp \
 	android_backup_BackupDataOutput.cpp \
 	android_backup_FileBackupHelper.cpp
 
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 302f39e..6d829fc 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -155,6 +155,7 @@
 extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env);
 extern int register_android_util_Base64(JNIEnv* env);
 extern int register_android_location_GpsLocationProvider(JNIEnv* env);
+extern int register_android_backup_BackupDataInput(JNIEnv *env);
 extern int register_android_backup_BackupDataOutput(JNIEnv *env);
 extern int register_android_backup_FileBackupHelper(JNIEnv *env);
 
@@ -1127,6 +1128,7 @@
     REG_JNI(register_android_ddm_DdmHandleNativeHeap),
     REG_JNI(register_android_util_Base64),
     REG_JNI(register_android_location_GpsLocationProvider),
+    REG_JNI(register_android_backup_BackupDataInput),
     REG_JNI(register_android_backup_BackupDataOutput),
     REG_JNI(register_android_backup_FileBackupHelper),
 };
diff --git a/core/jni/android_backup_BackupDataInput.cpp b/core/jni/android_backup_BackupDataInput.cpp
new file mode 100644
index 0000000..cb0ab5f
--- /dev/null
+++ b/core/jni/android_backup_BackupDataInput.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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;
+
+// android.backup.BackupDataInput$EntityHeader
+static jfieldID s_keyField = 0;
+static jfieldID s_dataSizeField = 0;
+
+static int
+ctor_native(JNIEnv* env, jobject clazz, jobject fileDescriptor)
+{
+    int err;
+
+    int fd = env->GetIntField(fileDescriptor, s_descriptorField);
+    if (fd == -1) {
+        return NULL;
+    }
+
+    return (int)new BackupDataReader(fd);
+}
+
+static void
+dtor_native(JNIEnv* env, jobject clazz, int r)
+{
+    delete (BackupDataReader*)r;
+}
+
+static jint
+readNextHeader_native(JNIEnv* env, jobject clazz, int r, jobject entity)
+{
+    int err;
+    BackupDataReader* reader = (BackupDataReader*)r;
+
+    err = reader->Status();
+    if (err != 0) {
+        return err < 0 ? err : -1;
+    }
+
+    while (reader->HasEntities()) {
+        int type;
+
+        err = reader->ReadNextHeader(&type);
+        if (err != 0) {
+            return err < 0 ? err : -1;
+        }
+
+        switch (type) {
+        case BACKUP_HEADER_APP_V1:
+        {
+            String8 packageName;
+            int cookie;
+            err = reader->ReadAppHeader(&packageName, &cookie);
+            if (err != 0) {
+                return err < 0 ? err : -1;
+            }
+            break;
+        }
+        case BACKUP_HEADER_ENTITY_V1:
+        {
+            String8 key;
+            size_t dataSize;
+            err = reader->ReadEntityHeader(&key, &dataSize);
+            if (err != 0) {
+                return err < 0 ? err : -1;
+            }
+            // TODO: Set the fields in the entity object
+            return 0;
+        }
+        case BACKUP_FOOTER_APP_V1:
+            break;
+        default:
+            LOGD("Unknown header type: 0x%08x\n", type);
+            return -1;
+        }
+    }
+    // done
+    return 1;
+}
+
+static jint
+readEntityData_native(JNIEnv* env, jobject clazz, int r, jbyteArray data, int size)
+{
+    int err;
+    BackupDataReader* reader = (BackupDataReader*)r;
+
+    if (env->GetArrayLength(data) > size) {
+        // size mismatch
+        return -1;
+    }
+
+    jbyte* dataBytes = env->GetByteArrayElements(data, NULL);
+    if (dataBytes == NULL) {
+        return -1;
+    }
+
+   err = reader->ReadEntityData(dataBytes, size);
+
+    env->ReleaseByteArrayElements(data, dataBytes, 0);
+
+    return err;
+}
+
+static const JNINativeMethod g_methods[] = {
+    { "ctor", "(Ljava/io/FileDescriptor;)I", (void*)ctor_native },
+    { "dtor", "(I)V", (void*)dtor_native },
+    { "readNextHeader_native", "(ILandroid/backup/BackupDataInput$EntityHeader;)I",
+            (void*)readNextHeader_native },
+    { "readEntityData_native", "(I[BI)I", (void*)readEntityData_native },
+};
+
+int register_android_backup_BackupDataInput(JNIEnv* env)
+{
+    LOGD("register_android_backup_BackupDataInput");
+
+    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");
+
+    clazz = env->FindClass("android/backup/BackupDataInput$EntityHeader");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.backup.BackupDataInput.EntityHeader");
+    s_keyField = env->GetFieldID(clazz, "key", "Ljava/lang/String;");
+    LOG_FATAL_IF(s_keyField == NULL,
+            "Unable to find key field in android.backup.BackupDataInput.EntityHeader");
+    s_dataSizeField = env->GetFieldID(clazz, "dataSize", "Ljava/lang/String;");
+    LOG_FATAL_IF(s_dataSizeField == NULL,
+            "Unable to find dataSize field in android.backup.BackupDataInput.EntityHeader");
+
+    return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupDataInput",
+            g_methods, NELEM(g_methods));
+}
+
+}
diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp
index aab1233..9660326 100644
--- a/core/jni/android_backup_BackupDataOutput.cpp
+++ b/core/jni/android_backup_BackupDataOutput.cpp
@@ -28,7 +28,7 @@
 static jfieldID s_descriptorField = 0;
 
 static int
-ctor_native(JNIEnv* env, jobject This, jobject fileDescriptor)
+ctor_native(JNIEnv* env, jobject clazz, jobject fileDescriptor)
 {
     int err;
 
@@ -41,14 +41,57 @@
 }
 
 static void
-dtor_native(JNIEnv* env, jobject This, int fd)
+dtor_native(JNIEnv* env, jobject clazz, int w)
 {
-    delete (BackupDataWriter*)fd;
+    delete (BackupDataWriter*)w;
+}
+
+static jint
+writeEntityHeader_native(JNIEnv* env, jobject clazz, int w, jstring key, int dataSize)
+{
+    int err;
+    BackupDataWriter* writer = (BackupDataWriter*)w;
+
+    const char* keyUTF = env->GetStringUTFChars(key, NULL);
+    if (keyUTF == NULL) {
+        return -1;
+    }
+
+    err = writer->WriteEntityHeader(String8(keyUTF), dataSize);
+
+    env->ReleaseStringUTFChars(key, keyUTF);
+
+    return err;
+}
+
+static jint
+writeEntityData_native(JNIEnv* env, jobject clazz, int w, jbyteArray data, int size)
+{
+    int err;
+    BackupDataWriter* writer = (BackupDataWriter*)w;
+
+    if (env->GetArrayLength(data) > size) {
+        // size mismatch
+        return -1;
+    }
+
+    jbyte* dataBytes = env->GetByteArrayElements(data, NULL);
+    if (dataBytes == NULL) {
+        return -1;
+    }
+
+    err = writer->WriteEntityData(dataBytes, size);
+
+    env->ReleaseByteArrayElements(data, dataBytes, JNI_ABORT);
+
+    return err;
 }
 
 static const JNINativeMethod g_methods[] = {
     { "ctor", "(Ljava/io/FileDescriptor;)I", (void*)ctor_native },
     { "dtor", "(I)V", (void*)dtor_native },
+    { "writeEntityHeader_native", "(ILjava/lang/String;I)I", (void*)writeEntityHeader_native },
+    { "writeEntityData_native", "(I[BI)I", (void*)writeEntityData_native },
 };
 
 int register_android_backup_BackupDataOutput(JNIEnv* env)
diff --git a/core/jni/android_backup_FileBackupHelper.cpp b/core/jni/android_backup_FileBackupHelper.cpp
index 2ee064b..418db8a 100644
--- a/core/jni/android_backup_FileBackupHelper.cpp
+++ b/core/jni/android_backup_FileBackupHelper.cpp
@@ -25,6 +25,7 @@
 namespace android
 {
 
+// java.io.FileDescriptor
 static jfieldID s_descriptorField = 0;
 
 static int
@@ -34,7 +35,6 @@
     int err;
 
     // all parameters have already been checked against null
-    LOGD("oldState=%p newState=%p data=%p\n", oldState, newState, data);
     int oldStateFD = oldState != NULL ? env->GetIntField(oldState, s_descriptorField) : -1;
     int newStateFD = env->GetIntField(newState, s_descriptorField);
     BackupDataWriter* dataStream = (BackupDataWriter*)data;
@@ -74,8 +74,6 @@
 
 int register_android_backup_FileBackupHelper(JNIEnv* env)
 {
-    LOGD("register_android_backup_FileBackupHelper");
-
     jclass clazz;
 
     clazz = env->FindClass("java/io/FileDescriptor");
diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp
index 120f23d..8c9f875 100644
--- a/libs/utils/BackupData.cpp
+++ b/libs/utils/BackupData.cpp
@@ -327,7 +327,7 @@
     }
     size_t size = m_header.app.packageLen;
     char* buf = packageName->lockBuffer(size);
-    if (packageName == NULL) {
+    if (buf == NULL) {
         packageName->unlockBuffer();
         m_status = ENOMEM;
         return m_status;
diff --git a/tests/backup/src/com/android/backuptest/BackupTestAgent.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
index 11e520e..a370d69 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestAgent.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
@@ -30,7 +30,8 @@
     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
              ParcelFileDescriptor newState) {
         Log.d(TAG, "onBackup");
-        FileBackupHelper.performBackup(this, oldState, data, newState, new String[] {
+        FileBackupHelper helper = new FileBackupHelper(this);
+        helper.performBackup(oldState, data, newState, new String[] {
                     BackupTestActivity.FILE_NAME
                 });
     }