Merge change 5470 into donut

* changes:
  Use the same interface in the TTS engine interface for setLanguage and loadLanguage. Adding function to check the support level for a language in TTS engine interface.
diff --git a/Android.mk b/Android.mk
index 0e8793d..f32129e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -82,6 +82,7 @@
 	core/java/android/app/IWallpaperService.aidl \
 	core/java/android/app/IWallpaperServiceCallback.aidl \
 	core/java/android/backup/IBackupManager.aidl \
+	core/java/android/backup/IRestoreObserver.aidl \
 	core/java/android/backup/IRestoreSession.aidl \
 	core/java/android/bluetooth/IBluetoothA2dp.aidl \
 	core/java/android/bluetooth/IBluetoothDevice.aidl \
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 841e3df..01083f1 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -17,6 +17,7 @@
 package com.android.commands.bmgr;
 
 import android.backup.IBackupManager;
+import android.backup.IRestoreObserver;
 import android.backup.IRestoreSession;
 import android.backup.RestoreSet;
 import android.os.RemoteException;
@@ -180,6 +181,25 @@
         }
     }
 
+    class RestoreObserver extends IRestoreObserver.Stub {
+        boolean done;
+        public void restoreStarting(int numPackages) {
+            System.out.println("restoreStarting: " + numPackages + " packages");
+        }
+
+        public void onUpdate(int nowBeingRestored) {
+            System.out.println("onUpdate: " + nowBeingRestored);
+        }
+
+        public void restoreFinished(int error) {
+            System.out.println("restoreFinished: " + error);
+            synchronized (this) {
+                done = true;
+                this.notify();
+            }
+        }
+    }
+
     private void doRestore() {
         int token;
         try {
@@ -189,6 +209,8 @@
             return;
         }
 
+        RestoreObserver observer = new RestoreObserver();
+
         try {
             int curTransport = mBmgr.getCurrentTransport();
             mRestore = mBmgr.beginRestoreSession(curTransport);
@@ -200,7 +222,7 @@
             for (RestoreSet s : sets) {
                 if (s.token == token) {
                     System.out.println("Scheduling restore: " + s.name);
-                    mRestore.performRestore(token);
+                    mRestore.performRestore(token, observer);
                     break;
                 }
             }
@@ -209,6 +231,17 @@
             System.err.println(e.toString());
             System.err.println(BMGR_NOT_RUNNING_ERR);
         }
+
+        // now wait for it to be done
+        synchronized (observer) {
+            while (!observer.done) {
+                try {
+                    observer.wait();
+                } catch (InterruptedException ex) {
+                }
+            }
+        }
+        System.out.println("done");
     }
 
     private String nextArg() {
@@ -229,4 +262,4 @@
         System.err.println("       bmgr restore token#");
         System.err.println("       bmgr run");
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/app/BackupAgent.java b/core/java/android/app/BackupAgent.java
index e810775..0ac8a1e 100644
--- a/core/java/android/app/BackupAgent.java
+++ b/core/java/android/app/BackupAgent.java
@@ -67,7 +67,7 @@
      *                 here after writing the requested data to dataFd.
      */
     public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-             ParcelFileDescriptor newState);
+             ParcelFileDescriptor newState) throws IOException;
     
     /**
      * The application is being restored from backup, and should replace any
@@ -120,6 +120,9 @@
             BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
             try {
                 BackupAgent.this.onBackup(oldState, output, newState);
+            } catch (IOException ex) {
+                Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+                throw new RuntimeException(ex);
             } catch (RuntimeException ex) {
                 Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
                 throw ex;
diff --git a/core/java/android/backup/BackupHelperAgent.java b/core/java/android/backup/BackupHelperAgent.java
index 3720d50..5d0c4a2 100644
--- a/core/java/android/backup/BackupHelperAgent.java
+++ b/core/java/android/backup/BackupHelperAgent.java
@@ -34,7 +34,7 @@
 
     @Override
     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-             ParcelFileDescriptor newState) {
+             ParcelFileDescriptor newState) throws IOException {
         mDispatcher.performBackup(oldState, data, newState);
     }
 
diff --git a/core/java/android/backup/BackupHelperDispatcher.java b/core/java/android/backup/BackupHelperDispatcher.java
index b25c3e3..6ccb83e 100644
--- a/core/java/android/backup/BackupHelperDispatcher.java
+++ b/core/java/android/backup/BackupHelperDispatcher.java
@@ -19,7 +19,10 @@
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.IOException;
+import java.io.FileDescriptor;
 import java.util.TreeMap;
 import java.util.Map;
 
@@ -27,6 +30,11 @@
 public class BackupHelperDispatcher {
     private static final String TAG = "BackupHelperDispatcher";
 
+    private static class Header {
+        int chunkSize; // not including the header
+        String keyPrefix;
+    }
+
     TreeMap<String,BackupHelper> mHelpers = new TreeMap<String,BackupHelper>();
     
     public BackupHelperDispatcher() {
@@ -36,13 +44,63 @@
         mHelpers.put(keyPrefix, helper);
     }
 
-    /** TODO: Make this save and restore the key prefix. */
     public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-             ParcelFileDescriptor newState) {
-        // Write out the state files -- mHelpers is a TreeMap, so the order is well defined.
-        for (Map.Entry<String,BackupHelper> entry: mHelpers.entrySet()) {
-            data.setKeyPrefix(entry.getKey());
-            entry.getValue().performBackup(oldState, data, newState);
+             ParcelFileDescriptor newState) throws IOException {
+        // First, do the helpers that we've already done, since they're already in the state
+        // file.
+        int err;
+        Header header = new Header();
+        TreeMap<String,BackupHelper> helpers = (TreeMap<String,BackupHelper>)mHelpers.clone();
+        FileDescriptor oldStateFD = null;
+        FileDescriptor newStateFD = newState.getFileDescriptor();
+
+        if (oldState != null) {
+            oldStateFD = oldState.getFileDescriptor();
+            while ((err = readHeader_native(header, oldStateFD)) >= 0) {
+                if (err == 0) {
+                    BackupHelper helper = helpers.get(header.keyPrefix);
+                    Log.d(TAG, "handling existing helper '" + header.keyPrefix + "' " + helper);
+                    if (helper != null) {
+                        doOneBackup(oldState, data, newState, header, helper);
+                        helpers.remove(header.keyPrefix);
+                    } else {
+                        skipChunk_native(oldStateFD, header.chunkSize);
+                    }
+                }
+            }
+        }
+
+        // Then go through and do the rest that we haven't done.
+        for (Map.Entry<String,BackupHelper> entry: helpers.entrySet()) {
+            header.keyPrefix = entry.getKey();
+            Log.d(TAG, "handling new helper '" + header.keyPrefix + "'");
+            BackupHelper helper = entry.getValue();
+            doOneBackup(oldState, data, newState, header, helper);
+        }
+    }
+
+    private void doOneBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+            ParcelFileDescriptor newState, Header header, BackupHelper helper) 
+            throws IOException {
+        int err;
+        FileDescriptor newStateFD = newState.getFileDescriptor();
+
+        // allocate space for the header in the file
+        int pos = allocateHeader_native(header, newStateFD);
+        if (pos < 0) {
+            throw new IOException("allocateHeader_native failed (error " + pos + ")");
+        }
+
+        data.setKeyPrefix(header.keyPrefix);
+
+        // do the backup
+        helper.performBackup(oldState, data, newState);
+
+        // fill in the header (seeking back to pos).  The file pointer will be returned to
+        // where it was at the end of performBackup.  Header.chunkSize will not be filled in.
+        err = writeHeader_native(header, newStateFD, pos);
+        if (err != 0) {
+            throw new IOException("writeHeader_native failed (error " + err + ")");
         }
     }
 
@@ -83,5 +141,11 @@
             helper.writeRestoreSnapshot(newState);
         }
     }
+
+    private static native int readHeader_native(Header h, FileDescriptor fd);
+    private static native int skipChunk_native(FileDescriptor fd, int bytesToSkip);
+
+    private static native int allocateHeader_native(Header h, FileDescriptor fd);
+    private static native int writeHeader_native(Header h, FileDescriptor fd, int pos);
 }
 
diff --git a/core/java/android/backup/IRestoreObserver.aidl b/core/java/android/backup/IRestoreObserver.aidl
new file mode 100644
index 0000000..59e59fc
--- /dev/null
+++ b/core/java/android/backup/IRestoreObserver.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+/**
+ * Callback class for receiving progress reports during a restore operation.
+ *
+ * @hide
+ */
+interface IRestoreObserver {
+    /**
+     * The restore operation has begun.
+     *
+     * @param numPackages The total number of packages being processed in
+     *   this restore operation.
+     */
+    void restoreStarting(int numPackages);
+
+    /**
+     * An indication of which package is being restored currently, out of the
+     * total number provided in the restoreStarting() callback.  This method
+     * is not guaranteed to be called.
+     *
+     * @param nowBeingRestored The index, between 1 and the numPackages parameter
+     *   to the restoreStarting() callback, of the package now being restored.
+     */
+    void onUpdate(int nowBeingRestored);
+
+    /**
+     * The restore operation has completed.
+     *
+     * @param error Zero on success; a nonzero error code if the restore operation
+     *   as a whole failed.
+     */
+    void restoreFinished(int error);
+}
diff --git a/core/java/android/backup/IRestoreSession.aidl b/core/java/android/backup/IRestoreSession.aidl
index 6bca865..ac01c2d 100644
--- a/core/java/android/backup/IRestoreSession.aidl
+++ b/core/java/android/backup/IRestoreSession.aidl
@@ -17,6 +17,7 @@
 package android.backup;
 
 import android.backup.RestoreSet;
+import android.backup.IRestoreObserver;
 
 /**
  * Binder interface used by clients who wish to manage a restore operation.  Every
@@ -41,8 +42,10 @@
      *
      * @param token The token from {@link getAvailableRestoreSets()} corresponding to
      *   the restore set that should be used.
+     * @param observer If non-null, this binder points to an object that will receive
+     *   progress callbacks during the restore operation.
      */
-    int performRestore(int token);
+    int performRestore(int token, IRestoreObserver observer);
 
     /**
      * End this restore session.  After this method is called, the IRestoreSession binder
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index b328869..888cb11 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -120,7 +120,8 @@
 	com_android_internal_graphics_NativeUtils.cpp \
 	android_backup_BackupDataInput.cpp \
 	android_backup_BackupDataOutput.cpp \
-	android_backup_FileBackupHelperBase.cpp
+	android_backup_FileBackupHelperBase.cpp \
+	android_backup_BackupHelperDispatcher.cpp
 
 LOCAL_C_INCLUDES += \
 	$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 7350348..c815301 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_FileBackupHelperBase(JNIEnv *env);
+extern int register_android_backup_BackupHelperDispatcher(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_FileBackupHelperBase),
+    REG_JNI(register_android_backup_BackupHelperDispatcher),
 };
 
 /*
diff --git a/core/jni/android_backup_BackupHelperDispatcher.cpp b/core/jni/android_backup_BackupHelperDispatcher.cpp
new file mode 100644
index 0000000..24d529b
--- /dev/null
+++ b/core/jni/android_backup_BackupHelperDispatcher.cpp
@@ -0,0 +1,251 @@
+/*
+ * 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 "BackupHelperDispatcher_native"
+#include <utils/Log.h>
+
+#include "JNIHelp.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+
+#define VERSION_1_HEADER 0x01706c48  // 'Hlp'1 little endian
+
+namespace android
+{
+
+struct chunk_header_v1 {
+    int headerSize;
+    int version;
+    int dataSize; // corresponds to Header.chunkSize
+    int nameLength; // not including the NULL terminator, which is not written to the file
+};
+
+// java.io.FileDescriptor
+static jfieldID s_descriptorField = 0;
+static jfieldID s_chunkSizeField = 0;
+static jfieldID s_keyPrefixField = 0;
+
+static int
+readHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj)
+{
+    chunk_header_v1 flattenedHeader;
+    int fd;
+    ssize_t amt;
+    String8 keyPrefix;
+    char* buf;
+
+    fd = env->GetIntField(fdObj, s_descriptorField);
+
+    amt = read(fd, &flattenedHeader.headerSize, sizeof(flattenedHeader.headerSize));
+    if (amt != sizeof(flattenedHeader.headerSize)) {
+        return -1;
+    }
+
+    int remainingHeader = flattenedHeader.headerSize - sizeof(flattenedHeader.headerSize);
+
+    if (flattenedHeader.headerSize < (int)sizeof(chunk_header_v1)) {
+        LOGW("Skipping unknown header: %d bytes", flattenedHeader.headerSize);
+        if (remainingHeader > 0) {
+            lseek(fd, remainingHeader, SEEK_CUR);
+            // >0 means skip this chunk
+            return 1;
+        }
+    }
+
+    amt = read(fd, &flattenedHeader.version,
+            sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize));
+    if (amt <= 0) {
+        LOGW("Failed reading chunk header");
+        return -1;
+    }
+    remainingHeader -= sizeof(chunk_header_v1)-sizeof(flattenedHeader.headerSize);
+
+    if (flattenedHeader.version != VERSION_1_HEADER) {
+        LOGW("Skipping unknown header version: 0x%08x, %d bytes", flattenedHeader.version,
+                flattenedHeader.headerSize);
+        if (remainingHeader > 0) {
+            lseek(fd, remainingHeader, SEEK_CUR);
+            // >0 means skip this chunk
+            return 1;
+        }
+    }
+
+    if (flattenedHeader.dataSize < 0 || flattenedHeader.nameLength < 0 ||
+            remainingHeader < flattenedHeader.nameLength) {
+        LOGW("Malformed V1 header remainingHeader=%d dataSize=%d nameLength=%d", remainingHeader,
+                flattenedHeader.dataSize, flattenedHeader.nameLength);
+        return -1;
+    }
+
+    buf = keyPrefix.lockBuffer(flattenedHeader.nameLength);
+    if (buf == NULL) {
+        LOGW("unable to allocate %d bytes", flattenedHeader.nameLength);
+        return -1;
+    }
+
+    amt = read(fd, buf, flattenedHeader.nameLength);
+
+    keyPrefix.unlockBuffer(flattenedHeader.nameLength);
+
+    remainingHeader -= flattenedHeader.nameLength;
+
+    LOGD("remainingHeader=%d", remainingHeader);
+
+    if (remainingHeader > 0) {
+        lseek(fd, remainingHeader, SEEK_CUR);
+    }
+
+    env->SetIntField(headerObj, s_chunkSizeField, flattenedHeader.dataSize);
+    env->SetObjectField(headerObj, s_keyPrefixField, env->NewStringUTF(keyPrefix.string()));
+
+    return 0;
+}
+
+static int
+skipChunk_native(JNIEnv* env, jobject clazz, jobject fdObj, jint bytesToSkip)
+{
+    int fd;
+
+    fd = env->GetIntField(fdObj, s_descriptorField);
+
+    lseek(fd, bytesToSkip, SEEK_CUR);
+
+    return 0;
+}
+
+static int
+padding_len(int len)
+{
+    len = len % 4;
+    return len == 0 ? len : 4 - len;
+}
+
+static int
+allocateHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj)
+{
+    int pos;
+    jstring nameObj;
+    int nameLength;
+    int namePadding;
+    int headerSize;
+    int fd;
+
+    fd = env->GetIntField(fdObj, s_descriptorField);
+
+    nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
+
+    nameLength = env->GetStringUTFLength(nameObj);
+    namePadding = padding_len(nameLength);
+
+    headerSize = sizeof(chunk_header_v1) + nameLength + namePadding;
+
+    pos = lseek(fd, 0, SEEK_CUR);
+
+    lseek(fd, headerSize, SEEK_CUR);
+    
+    return pos;
+}
+
+static int
+writeHeader_native(JNIEnv* env, jobject clazz, jobject headerObj, jobject fdObj, jint pos)
+{
+    int err;
+    chunk_header_v1 header;
+    int fd;
+    int namePadding;
+    int prevPos;
+    jstring nameObj;
+    const char* buf;
+
+    fd = env->GetIntField(fdObj, s_descriptorField);
+    prevPos = lseek(fd, 0, SEEK_CUR);
+
+    nameObj = (jstring)env->GetObjectField(headerObj, s_keyPrefixField);
+    header.nameLength = env->GetStringUTFLength(nameObj);
+    namePadding = padding_len(header.nameLength);
+
+    header.headerSize = sizeof(chunk_header_v1) + header.nameLength + namePadding;
+    header.version = VERSION_1_HEADER;
+
+    lseek(fd, pos, SEEK_SET);
+    err = write(fd, &header, sizeof(chunk_header_v1));
+    if (err != sizeof(chunk_header_v1)) {
+        return errno;
+    }
+
+    buf = env->GetStringUTFChars(nameObj, NULL);
+    err = write(fd, buf, header.nameLength);
+    env->ReleaseStringUTFChars(nameObj, buf);
+    if (err != header.nameLength) {
+        return errno;
+    }
+
+    if (namePadding != 0) {
+        int zero = 0;
+        err = write(fd, &zero, namePadding);
+        if (err != namePadding) {
+            return errno;
+        }
+    }
+
+    lseek(fd, prevPos, SEEK_SET);
+    return 0;
+}
+
+static const JNINativeMethod g_methods[] = {
+    { "readHeader_native",
+       "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
+       (void*)readHeader_native },
+    { "skipChunk_native",
+        "(Ljava/io/FileDescriptor;I)I",
+        (void*)skipChunk_native },
+    { "allocateHeader_native",
+        "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;)I",
+        (void*)allocateHeader_native },
+    { "writeHeader_native",
+       "(Landroid/backup/BackupHelperDispatcher$Header;Ljava/io/FileDescriptor;I)I",
+       (void*)writeHeader_native },
+};
+
+int register_android_backup_BackupHelperDispatcher(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");
+    
+    clazz = env->FindClass("android/backup/BackupHelperDispatcher$Header");
+    LOG_FATAL_IF(clazz == NULL,
+            "Unable to find class android.backup.BackupHelperDispatcher.Header");
+    s_chunkSizeField = env->GetFieldID(clazz, "chunkSize", "I");
+    LOG_FATAL_IF(s_chunkSizeField == NULL,
+            "Unable to find chunkSize field in android.backup.BackupHelperDispatcher.Header");
+    s_keyPrefixField = env->GetFieldID(clazz, "keyPrefix", "Ljava/lang/String;");
+    LOG_FATAL_IF(s_keyPrefixField == NULL,
+            "Unable to find keyPrefix field in android.backup.BackupHelperDispatcher.Header");
+    
+    return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupHelperDispatcher",
+            g_methods, NELEM(g_methods));
+}
+
+}
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index c890b0f..aee0ed7 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -269,9 +269,9 @@
 void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
                                               jint pid, jint pri)
 {
-    if (pri == ANDROID_PRIORITY_BACKGROUND) {
+    if (pri >= ANDROID_PRIORITY_BACKGROUND) {
         add_pid_to_cgroup(pid, ANDROID_TGROUP_BG_NONINTERACT);
-    } else if (getpriority(PRIO_PROCESS, pid) == ANDROID_PRIORITY_BACKGROUND) {
+    } else if (getpriority(PRIO_PROCESS, pid) >= ANDROID_PRIORITY_BACKGROUND) {
         add_pid_to_cgroup(pid, ANDROID_TGROUP_DEFAULT);
     }
 
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index e131c6eb..bc2eaed 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -47,6 +47,7 @@
 import android.util.SparseArray;
 
 import android.backup.IBackupManager;
+import android.backup.IRestoreObserver;
 import android.backup.IRestoreSession;
 import android.backup.BackupManager;
 import android.backup.RestoreSet;
@@ -81,6 +82,7 @@
     private static final int MSG_RUN_BACKUP = 1;
     private static final int MSG_RUN_FULL_BACKUP = 2;
     private static final int MSG_RUN_RESTORE = 3;
+    private static final String RESTORE_OBSERVER_KEY = "_resOb";
 
     // Timeout interval for deciding that a bind or clear-data has taken too long
     static final long TIMEOUT_INTERVAL = 10 * 1000;
@@ -133,6 +135,16 @@
     private IBackupTransport mLocalTransport, mGoogleTransport;
     private RestoreSession mActiveRestoreSession;
 
+    private class RestoreParams {
+        public IBackupTransport transport;
+        public IRestoreObserver observer;
+
+        RestoreParams(IBackupTransport _transport, IRestoreObserver _obs) {
+            transport = _transport;
+            observer = _obs;
+        }
+    }
+
     // Where we keep our journal files and other bookkeeping
     private File mBaseStateDir;
     private File mDataDir;
@@ -336,8 +348,8 @@
             case MSG_RUN_RESTORE:
             {
                 int token = msg.arg1;
-                IBackupTransport transport = (IBackupTransport)msg.obj;
-                (new PerformRestoreThread(transport, token)).start();
+                RestoreParams params = (RestoreParams)msg.obj;
+                (new PerformRestoreThread(params.transport, params.observer, token)).start();
                 break;
             }
             }
@@ -748,6 +760,7 @@
 
     class PerformRestoreThread extends Thread {
         private IBackupTransport mTransport;
+        private IRestoreObserver mObserver;
         private int mToken;
         private RestoreSet mImage;
         private File mStateDir;
@@ -762,8 +775,10 @@
             }
         }
 
-        PerformRestoreThread(IBackupTransport transport, int restoreSetToken) {
+        PerformRestoreThread(IBackupTransport transport, IRestoreObserver observer,
+                int restoreSetToken) {
             mTransport = transport;
+            mObserver = observer;
             mToken = restoreSetToken;
 
             try {
@@ -792,6 +807,8 @@
              * 4. shut down the transport
              */
 
+            int error = -1; // assume error
+
             // build the set of apps to restore
             try {
                 RestoreSet[] images = mTransport.getAvailableRestoreSets();
@@ -818,6 +835,18 @@
                 List<PackageInfo> agentPackages = allAgentPackages();
                 restorePackages.addAll(agentPackages);
 
+                // let the observer know that we're running
+                if (mObserver != null) {
+                    try {
+                        // !!! TODO: get an actual count from the transport after
+                        // its startRestore() runs?
+                        mObserver.restoreStarting(restorePackages.size());
+                    } catch (RemoteException e) {
+                        Log.d(TAG, "Restore observer died at restoreStarting");
+                        mObserver = null;
+                    }
+                }
+
                 // STOPSHIP TODO: pick out the set for this token (instead of images[0])
                 long token = images[0].token;
                 if (!mTransport.startRestore(token, restorePackages.toArray(new PackageInfo[0]))) {
@@ -845,6 +874,7 @@
                         mPackageManager, agentPackages);
                 processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
 
+                int count = 0;
                 for (;;) {
                     packageName = mTransport.nextRestorePackage();
                     if (packageName == null) {
@@ -855,6 +885,16 @@
                         break;
                     }
 
+                    if (mObserver != null) {
+                        ++count;
+                        try {
+                            mObserver.onUpdate(count);
+                        } catch (RemoteException e) {
+                            Log.d(TAG, "Restore observer died in onUpdate");
+                            mObserver = null;
+                        }
+                    }
+
                     Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
                     if (metaInfo == null) {
                         Log.e(TAG, "Missing metadata for " + packageName);
@@ -898,6 +938,9 @@
                         mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
                     }
                 }
+
+                // if we get this far, report success to the observer
+                error = 0;
             } catch (NameNotFoundException e) {
                 // STOPSHIP TODO: Handle the failure somehow?
                 Log.e(TAG, "Invalid paackage restoring data", e);
@@ -910,6 +953,14 @@
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error finishing restore", e);
                 }
+
+                if (mObserver != null) {
+                    try {
+                        mObserver.restoreFinished(error);
+                    } catch (RemoteException e) {
+                        Log.d(TAG, "Restore observer died at restoreFinished");
+                    }
+                }
             }
         }
 
@@ -1147,14 +1198,15 @@
             }
         }
 
-        public int performRestore(int token) throws android.os.RemoteException {
+        public int performRestore(int token, IRestoreObserver observer)
+                throws android.os.RemoteException {
             mContext.enforceCallingPermission("android.permission.BACKUP", "performRestore");
 
             if (mRestoreSets != null) {
                 for (int i = 0; i < mRestoreSets.length; i++) {
                     if (token == mRestoreSets[i].token) {
-                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE,
-                                mRestoreTransport);
+                        Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+                        msg.obj = new RestoreParams(mRestoreTransport, observer);
                         msg.arg1 = token;
                         mBackupHandler.sendMessage(msg);
                         return 0;
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index 62a5d65..890ea63 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -78,6 +78,7 @@
     protected static final String[] RAW_PROJECTION = new String[] {
         "pdu",
         "sequence",
+        "destination_port",
     };
 
     static final int MAIL_SEND_SMS = 1;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 2d43e0de..ecdc8f6 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -168,8 +168,8 @@
         int index = 0;
         int msgType;
 
-        int sourcePort;
-        int destinationPort;
+        int sourcePort = 0;
+        int destinationPort = 0;
 
         msgType = pdu[index++];
         if (msgType != 0){
@@ -179,11 +179,14 @@
         totalSegments = pdu[index++]; // >=1
         segment = pdu[index++]; // >=0
 
-        //process WDP segment
-        sourcePort = (0xFF & pdu[index++]) << 8;
-        sourcePort |= 0xFF & pdu[index++];
-        destinationPort = (0xFF & pdu[index++]) << 8;
-        destinationPort |= 0xFF & pdu[index++];
+        // Only the first segment contains sourcePort and destination Port
+        if (segment == 0) {
+            //process WDP segment
+            sourcePort = (0xFF & pdu[index++]) << 8;
+            sourcePort |= 0xFF & pdu[index++];
+            destinationPort = (0xFF & pdu[index++]) << 8;
+            destinationPort |= 0xFF & pdu[index++];
+        }
 
         // Lookup all other related parts
         StringBuilder where = new StringBuilder("reference_number =");
@@ -224,6 +227,11 @@
             for (int i = 0; i < cursorCount; i++) {
                 cursor.moveToNext();
                 int cursorSequence = (int)cursor.getLong(sequenceColumn);
+                // Read the destination port from the first segment
+                if (cursorSequence == 0) {
+                    int destinationPortColumn = cursor.getColumnIndex("destination_port");
+                    destinationPort = (int)cursor.getLong(destinationPortColumn);
+                }
                 pdus[cursorSequence] = HexDump.hexStringToByteArray(
                         cursor.getString(pduColumn));
             }
diff --git a/tests/backup/src/com/android/backuptest/BackupTestAgent.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
index c6acc66..6acd90c 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestAgent.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
@@ -23,6 +23,8 @@
 {
     public void onCreate() {
         addHelper("data_files", new FileBackupHelper(this, BackupTestActivity.FILE_NAME));
+        addHelper("more_data_files", new FileBackupHelper(this, "another_file.txt", "3.txt",
+                    "empty.txt"));
     }
 }
 
diff --git a/tests/backup/test_backup.sh b/tests/backup/test_backup.sh
new file mode 100755
index 0000000..6ef5dff
--- /dev/null
+++ b/tests/backup/test_backup.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+#adb kill-server
+
+# set the transport
+adb shell bmgr transport 1
+
+# load up the three files
+adb shell "rm /data/data/com.android.backuptest/files/* ; \
+           mkdir /data/data/com.android.backuptest ; \
+           mkdir /data/data/com.android.backuptest/files ; \
+           echo -n first file > /data/data/com.android.backuptest/files/file.txt ; \
+           echo -n asdf > /data/data/com.android.backuptest/files/another_file.txt ; \
+           echo -n 3 > /data/data/com.android.backuptest/files/3.txt ; \
+           echo -n "" > /data/data/com.android.backuptest/files/empty.txt ; \
+"
+
+# say that the data has changed
+adb shell bmgr backup com.android.backuptest
+
+# run the backup
+adb shell bmgr run
diff --git a/tests/backup/test_restore.sh b/tests/backup/test_restore.sh
new file mode 100755
index 0000000..f3d581e
--- /dev/null
+++ b/tests/backup/test_restore.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+function check_file
+{
+    data=$(adb shell cat /data/data/com.android.backuptest/files/$1)
+    if [ "$data" = "$2" ] ; then
+        echo "$1 has correct value [$2]"
+    else
+        echo $1 is INCORRECT
+        echo "   value:    [$data]"
+        echo "   expected: [$2]"
+    fi
+}
+
+# delete the old data
+echo --- Previous files
+adb shell "ls -l /data/data/com.android.backuptest/files"
+adb shell "rm /data/data/com.android.backuptest/files/*"
+echo --- Erased files
+adb shell "ls -l /data/data/com.android.backuptest/files"
+echo ---
+
+echo
+echo
+echo
+
+# run the restore
+adb shell bmgr restore 0
+
+echo
+echo
+echo
+
+# check the results
+check_file file.txt "first file"
+check_file another_file.txt "asdf"
+check_file 3.txt "3"
+check_file empty.txt ""
+
+echo
+echo
+echo
+echo --- Restored files
+adb shell "ls -l /data/data/com.android.backuptest/files"
+echo ---
+echo