Merge change 3828 into donut

* changes:
  Renaming the Tts class to TextToSpeech for clarity. Switching to HashMap for parameter lists to pass key-value parameter pairs. Using TODO instead of FIXME in comments. Removing methods that don't belong anymore to the TTS interface now the feature becomes part of the platform.
diff --git a/api/current.xml b/api/current.xml
index cb348e1..f30c505 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -3518,17 +3518,6 @@
  visibility="public"
 >
 </field>
-<field name="donut_resource_pad30"
- type="int"
- transient="false"
- volatile="false"
- value="16843394"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="donut_resource_pad4"
  type="int"
  transient="false"
@@ -6840,6 +6829,17 @@
  visibility="public"
 >
 </field>
+<field name="queryAfterZeroResults"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843394"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="radioButtonStyle"
  type="int"
  transient="false"
@@ -62837,7 +62837,7 @@
  type="float"
  transient="false"
  volatile="false"
- value="0.0010f"
+ value="0.001f"
  static="true"
  final="true"
  deprecated="not deprecated"
diff --git a/cmds/keystore/commands.c b/cmds/keystore/commands.c
index 7474d81..e53cece 100644
--- a/cmds/keystore/commands.c
+++ b/cmds/keystore/commands.c
@@ -40,7 +40,7 @@
     reply[0]=0;
     while ((de = readdir(d))) {
         if (de->d_type != DT_REG) continue;
-        strlcat(reply, " ", REPLY_MAX);
+        if (reply[0] != 0) strlcat(reply, " ", REPLY_MAX);
         if (strlcat(reply, de->d_name, REPLY_MAX) >= REPLY_MAX) {
             LOGE("reply is too long(too many files under '%s'\n", dir);
             return -1;
diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c
index dbb62b37..5193b3d 100644
--- a/cmds/keystore/keystore.c
+++ b/cmds/keystore/keystore.c
@@ -113,7 +113,7 @@
     unsigned i;
     unsigned n = 0;
     unsigned short count;
-    int ret = -1;
+    short ret = -1;
 
     /* default reply is "" */
     reply[0] = 0;
@@ -139,7 +139,7 @@
                 LOGE("%s requires %d arguments (%d given)\n",
                      cmds[i].name, cmds[i].numargs, n);
             } else {
-                ret = cmds[i].func(arg + 1, reply);
+                ret = (short) cmds[i].func(arg + 1, reply);
             }
             goto done;
         }
@@ -148,24 +148,26 @@
 
 done:
     if (reply[0]) {
-        n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply);
+        strlcpy(cmd, reply, BUFFER_MAX);
+        count = strlen(cmd);
     } else {
-        n = snprintf(cmd, BUFFER_MAX, "%d", ret);
+        count = 0;
     }
-    if (n > BUFFER_MAX) n = BUFFER_MAX;
-    count = n;
-
-    if (writex(s, &count, sizeof(count))) return -1;
-    if (writex(s, cmd, count)) return -1;
+    if (writex(s, &ret, sizeof(ret))) return -1;
+    if (ret == 0) {
+        if (writex(s, &count, sizeof(count))) return -1;
+        if (writex(s, cmd, count)) return -1;
+    }
 
     return 0;
 }
 
 int shell_command(const int argc, const char **argv)
 {
-    int fd, i, r;
+    int fd, i;
+    short ret;
     unsigned short count;
-    char cmd[BUFFER_MAX]="";
+    char buf[BUFFER_MAX]="";
 
     fd = socket_local_client(SOCKET_PATH,
                              ANDROID_SOCKET_NAMESPACE_RESERVED,
@@ -175,19 +177,24 @@
         exit(1);
     }
     for(i = 0; i < argc; i++) {
-        if (i > 0) strlcat(cmd, " ", BUFFER_MAX);
-        if(strlcat(cmd, argv[i], BUFFER_MAX) >= BUFFER_MAX) {
+        if (i > 0) strlcat(buf, " ", BUFFER_MAX);
+        if(strlcat(buf, argv[i], BUFFER_MAX) >= BUFFER_MAX) {
             fprintf(stderr, "Arguments are too long\n");
             exit(1);
         }
     }
-    count = strlen(cmd);
+    count = strlen(buf);
     if (writex(fd, &count, sizeof(count))) return -1;
-    if (writex(fd, cmd, strlen(cmd))) return -1;
-    if (readx(fd, &count, sizeof(count))) return -1;
-    if (readx(fd, cmd, count)) return -1;
-    cmd[count]=0;
-    fprintf(stdout, "%s\n", cmd);
+    if (writex(fd, buf, strlen(buf))) return -1;
+    if (readx(fd, &ret, sizeof(ret))) return -1;
+    if (ret == 0) {
+        if (readx(fd, &count, sizeof(count))) return -1;
+        if (readx(fd, buf, count)) return -1;
+        buf[count]=0;
+        fprintf(stdout, "%s\n", buf);
+    } else {
+        fprintf(stderr, "Failed, please check log!\n");
+    }
     return 0;
 }
 
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 3f0aa95..820f192 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1402,6 +1402,16 @@
      * @hide For internal use, not part of the public API.
      */
     public final static String SUGGEST_COLUMN_BACKGROUND_COLOR = "suggest_background_color";
+    
+    /**
+     * Column name for suggestions cursor. <i>Optional.</i> This column is used to specify
+     * that a spinner should be shown in lieu of an icon2 while the shortcut of this suggestion
+     * is being refreshed.
+     * 
+     * @hide Pending API council approval.
+     */
+    public final static String SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING =
+            "suggest_spinner_while_refreshing";
 
     /**
      * Column value for suggestion column {@link #SUGGEST_COLUMN_SHORTCUT_ID} when a suggestion
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
index 6e8bd78..8c5cee3 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -20,16 +20,19 @@
 import android.content.Context;
 import android.content.res.Resources.NotFoundException;
 import android.database.Cursor;
+import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.server.search.SearchableInfo;
 import android.text.Html;
 import android.text.TextUtils;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.AbsListView;
 import android.widget.ImageView;
 import android.widget.ResourceCursorAdapter;
 import android.widget.TextView;
@@ -255,7 +258,7 @@
      */
     @Override
     public View newView(Context context, Cursor cursor, ViewGroup parent) {
-        View v = super.newView(context, cursor, parent);
+        View v = new SuggestionItemView(context, cursor);
         v.setTag(new ChildViewCache(v));
         return v;
     }
@@ -303,7 +306,7 @@
         if (backgroundColor == 0) {
             backgroundColor = mDefaultBackgroundColor;
         }
-        view.setBackgroundColor(backgroundColor);
+        ((SuggestionItemView)view).setColor(backgroundColor);
 
         final boolean isHtml = mFormatCol > 0 && "html".equals(cursor.getString(mFormatCol));
         setViewText(cursor, views.mText1, mText1Col, isHtml);
@@ -506,4 +509,68 @@
         return cursor.getString(col);
     }
 
+    /**
+     * A parent viewgroup class which holds the actual suggestion item as a child.
+     *
+     * The sole purpose of this class is to draw the given background color when the item is in
+     * normal state and not draw the background color when it is pressed, so that when pressed the
+     * list view's selection highlight will be displayed properly (if we draw our background it
+     * draws on top of the list view selection highlight).
+     */
+    private class SuggestionItemView extends ViewGroup {
+        private int mBackgroundColor;  // the background color to draw in normal state.
+        private View mView;  // the suggestion item's view.
+
+        protected SuggestionItemView(Context context, Cursor cursor) {
+            // Initialize ourselves
+            super(context);
+
+            // For our layout use the default list item height from the current theme.
+            TypedValue lineHeight = new TypedValue();
+            context.getTheme().resolveAttribute(
+                    com.android.internal.R.attr.searchResultListItemHeight, lineHeight, true);
+            DisplayMetrics metrics = new DisplayMetrics();
+            metrics.setToDefaults();
+            AbsListView.LayoutParams layout = new AbsListView.LayoutParams(
+                    AbsListView.LayoutParams.FILL_PARENT,
+                    (int)lineHeight.getDimension(metrics));
+
+            setLayoutParams(layout);
+
+            // Initialize the child view
+            mView = SuggestionsAdapter.super.newView(context, cursor, this);
+            if (mView != null) {
+                addView(mView, layout.width, layout.height);
+                mView.setVisibility(View.VISIBLE);
+            }
+        }
+
+        public void setColor(int backgroundColor) {
+            mBackgroundColor = backgroundColor;
+        }
+
+        @Override
+        public void dispatchDraw(Canvas canvas) {
+            if (!isPressed()) {
+                canvas.drawColor(mBackgroundColor);
+            }
+            super.dispatchDraw(canvas);
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            if (mView != null) {
+                mView.measure(widthMeasureSpec, heightMeasureSpec);
+            }
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+            if (mView != null) {
+                mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+            }
+        }
+    }
+
 }
diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java
index c3b6a02..8df7eae 100644
--- a/core/java/android/backup/BackupManager.java
+++ b/core/java/android/backup/BackupManager.java
@@ -45,7 +45,7 @@
     /**
      * Defined backup transports understood by {@link IBackupManager.selectBackupTransport}.
      */
-    public static final int TRANSPORT_ADB = 1;
+    public static final int TRANSPORT_LOCAL = 1;
     public static final int TRANSPORT_GOOGLE = 2;
 
     /**
diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java
index c083142..4df7368 100644
--- a/core/java/android/server/search/SearchableInfo.java
+++ b/core/java/android/server/search/SearchableInfo.java
@@ -66,6 +66,7 @@
     private final int mSearchInputType;
     private final int mSearchImeOptions;
     private final boolean mIncludeInGlobalSearch;
+    private final boolean mQueryAfterZeroResults;
     private final String mSuggestAuthority;
     private final String mSuggestPath;
     private final String mSuggestSelection;
@@ -276,6 +277,8 @@
                 EditorInfo.IME_ACTION_SEARCH);
         mIncludeInGlobalSearch = a.getBoolean(
                 com.android.internal.R.styleable.Searchable_includeInGlobalSearch, false);
+        mQueryAfterZeroResults = a.getBoolean(
+                com.android.internal.R.styleable.Searchable_queryAfterZeroResults, false);
 
         mSuggestAuthority = a.getString(
                 com.android.internal.R.styleable.Searchable_searchSuggestAuthority);
@@ -637,6 +640,17 @@
     }
 
     /**
+     * Checks whether this searchable activity should be invoked after a query returned zero
+     * results.
+     *
+     * @return The value of the <code>queryAfterZeroResults</code> attribute,
+     *         or <code>false</code> if the attribute is not set.
+     */
+    public boolean queryAfterZeroResults() {
+        return mQueryAfterZeroResults;
+    }
+
+    /**
      * Support for parcelable and aidl operations.
      */
     public static final Parcelable.Creator<SearchableInfo> CREATOR
@@ -667,6 +681,7 @@
         mSearchInputType = in.readInt();
         mSearchImeOptions = in.readInt();
         mIncludeInGlobalSearch = in.readInt() != 0;
+        mQueryAfterZeroResults = in.readInt() != 0;
 
         mSuggestAuthority = in.readString();
         mSuggestPath = in.readString();
@@ -702,6 +717,7 @@
         dest.writeInt(mSearchInputType);
         dest.writeInt(mSearchImeOptions);
         dest.writeInt(mIncludeInGlobalSearch ? 1 : 0);
+        dest.writeInt(mQueryAfterZeroResults ? 1 : 0);
         
         dest.writeString(mSuggestAuthority);
         dest.writeString(mSuggestPath);
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index f376ce5..585ce3d 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -123,10 +123,6 @@
 
     private AutoCompleteTextView.ListSelectorHider mHideSelector;
 
-    // Indicates whether this AutoCompleteTextView is attached to a window or not
-    // The widget is attached to a window when mAttachCount > 0
-    private int mAttachCount;
-
     private AutoCompleteTextView.PassThroughClickListener mPassThroughClickListener;
 
     public AutoCompleteTextView(Context context) {
@@ -960,7 +956,8 @@
 
     /** {@inheritDoc} */
     public void onFilterComplete(int count) {
-        if (mAttachCount <= 0) return;
+        // Not attached to window, don't update drop-down
+        if (getWindowVisibility() == View.GONE) return;
 
         /*
          * This checks enoughToFilter() again because filtering requests
@@ -999,13 +996,11 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        mAttachCount++;
     }
 
     @Override
     protected void onDetachedFromWindow() {
         dismissDropDown();
-        mAttachCount--;
         super.onDetachedFromWindow();
     }
 
diff --git a/core/java/com/android/internal/backup/AdbTransport.java b/core/java/com/android/internal/backup/AdbTransport.java
deleted file mode 100644
index 8d3bd1c..0000000
--- a/core/java/com/android/internal/backup/AdbTransport.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.android.internal.backup;
-
-import android.backup.RestoreSet;
-import android.content.pm.PackageInfo;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-
-/**
- * Backup transport for full backup over adb.  This transport pipes everything to
- * a file in a known location in /cache, which 'adb backup' then pulls to the desktop
- * (deleting it afterwards).
- */
-
-public class AdbTransport extends IBackupTransport.Stub {
-
-    public long requestBackupTime() throws RemoteException {
-        return 0;
-    }
-
-    public int startSession() throws RemoteException {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    public int endSession() throws RemoteException {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
-            throws RemoteException {
-        // TODO Auto-generated method stub
-        return 0;
-    }
-
-    // Restore handling
-    public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
-        RestoreSet[] set = new RestoreSet[1];
-        set[0].device = "USB";
-        set[0].name = "adb";
-        set[0].token = 0;
-        return set;
-    }
-
-    public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
-        // !!! TODO: real implementation
-        return new PackageInfo[0];
-    }
-
-    public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor data)
-            throws android.os.RemoteException {
-        // !!! TODO: real implementation
-        return 0;
-    }
-}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
new file mode 100644
index 0000000..62fba4a
--- /dev/null
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -0,0 +1,140 @@
+package com.android.internal.backup;
+
+import android.backup.RestoreSet;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Backup transport for stashing stuff into a known location on disk, and
+ * later restoring from there.  For testing only.
+ */
+
+public class LocalTransport extends IBackupTransport.Stub {
+    private static final String TAG = "LocalTransport";
+    private static final String DATA_FILE_NAME = "data";
+
+    private Context mContext;
+    private PackageManager mPackageManager;
+    private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
+    private FileFilter mDirFileFilter = new FileFilter() {
+        public boolean accept(File f) {
+            return f.isDirectory();
+        }
+    };
+
+
+    public LocalTransport(Context context) {
+        mContext = context;
+        mPackageManager = context.getPackageManager();
+    }
+
+    public long requestBackupTime() throws RemoteException {
+        // any time is a good time for local backup
+        return 0;
+    }
+
+    public int startSession() throws RemoteException {
+        return 0;
+    }
+
+    public int endSession() throws RemoteException {
+        return 0;
+    }
+
+    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
+            throws RemoteException {
+        File packageDir = new File(mDataDir, packageInfo.packageName);
+        File imageFileName = new File(packageDir, DATA_FILE_NAME);
+
+        //!!! TODO: process the (partial) update into the persistent restore set:
+        
+        // Parse out the existing image file into the key/value map
+
+        // Parse out the backup data into the key/value updates
+
+        // Apply the backup key/value updates to the image
+
+        // Write out the image in the canonical format
+
+        return -1;
+    }
+
+    // Restore handling
+    public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
+        // one hardcoded restore set
+        RestoreSet[] set = new RestoreSet[1];
+        set[0].device = "flash";
+        set[0].name = "Local disk image";
+        set[0].token = 0;
+        return set;
+    }
+
+    public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
+        // the available packages are the extant subdirs of mDatadir
+        File[] packageDirs = mDataDir.listFiles(mDirFileFilter);
+        ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>();
+        for (File dir : packageDirs) {
+            try {
+                PackageInfo pkg = mPackageManager.getPackageInfo(dir.getName(),
+                        PackageManager.GET_SIGNATURES);
+                if (pkg != null) {
+                    packages.add(pkg);
+                }
+            } catch (NameNotFoundException e) {
+                // restore set contains data for a package not installed on the
+                // phone -- just ignore it.
+            }
+        }
+
+        Log.v(TAG, "Built app set of " + packages.size() + " entries:");
+        for (PackageInfo p : packages) {
+            Log.v(TAG, "    + " + p.packageName);
+        }
+
+        PackageInfo[] result = new PackageInfo[packages.size()];
+        return packages.toArray(result);
+    }
+
+    public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor output)
+            throws android.os.RemoteException {
+        // we only support one hardcoded restore set
+        if (token != 0) return -1;
+
+        // the data for a given package is at a known location
+        File packageDir = new File(mDataDir, packageInfo.packageName);
+        File imageFile = new File(packageDir, DATA_FILE_NAME);
+
+        // restore is relatively easy: we already maintain the full data set in
+        // the canonical form understandable to the BackupAgent
+        return copyFileToFD(imageFile, output);
+    }
+
+    private int copyFileToFD(File source, ParcelFileDescriptor dest) {
+        try {
+            FileInputStream in = new FileInputStream(source);
+            FileOutputStream out = new FileOutputStream(dest.getFileDescriptor());
+            byte[] buffer = new byte[4096];
+            int bytesRead;
+            while ((bytesRead = in.read(buffer)) >= 0) {
+                out.write(buffer, 0, bytesRead);
+            }
+        } catch (IOException e) {
+            // something went wrong; claim failure
+            return -1;
+        }
+        return 0;
+    }
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 99a381c..16a3bad 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -30,9 +30,11 @@
 import android.util.Printer;
 import android.util.SparseArray;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -53,7 +55,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS' 
 
     // Current on-disk Parcel version
-    private static final int VERSION = 38;
+    private static final int VERSION = 39;
 
     private final File mFile;
     private final File mBackupFile;
@@ -94,7 +96,7 @@
     
     boolean mScreenOn;
     StopwatchTimer mScreenOnTimer;
-    
+
     int mScreenBrightnessBin = -1;
     final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
     
@@ -137,10 +139,10 @@
     long mTrackBatteryUptimeStart;
     long mTrackBatteryPastRealtime;
     long mTrackBatteryRealtimeStart;
-    
+
     long mUnpluggedBatteryUptime;
     long mUnpluggedBatteryRealtime;
-    
+
     /*
      * These keep track of battery levels (1-100) at the last plug event and the last unplug event.
      */
@@ -149,6 +151,15 @@
 
     long mLastWriteTime = 0; // Milliseconds
 
+    // Mobile data transferred while on battery
+    private long[] mMobileDataTx = new long[4];
+    private long[] mMobileDataRx = new long[4];
+    private long[] mTotalDataTx = new long[4];
+    private long[] mTotalDataRx = new long[4];
+
+    private long mRadioDataUptime;
+    private long mRadioDataStart;
+
     /*
      * Holds a SamplingTimer associated with each kernel wakelock name being tracked.
      */
@@ -893,7 +904,40 @@
         }
         return kwlt;
     }
-    
+
+    private void doDataPlug(long[] dataTransfer, long currentBytes) {
+        dataTransfer[STATS_LAST] = dataTransfer[STATS_UNPLUGGED];
+        dataTransfer[STATS_UNPLUGGED] = -1;
+    }
+
+    private void doDataUnplug(long[] dataTransfer, long currentBytes) {
+        dataTransfer[STATS_UNPLUGGED] = currentBytes;
+    }
+
+    private long getCurrentRadioDataUptimeMs() {
+        try {
+            File awakeTimeFile = new File("/sys/devices/virtual/net/rmnet0/awake_time_ms");
+            if (!awakeTimeFile.exists()) return 0;
+            BufferedReader br = new BufferedReader(new FileReader(awakeTimeFile));
+            String line = br.readLine();
+            br.close();
+            return Long.parseLong(line);
+        } catch (NumberFormatException nfe) {
+            // Nothing
+        } catch (IOException ioe) {
+            // Nothing
+        }
+        return 0;
+    }
+
+    public long getRadioDataUptimeMs() {
+        if (mRadioDataStart == -1) {
+            return mRadioDataUptime;
+        } else {
+            return getCurrentRadioDataUptimeMs() - mRadioDataStart;
+        }
+    }
+
     public void doUnplug(long batteryUptime, long batteryRealtime) {
         for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
             Uid u = mUidStats.valueAt(iu);
@@ -905,8 +949,16 @@
         for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
             mUnpluggables.get(i).unplug(batteryUptime, batteryRealtime);
         }
+        // Track total mobile data
+        doDataUnplug(mMobileDataRx, NetStat.getMobileRxBytes());
+        doDataUnplug(mMobileDataTx, NetStat.getMobileTxBytes());
+        doDataUnplug(mTotalDataRx, NetStat.getTotalRxBytes());
+        doDataUnplug(mTotalDataTx, NetStat.getTotalTxBytes());
+        // Track radio awake time
+        mRadioDataStart = getCurrentRadioDataUptimeMs();
+        mRadioDataUptime = 0;
     }
-    
+
     public void doPlug(long batteryUptime, long batteryRealtime) {
         for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
             Uid u = mUidStats.valueAt(iu);
@@ -922,8 +974,15 @@
         for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
             mUnpluggables.get(i).plug(batteryUptime, batteryRealtime);
         }
+        doDataPlug(mMobileDataRx, NetStat.getMobileRxBytes());
+        doDataPlug(mMobileDataTx, NetStat.getMobileTxBytes());
+        doDataPlug(mTotalDataRx, NetStat.getTotalRxBytes());
+        doDataPlug(mTotalDataTx, NetStat.getTotalTxBytes());
+        // Track radio awake time
+        mRadioDataUptime = getRadioDataUptimeMs();
+        mRadioDataStart = -1;
     }
-    
+
     public void noteStartGps(int uid) {
         mUidStats.get(uid).noteStartGps();
     }
@@ -931,7 +990,7 @@
     public void noteStopGps(int uid) {
         mUidStats.get(uid).noteStopGps();
     }
-    
+
     public void noteScreenOnLocked() {
         if (!mScreenOn) {
             mScreenOn = true;
@@ -1039,6 +1098,7 @@
                     break;
             }
         }
+        if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
         if (mPhoneDataConnectionType != bin) {
             if (mPhoneDataConnectionType >= 0) {
                 mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(this);
@@ -2701,7 +2761,44 @@
     public long getBatteryRealtime(long curTime) {
         return getBatteryRealtimeLocked(curTime);
     }
-    
+
+    private long getTcpBytes(long current, long[] dataBytes, int which) {
+        if (which == STATS_LAST) {
+            return dataBytes[STATS_LAST];
+        } else {
+            if (which == STATS_UNPLUGGED) {
+                if (dataBytes[STATS_UNPLUGGED] < 0) {
+                    return dataBytes[STATS_LAST];
+                } else {
+                    return current - dataBytes[STATS_UNPLUGGED];
+                }
+            } else if (which == STATS_TOTAL) {
+                return (current - dataBytes[STATS_CURRENT]) + dataBytes[STATS_TOTAL];
+            }
+            return current - dataBytes[STATS_CURRENT];
+        }
+    }
+
+    /** Only STATS_UNPLUGGED works properly */
+    public long getMobileTcpBytesSent(int which) {
+        return getTcpBytes(NetStat.getMobileTxBytes(), mMobileDataTx, which);
+    }
+
+    /** Only STATS_UNPLUGGED works properly */
+    public long getMobileTcpBytesReceived(int which) {
+        return getTcpBytes(NetStat.getMobileRxBytes(), mMobileDataRx, which);
+    }
+
+    /** Only STATS_UNPLUGGED works properly */
+    public long getTotalTcpBytesSent(int which) {
+        return getTcpBytes(NetStat.getTotalTxBytes(), mTotalDataTx, which);
+    }
+
+    /** Only STATS_UNPLUGGED works properly */
+    public long getTotalTcpBytesReceived(int which) {
+        return getTcpBytes(NetStat.getTotalRxBytes(), mTotalDataRx, which);
+    }
+
     @Override
     public int getDischargeStartLevel() {
         synchronized(this) {
@@ -3227,6 +3324,18 @@
         mDischargeCurrentLevel = in.readInt();
         mLastWriteTime = in.readLong();
 
+        mMobileDataRx[STATS_LAST] = in.readLong();
+        mMobileDataRx[STATS_UNPLUGGED] = -1;
+        mMobileDataTx[STATS_LAST] = in.readLong();
+        mMobileDataTx[STATS_UNPLUGGED] = -1;
+        mTotalDataRx[STATS_LAST] = in.readLong();
+        mTotalDataRx[STATS_UNPLUGGED] = -1;
+        mTotalDataTx[STATS_LAST] = in.readLong();
+        mTotalDataTx[STATS_UNPLUGGED] = -1;
+
+        mRadioDataUptime = in.readLong();
+        mRadioDataStart = -1;
+
         mKernelWakelockStats.clear();
         int NKW = in.readInt();
         for (int ikw = 0; ikw < NKW; ikw++) {
@@ -3301,6 +3410,14 @@
         out.writeInt(mDischargeCurrentLevel);
         out.writeLong(mLastWriteTime);
 
+        out.writeLong(getMobileTcpBytesReceived(STATS_UNPLUGGED));
+        out.writeLong(getMobileTcpBytesSent(STATS_UNPLUGGED));
+        out.writeLong(getTotalTcpBytesReceived(STATS_UNPLUGGED));
+        out.writeLong(getTotalTcpBytesSent(STATS_UNPLUGGED));
+
+        // Write radio uptime for data
+        out.writeLong(getRadioDataUptimeMs());
+
         out.writeInt(mKernelWakelockStats.size());
         for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
             SamplingTimer kwlt = ent.getValue();
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index f08dddd..a37bf6e 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -26,6 +26,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashMap;
 
 /**
@@ -118,26 +119,28 @@
      */
     public static final String POWER_VIDEO = "dsp.video";
 
-    static final HashMap<String, Double> sPowerMap = new HashMap<String, Double>();
+    static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>();
 
     private static final String TAG_DEVICE = "device";
     private static final String TAG_ITEM = "item";
+    private static final String TAG_ARRAY = "array";
+    private static final String TAG_ARRAYITEM = "value";
     private static final String ATTR_NAME = "name";
 
-    public PowerProfile(Context context, CharSequence profile) {
+    public PowerProfile(Context context) {
         // Read the XML file for the given profile (normally only one per
         // device)
         if (sPowerMap.size() == 0) {
-            readPowerValuesFromXml(context, profile);
+            readPowerValuesFromXml(context);
         }
     }
 
-    private void readPowerValuesFromXml(Context context, CharSequence profile) {
-        // FIXME
-        //int id = context.getResources().getIdentifier(profile.toString(), "xml", 
-        //        "com.android.internal");
-        int id = com.android.internal.R.xml.power_profile_default;
+    private void readPowerValuesFromXml(Context context) {
+        int id = com.android.internal.R.xml.power_profile;
         XmlResourceParser parser = context.getResources().getXml(id);
+        boolean parsingArray = false;
+        ArrayList<Double> array = new ArrayList<Double>();
+        String arrayName = null;
 
         try {
             XmlUtils.beginDocument(parser, TAG_DEVICE);
@@ -145,22 +148,39 @@
             while (true) {
                 XmlUtils.nextElement(parser);
 
-                String element = parser.getName(); 
-                if (element == null || !(element.equals(TAG_ITEM))) {
-                    break;
+                String element = parser.getName();
+                if (element == null) break;
+                
+                if (parsingArray && !element.equals(TAG_ARRAYITEM)) {
+                    // Finish array
+                    sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
+                    parsingArray = false;
                 }
-
-                String name = parser.getAttributeValue(null, ATTR_NAME);
-                if (parser.next() == XmlPullParser.TEXT) {
-                    String power = parser.getText();
-                    double value = 0;
-                    try {
-                        value = Double.valueOf(power);
-                    } catch (NumberFormatException nfe) {
+                if (element.equals(TAG_ARRAY)) {
+                    parsingArray = true;
+                    array.clear();
+                    arrayName = parser.getAttributeValue(null, ATTR_NAME);
+                } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) {
+                    String name = null;
+                    if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME);
+                    if (parser.next() == XmlPullParser.TEXT) {
+                        String power = parser.getText();
+                        double value = 0;
+                        try {
+                            value = Double.valueOf(power);
+                        } catch (NumberFormatException nfe) {
+                        }
+                        if (element.equals(TAG_ITEM)) {
+                            sPowerMap.put(name, value);
+                        } else if (parsingArray) {
+                            array.add(value);
+                        }
                     }
-                    sPowerMap.put(name, value);
                 }
             }
+            if (parsingArray) {
+                sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
+            }
         } catch (XmlPullParserException e) {
             throw new RuntimeException(e);
         } catch (IOException e) {
@@ -177,7 +197,40 @@
      */
     public double getAveragePower(String type) {
         if (sPowerMap.containsKey(type)) {
-            return sPowerMap.get(type);
+            Object data = sPowerMap.get(type);
+            if (data instanceof Double[]) {
+                return ((Double[])data)[0];
+            } else {
+                return (Double) sPowerMap.get(type);
+            }
+        } else {
+            return 0;
+        }
+    }
+    
+    /**
+     * Returns the average current in mA consumed by the subsystem for the given level. 
+     * @param type the subsystem type
+     * @param level the level of power at which the subsystem is running. For instance, the
+     *  signal strength of the cell network between 0 and 4 (if there are 4 bars max.).
+     *  If there is no data for multiple levels, the level is ignored.
+     * @return the average current in milliAmps.
+     */
+    public double getAveragePower(String type, int level) {
+        if (sPowerMap.containsKey(type)) {
+            Object data = sPowerMap.get(type);
+            if (data instanceof double[]) {
+                final double[] values = (double[]) data;
+                if (values.length > level) {
+                    return values[level];
+                } else if (values.length < 0) {
+                    return values[0];
+                } else {
+                    return values[values.length - 1];
+                }
+            } else {
+                return (Double) data;
+            }
         } else {
             return 0;
         }
diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/core/jni/android_location_GpsLocationProvider.cpp
index 0858741..5c4fb22 100644
--- a/core/jni/android_location_GpsLocationProvider.cpp
+++ b/core/jni/android_location_GpsLocationProvider.cpp
@@ -270,6 +270,12 @@
     sGpsInterface->inject_time(time, timeReference, uncertainty);
 }
 
+static void android_location_GpsLocationProvider_inject_location(JNIEnv* env, jobject obj,
+        jdouble latitude, jdouble longitude, jfloat accuracy)
+{
+    sGpsInterface->inject_location(latitude, longitude, accuracy);
+}
+
 static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, jobject obj)
 {
     if (!sGpsXtraInterface) {
@@ -353,6 +359,7 @@
     {"native_wait_for_event", "()V", (void*)android_location_GpsLocationProvider_wait_for_event},
     {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status},
     {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time},
+    {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location},
     {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra},
     {"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data},
     {"native_agps_data_conn_open", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open},
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index c13bf775..99fe9c8 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2849,6 +2849,14 @@
              The default value is <code>false</code>. <i>Optional attribute.</i>. -->
         <attr name="includeInGlobalSearch" format="boolean" />
 
+        <!-- If provided and <code>true</code>, this searchable activity will be invoked for all
+             queries in a particular session. If set to <code>false</code> and the activity
+             returned zero results for a query, it will not be invoked again in that session for
+             supersets of that zero-results query. For example, if the activity returned zero
+             results for "bo", it would not be queried again for "bob".
+             The default value is <code>false</code>. <i>Optional attribute.</i>. -->
+        <attr name="queryAfterZeroResults" format="boolean" />
+
     </declare-styleable>
 
     <!-- In order to process special action keys during search, you must define them using
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a50f3f9..e5a58b1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1115,6 +1115,7 @@
   <public type="attr" name="backupAgent" />
   <public type="attr" name="allowBackup" />
   <public type="attr" name="glEsVersion" />
+  <public type="attr" name="queryAfterZeroResults" />
   
   <public-padding type="attr" name="donut_resource_pad" end="0x0101029f" />
 
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
new file mode 100644
index 0000000..859902e
--- /dev/null
+++ b/core/res/res/xml/power_profile.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<device name="Android">
+  <item name="none">0</item>
+  <item name="screen.on">0.1</item>
+  <item name="bluetooth.active">0.1</item>
+  <item name="bluetooth.on">0.1</item>
+  <item name="screen.full">0.1</item>
+  <item name="wifi.on">0.1</item>
+  <item name="wifi.active">0.1</item>
+  <item name="wifi.scan">0.1</item>
+  <item name="cpu.idle">0.1</item>
+  <item name="cpu.normal">0.2</item>
+  <item name="cpu.full">1</item>
+  <item name="dsp.audio">0.1</item>
+  <item name="dsp.video">0.1</item>
+  <item name="radio.active">1</item>
+  <item name="gps.on">1</item>
+  <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
+      <value>1</value>
+      <value>0.1</value>
+  </array>
+</device>
diff --git a/core/res/res/xml/power_profile_default.xml b/core/res/res/xml/power_profile_default.xml
deleted file mode 100644
index ceecb1a..0000000
--- a/core/res/res/xml/power_profile_default.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<device name="Android">
-  <item name="none">0</item>
-  <item name="screen.on">30</item>
-  <item name="bluetooth.active">103</item>
-  <item name="bluetooth.on">5</item>
-  <item name="screen.full">114</item>
-  <item name="wifi.on">23</item>
-  <item name="wifi.active">200</item>
-  <item name="wifi.scan">200</item>
-  <item name="cpu.idle">1.6</item>
-  <item name="cpu.normal">100</item>
-  <item name="cpu.full">140</item>
-  <item name="dsp.audio">70</item>
-  <item name="dsp.video">100</item>
-  <item name="radio.on">3</item>
-  <item name="radio.active">175</item>
-  <item name="gps.on">120</item>
-</device>
diff --git a/docs/html/search.jd b/docs/html/search.jd
index 0a802a6..defba30 100644
--- a/docs/html/search.jd
+++ b/docs/html/search.jd
@@ -2,7 +2,7 @@
 @jd:body

 

 <script src="http://www.google.com/jsapi" type="text/javascript"></script>

-<script src="/assets/jquery-history.js" type="text/javascript"></script>

+<script src="{@docRoot}assets/jquery-history.js" type="text/javascript"></script>

 <script type="text/javascript">

       google.load('search', '1');

 

diff --git a/keystore/java/android/security/Keystore.java b/keystore/java/android/security/Keystore.java
new file mode 100644
index 0000000..3f83473
--- /dev/null
+++ b/keystore/java/android/security/Keystore.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.security;
+
+/**
+ * The Keystore class provides the functions to list the certs/keys in keystore.
+ * {@hide}
+ */
+public abstract class Keystore {
+    private static final String TAG = "Keystore";
+    private static final String[] NOTFOUND = new String[0];
+
+    /**
+     */
+    public static Keystore getInstance() {
+        return new FileKeystore();
+    }
+
+    /**
+     */
+    public abstract String getUserkey(String key);
+
+    /**
+     */
+    public abstract String getCertificate(String key);
+
+    /**
+     */
+    public abstract String[] getAllCertificateKeys();
+
+    /**
+     */
+    public abstract String[] getAllUserkeyKeys();
+
+    private static class FileKeystore extends Keystore {
+        private static final String SERVICE_NAME = "keystore";
+        private static final String LIST_CERTIFICATES = "listcerts";
+        private static final String LIST_USERKEYS = "listuserkeys";
+        private static final String PATH = "/data/misc/keystore/";
+        private static final String USERKEY_PATH = PATH + "userkeys/";
+        private static final String CERT_PATH = PATH + "certs/";
+        private static final ServiceCommand mServiceCommand =
+                new ServiceCommand(SERVICE_NAME);
+
+        @Override
+        public String getUserkey(String key) {
+            return USERKEY_PATH + key;
+        }
+
+        @Override
+        public String getCertificate(String key) {
+            return CERT_PATH + key;
+        }
+
+        /**
+         * Returns the array of the certificate names in keystore if successful.
+         * Or return an empty array if error.
+         *
+         * @return array of the certificates
+         */
+        @Override
+        public String[] getAllCertificateKeys() {
+            try {
+                String result = mServiceCommand.execute(LIST_CERTIFICATES);
+                if (result != null) return result.split("\\s+");
+                return NOTFOUND;
+            } catch (NumberFormatException ex) {
+                return NOTFOUND;
+            }
+        }
+
+        /**
+         * Returns the array of the names of private keys in keystore if successful.
+         * Or return an empty array if errors.
+         *
+         * @return array of the user keys
+         */
+        @Override
+        public String[] getAllUserkeyKeys() {
+            try {
+                String result = mServiceCommand.execute(LIST_USERKEYS);
+                if (result != null) return result.split("\\s+");
+                return NOTFOUND;
+            } catch (NumberFormatException ex) {
+                return NOTFOUND;
+            }
+        }
+    }
+}
diff --git a/keystore/java/android/security/ServiceCommand.java b/keystore/java/android/security/ServiceCommand.java
new file mode 100644
index 0000000..f1d4302
--- /dev/null
+++ b/keystore/java/android/security/ServiceCommand.java
@@ -0,0 +1,178 @@
+/*
+ * 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.security;
+
+import android.net.LocalSocketAddress;
+import android.net.LocalSocket;
+import android.util.Config;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/*
+ * ServiceCommand is used to connect to a service throught the local socket,
+ * and send out the command, return the result to the caller.
+ * {@hide}
+ */
+public class ServiceCommand {
+    public static final String SUCCESS = "0";
+    public static final String FAILED = "-1";
+
+    private String mServiceName;
+    private String mTag;
+    private InputStream mIn;
+    private OutputStream mOut;
+    private LocalSocket mSocket;
+    private static final int BUFFER_LENGTH = 1024;
+
+    private byte buf[] = new byte[BUFFER_LENGTH];
+    private int buflen = 0;
+
+    private boolean connect() {
+        if (mSocket != null) {
+            return true;
+        }
+        Log.i(mTag, "connecting...");
+        try {
+            mSocket = new LocalSocket();
+
+            LocalSocketAddress address = new LocalSocketAddress(
+                    mServiceName, LocalSocketAddress.Namespace.RESERVED);
+
+            mSocket.connect(address);
+
+            mIn = mSocket.getInputStream();
+            mOut = mSocket.getOutputStream();
+        } catch (IOException ex) {
+            disconnect();
+            return false;
+        }
+        return true;
+    }
+
+    private void disconnect() {
+        Log.i(mTag,"disconnecting...");
+        try {
+            if (mSocket != null) mSocket.close();
+        } catch (IOException ex) { }
+        try {
+            if (mIn != null) mIn.close();
+        } catch (IOException ex) { }
+        try {
+            if (mOut != null) mOut.close();
+        } catch (IOException ex) { }
+        mSocket = null;
+        mIn = null;
+        mOut = null;
+    }
+
+    private boolean readBytes(byte buffer[], int len) {
+        int off = 0, count;
+        if (len < 0) return false;
+        while (off != len) {
+            try {
+                count = mIn.read(buffer, off, len - off);
+                if (count <= 0) {
+                    Log.e(mTag, "read error " + count);
+                    break;
+                }
+                off += count;
+            } catch (IOException ex) {
+                Log.e(mTag,"read exception");
+                break;
+            }
+        }
+        if (off == len) return true;
+        disconnect();
+        return false;
+    }
+
+    private boolean readReply() {
+        int len, ret;
+        buflen = 0;
+
+        if (!readBytes(buf, 2)) return false;
+        ret = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
+        if (ret != 0) return false;
+
+        if (!readBytes(buf, 2)) return false;
+        len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
+        if (len > BUFFER_LENGTH) {
+            Log.e(mTag,"invalid reply length (" + len + ")");
+            disconnect();
+            return false;
+        }
+        if (!readBytes(buf, len)) return false;
+        buflen = len;
+        return true;
+    }
+
+    private boolean writeCommand(String _cmd) {
+        byte[] cmd = _cmd.getBytes();
+        int len = cmd.length;
+        if ((len < 1) || (len > BUFFER_LENGTH)) return false;
+        buf[0] = (byte) (len & 0xff);
+        buf[1] = (byte) ((len >> 8) & 0xff);
+        try {
+            mOut.write(buf, 0, 2);
+            mOut.write(cmd, 0, len);
+        } catch (IOException ex) {
+            Log.e(mTag,"write error");
+            disconnect();
+            return false;
+        }
+        return true;
+    }
+
+    private String executeCommand(String cmd) {
+        if (!writeCommand(cmd)) {
+            /* If service died and restarted in the background
+             * (unlikely but possible) we'll fail on the next
+             * write (this one).  Try to reconnect and write
+             * the command one more time before giving up.
+             */
+            Log.e(mTag, "write command failed? reconnect!");
+            if (!connect() || !writeCommand(cmd)) {
+                return null;
+            }
+        }
+        if (readReply()) {
+            return new String(buf, 0, buflen);
+        } else {
+            return null;
+        }
+    }
+
+    public synchronized String execute(String cmd) {
+      String result;
+      if (!connect()) {
+          Log.e(mTag, "connection failed");
+          return null;
+      }
+      result = executeCommand(cmd);
+      disconnect();
+      return result;
+    }
+
+    public ServiceCommand(String service) {
+        mServiceName = service;
+        mTag = service;
+    }
+}
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 725fbf9..9698553 100644
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -410,6 +410,10 @@
      * Someday we might use this for network location injection to aid the GPS
      */
     public void updateLocation(Location location) {
+        if (location.hasAccuracy()) {
+            native_inject_location(location.getLatitude(), location.getLongitude(),
+                    location.getAccuracy());
+        }
     }
 
     /**
@@ -1210,7 +1214,8 @@
     // mask[0] is ephemeris mask and mask[1] is almanac mask
     private native int native_read_sv_status(int[] svs, float[] snrs,
             float[] elevations, float[] azimuths, int[] masks);
-    
+    private native void native_inject_location(double latitude, double longitude, float accuracy);
+
     // XTRA Support    
     private native void native_inject_time(long time, long timeReference, int uncertainty);
     private native boolean native_supports_xtra();
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
index e01bd53..ea42f53 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
@@ -80,7 +80,6 @@
       assertTrue("MIDI getDuration", duratoinWithinTolerence);  
     }
     
-    @Suppress
     @MediumTest
     public void testWMA9GetDuration() throws Exception {
       int duration = CodecTest.getDuration(MediaNames.WMA9); 
@@ -122,7 +121,6 @@
       assertTrue("MIDI GetCurrentPosition", currentPosition);  
     }
     
-    @Suppress
     @LargeTest
     public void testWMA9GetCurrentPosition() throws Exception {
       boolean currentPosition = CodecTest.getCurrentPosition(MediaNames.WMA9);  
@@ -160,7 +158,6 @@
       assertTrue("MIDI Pause", isPaused);  
     }
    
-    @Suppress
     @LargeTest
     public void testWMA9Pause() throws Exception {
       boolean isPaused = CodecTest.pause(MediaNames.WMA9);  
@@ -232,7 +229,6 @@
       assertTrue("MIDI setLooping", isLoop);  
     }
     
-    @Suppress
     @LargeTest
     public void testWMA9SetLooping() throws Exception {
       boolean isLoop = CodecTest.setLooping(MediaNames.WMA9);  
@@ -271,7 +267,6 @@
       assertTrue("MIDI seekTo", isLoop);  
     }
     
-    @Suppress
     @LargeTest
     public void testWMA9SeekTo() throws Exception {
       boolean isLoop = CodecTest.seekTo(MediaNames.WMA9);  
@@ -310,7 +305,7 @@
       boolean isEnd = CodecTest.seekToEnd(MediaNames.MIDI);  
       assertTrue("MIDI seekToEnd", isEnd);  
     }
-    
+
     @Suppress
     @LargeTest
     public void testWMA9SeekToEnd() throws Exception {
@@ -388,7 +383,6 @@
       assertTrue("H264AMR SeekTo", isSeek);         
     }
    
-    @Suppress
     @LargeTest
     public void testVideoWMVSeekTo() throws Exception {
       boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_WMV);
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index c2c76fd..8a64113 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -117,16 +117,7 @@
         // app.
         prefs = PreferenceManager.getDefaultSharedPreferences(this);
 
-        PackageManager pm = this.getPackageManager();
-        String soLibPath = "";
-        try {
-            soLibPath = pm.getApplicationInfo("com.svox.pico", 0).dataDir;
-        } catch (NameNotFoundException e) {
-            // This exception cannot actually happen as com.svox.pico is
-            // included with the system image.
-            e.printStackTrace();
-        }
-        soLibPath = soLibPath + "/lib/libttspico.so";
+        String soLibPath = "/system/lib/libttspico.so";
         nativeSynth = new SynthProxy(soLibPath);
 
         mSelf = this;
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 42a895c..d3067ec 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -25,6 +25,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDataObserver;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -46,13 +47,14 @@
 import android.backup.BackupManager;
 import android.backup.RestoreSet;
 
-import com.android.internal.backup.AdbTransport;
+import com.android.internal.backup.LocalTransport;
 import com.android.internal.backup.GoogleTransport;
 import com.android.internal.backup.IBackupTransport;
 
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.String;
 import java.util.ArrayList;
@@ -70,7 +72,11 @@
 
     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;
+
+    // Timeout interval for deciding that a bind or clear-data has taken too long
+    static final long TIMEOUT_INTERVAL = 10 * 1000;
+
     private Context mContext;
     private PackageManager mPackageManager;
     private final IActivityManager mActivityManager;
@@ -108,6 +114,10 @@
     private IBackupAgent mConnectedAgent;
     private volatile boolean mConnecting;
 
+    // A similar synchronicity mechanism around clearing apps' data for restore
+    private final Object mClearDataLock = new Object();
+    private volatile boolean mClearingData;
+
     private int mTransportId;
 
     private File mStateDir;
@@ -122,7 +132,9 @@
         mStateDir = new File(Environment.getDataDirectory(), "backup");
         mStateDir.mkdirs();
         mDataDir = Environment.getDownloadCacheDirectory();
-        mTransportId = BackupManager.TRANSPORT_GOOGLE;
+
+        //!!! TODO: default to cloud transport, not local
+        mTransportId = BackupManager.TRANSPORT_LOCAL;
         
         // Build our mapping of uid to backup client services
         synchronized (mBackupParticipants) {
@@ -203,81 +215,15 @@
 
             case MSG_RUN_FULL_BACKUP:
                 break;
+
+            case MSG_RUN_RESTORE:
+            {
+                int token = msg.arg1;
+                IBackupTransport transport = (IBackupTransport)msg.obj;
+                (new PerformRestoreThread(transport, token)).run();
+                break;
             }
-        }
-    }
-
-    void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
-        final String packageName = request.appInfo.packageName;
-        Log.d(TAG, "processOneBackup doBackup() on " + packageName);
-
-        try {
-            // Look up the package info & signatures.  This is first so that if it
-            // throws an exception, there's no file setup yet that would need to
-            // be unraveled.
-            PackageInfo packInfo = mPackageManager.getPackageInfo(packageName,
-                    PackageManager.GET_SIGNATURES);
-
-            // !!! TODO: get the state file dir from the transport
-            File savedStateName = new File(mStateDir, packageName);
-            File backupDataName = new File(mDataDir, packageName + ".data");
-            File newStateName = new File(mStateDir, packageName + ".new");
-            
-            // In a full backup, we pass a null ParcelFileDescriptor as
-            // the saved-state "file"
-            ParcelFileDescriptor savedState = (request.fullBackup) ? null
-                    : ParcelFileDescriptor.open(savedStateName,
-                        ParcelFileDescriptor.MODE_READ_ONLY |
-                        ParcelFileDescriptor.MODE_CREATE);
-
-            backupDataName.delete();
-            ParcelFileDescriptor backupData =
-                    ParcelFileDescriptor.open(backupDataName,
-                            ParcelFileDescriptor.MODE_READ_WRITE |
-                            ParcelFileDescriptor.MODE_CREATE);
-
-            newStateName.delete();
-            ParcelFileDescriptor newState =
-                    ParcelFileDescriptor.open(newStateName,
-                            ParcelFileDescriptor.MODE_READ_WRITE |
-                            ParcelFileDescriptor.MODE_CREATE);
-
-            // Run the target's backup pass
-            boolean success = false;
-            try {
-                agent.doBackup(savedState, backupData, newState);
-                success = true;
-            } finally {
-                if (savedState != null) {
-                    savedState.close();
-                }
-                backupData.close();
-                newState.close();
             }
-
-            // Now propagate the newly-backed-up data to the transport
-            if (success) {
-                if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
-                backupData =
-                    ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
-                int error = transport.performBackup(packInfo, backupData);
-
-                // !!! TODO: After successful transport, delete the now-stale data
-                // and juggle the files so that next time the new state is passed
-                //backupDataName.delete();
-                newStateName.renameTo(savedStateName);
-            }
-        } catch (NameNotFoundException e) {
-            Log.e(TAG, "Package not found on backup: " + packageName);
-        } catch (FileNotFoundException fnf) {
-            Log.w(TAG, "File not found on backup: ");
-            fnf.printStackTrace();
-        } catch (RemoteException e) {
-            Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:");
-            e.printStackTrace();
-        } catch (Exception e) {
-            Log.w(TAG, "Final exception guard in backup: ");
-            e.printStackTrace();
         }
     }
 
@@ -396,9 +342,9 @@
     private IBackupTransport createTransport(int transportID) {
         IBackupTransport transport = null;
         switch (transportID) {
-        case BackupManager.TRANSPORT_ADB:
-            if (DEBUG) Log.v(TAG, "Initializing adb transport");
-            transport = new AdbTransport();
+        case BackupManager.TRANSPORT_LOCAL:
+            if (DEBUG) Log.v(TAG, "Initializing local transport");
+            transport = new LocalTransport(mContext);
             break;
 
         case BackupManager.TRANSPORT_GOOGLE:
@@ -424,11 +370,14 @@
                     Log.d(TAG, "awaiting agent for " + app);
 
                     // success; wait for the agent to arrive
-                    while (mConnecting && mConnectedAgent == null) {
+                    // only wait 10 seconds for the clear data to happen
+                    long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+                    while (mConnecting && mConnectedAgent == null
+                            && (System.currentTimeMillis() < timeoutMark)) {
                         try {
-                            mAgentConnectLock.wait(10000);
+                            mAgentConnectLock.wait(5000);
                         } catch (InterruptedException e) {
-                            // just retry
+                            // just bail
                             return null;
                         }
                     }
@@ -447,6 +396,37 @@
         return agent;
     }
 
+    // clear an application's data, blocking until the operation completes or times out
+    void clearApplicationDataSynchronous(String packageName) {
+        ClearDataObserver observer = new ClearDataObserver();
+
+        synchronized(mClearDataLock) {
+            mClearingData = true;
+            mPackageManager.clearApplicationUserData(packageName, observer);
+
+            // only wait 10 seconds for the clear data to happen
+            long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+            while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
+                try {
+                    mClearDataLock.wait(5000);
+                } catch (InterruptedException e) {
+                    // won't happen, but still.
+                    mClearingData = false;
+                }
+            }
+        }
+    }
+
+    class ClearDataObserver extends IPackageDataObserver.Stub {
+        public void onRemoveCompleted(String packageName, boolean succeeded)
+                throws android.os.RemoteException {
+            synchronized(mClearDataLock) {
+                mClearingData = false;
+                notifyAll();
+            }
+        }
+    }
+
     // ----- Back up a set of applications via a worker thread -----
 
     class PerformBackupThread extends Thread {
@@ -508,13 +488,88 @@
                     mActivityManager.unbindBackupAgent(request.appInfo);
                 } catch (SecurityException ex) {
                     // Try for the next one.
-                    Log.d(TAG, "error in bind", ex);
+                    Log.d(TAG, "error in bind/backup", ex);
                 } catch (RemoteException e) {
-                    // can't happen
+                    Log.v(TAG, "bind/backup threw");
+                    e.printStackTrace();
                 }
 
             }
         }
+
+        void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
+            final String packageName = request.appInfo.packageName;
+            Log.d(TAG, "processOneBackup doBackup() on " + packageName);
+
+            try {
+                // Look up the package info & signatures.  This is first so that if it
+                // throws an exception, there's no file setup yet that would need to
+                // be unraveled.
+                PackageInfo packInfo = mPackageManager.getPackageInfo(packageName,
+                        PackageManager.GET_SIGNATURES);
+
+                // !!! TODO: get the state file dir from the transport
+                File savedStateName = new File(mStateDir, packageName);
+                File backupDataName = new File(mDataDir, packageName + ".data");
+                File newStateName = new File(mStateDir, packageName + ".new");
+
+                // In a full backup, we pass a null ParcelFileDescriptor as
+                // the saved-state "file"
+                ParcelFileDescriptor savedState = (request.fullBackup) ? null
+                        : ParcelFileDescriptor.open(savedStateName,
+                            ParcelFileDescriptor.MODE_READ_ONLY |
+                            ParcelFileDescriptor.MODE_CREATE);
+
+                backupDataName.delete();
+                ParcelFileDescriptor backupData =
+                        ParcelFileDescriptor.open(backupDataName,
+                                ParcelFileDescriptor.MODE_READ_WRITE |
+                                ParcelFileDescriptor.MODE_CREATE);
+
+                newStateName.delete();
+                ParcelFileDescriptor newState =
+                        ParcelFileDescriptor.open(newStateName,
+                                ParcelFileDescriptor.MODE_READ_WRITE |
+                                ParcelFileDescriptor.MODE_CREATE);
+
+                // Run the target's backup pass
+                boolean success = false;
+                try {
+                    agent.doBackup(savedState, backupData, newState);
+                    success = true;
+                } finally {
+                    if (savedState != null) {
+                        savedState.close();
+                    }
+                    backupData.close();
+                    newState.close();
+                }
+
+                // Now propagate the newly-backed-up data to the transport
+                if (success) {
+                    if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
+                    backupData =
+                        ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
+                    int error = transport.performBackup(packInfo, backupData);
+
+                    // !!! TODO: After successful transport, delete the now-stale data
+                    // and juggle the files so that next time the new state is passed
+                    //backupDataName.delete();
+                    newStateName.renameTo(savedStateName);
+                }
+            } catch (NameNotFoundException e) {
+                Log.e(TAG, "Package not found on backup: " + packageName);
+            } catch (FileNotFoundException fnf) {
+                Log.w(TAG, "File not found on backup: ");
+                fnf.printStackTrace();
+            } catch (RemoteException e) {
+                Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:");
+                e.printStackTrace();
+            } catch (Exception e) {
+                Log.w(TAG, "Final exception guard in backup: ");
+                e.printStackTrace();
+            }
+        }
     }
 
 
@@ -524,12 +579,12 @@
     // ApplicationInfo struct if it is; null if not.
     //
     // !!! TODO: also consider signatures
-    ApplicationInfo isRestorable(PackageInfo packageInfo) {
+    PackageInfo isRestorable(PackageInfo packageInfo) {
         if (packageInfo.packageName != null) {
             try {
-                ApplicationInfo app = mPackageManager.getApplicationInfo(packageInfo.packageName,
+                PackageInfo app = mPackageManager.getPackageInfo(packageInfo.packageName,
                         PackageManager.GET_SIGNATURES);
-                if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
+                if ((app.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
                     return app;
                 }
             } catch (Exception e) {
@@ -541,9 +596,12 @@
 
     class PerformRestoreThread extends Thread {
         private IBackupTransport mTransport;
+        private int mToken;
+        private RestoreSet mImage;
 
-        PerformRestoreThread(IBackupTransport transport) {
+        PerformRestoreThread(IBackupTransport transport, int restoreSetToken) {
             mTransport = transport;
+            mToken = restoreSetToken;
         }
 
         @Override
@@ -556,6 +614,7 @@
              * 3. for each app in the restore set:
              *    3.a. if it's restorable on this device, add it to the restore queue
              * 4. for each app in the restore queue:
+             *    4.a. clear the app data
              *    4.b. get the restore data for the app from the transport
              *    4.c. launch the backup agent for the app
              *    4.d. agent.doRestore() with the data from the server
@@ -576,14 +635,15 @@
                 try {
                     RestoreSet[] images = mTransport.getAvailableRestoreSets();
                     if (images.length > 0) {
-                        // !!! for now we always take the first set
-                        RestoreSet image = images[0];
+                        // !!! TODO: pick out the set for this token
+                        mImage = images[0];
 
                         // build the set of apps we will attempt to restore
-                        PackageInfo[] packages = mTransport.getAppSet(image.token);
-                        HashSet<ApplicationInfo> appsToRestore = new HashSet<ApplicationInfo>();
+                        PackageInfo[] packages = mTransport.getAppSet(mImage.token);
+                        HashSet<PackageInfo> appsToRestore = new HashSet<PackageInfo>();
                         for (PackageInfo pkg: packages) {
-                            ApplicationInfo app = isRestorable(pkg);
+                            // get the real PackageManager idea of the package
+                            PackageInfo app = isRestorable(pkg);
                             if (app != null) {
                                 appsToRestore.add(app);
                             }
@@ -609,19 +669,23 @@
         }
 
         // restore each app in the queue
-        void doQueuedRestores(HashSet<ApplicationInfo> appsToRestore) {
-            for (ApplicationInfo app : appsToRestore) {
+        void doQueuedRestores(HashSet<PackageInfo> appsToRestore) {
+            for (PackageInfo app : appsToRestore) {
                 Log.d(TAG, "starting agent for restore of " + app);
 
-                IBackupAgent agent = null;
                 try {
-                    agent = bindToAgentSynchronous(app, IApplicationThread.BACKUP_MODE_RESTORE);
+                    // Remove the app's data first
+                    clearApplicationDataSynchronous(app.packageName);
+
+                    // Now perform the restore into the clean app
+                    IBackupAgent agent = bindToAgentSynchronous(app.applicationInfo,
+                            IApplicationThread.BACKUP_MODE_RESTORE);
                     if (agent != null) {
                         processOneRestore(app, agent);
                     }
 
                     // unbind even on timeout, just in case
-                    mActivityManager.unbindBackupAgent(app);
+                    mActivityManager.unbindBackupAgent(app.applicationInfo);
                 } catch (SecurityException ex) {
                     // Try for the next one.
                     Log.d(TAG, "error in bind", ex);
@@ -632,9 +696,67 @@
             }
         }
 
-        // do the guts of a restore
-        void processOneRestore(ApplicationInfo app, IBackupAgent agent) {
+        // Do the guts of a restore of one application, derived from the 'mImage'
+        // restore set via the 'mTransport' transport.
+        void processOneRestore(PackageInfo app, IBackupAgent agent) {
             // !!! TODO: actually run the restore through mTransport
+            final String packageName = app.packageName;
+
+            // !!! TODO: get the dirs from the transport
+            File backupDataName = new File(mDataDir, packageName + ".restore");
+            backupDataName.delete();
+            try {
+                ParcelFileDescriptor backupData =
+                    ParcelFileDescriptor.open(backupDataName,
+                            ParcelFileDescriptor.MODE_READ_WRITE |
+                            ParcelFileDescriptor.MODE_CREATE);
+
+                // Run the transport's restore pass
+                // Run the target's backup pass
+                int err = -1;
+                try {
+                    err = mTransport.getRestoreData(mImage.token, app, backupData);
+                } catch (RemoteException e) {
+                    // can't happen
+                } finally {
+                    backupData.close();
+                }
+
+                // Okay, we have the data.  Now have the agent do the restore.
+                File newStateName = new File(mStateDir, packageName + ".new");
+                ParcelFileDescriptor newState =
+                    ParcelFileDescriptor.open(newStateName,
+                            ParcelFileDescriptor.MODE_READ_WRITE |
+                            ParcelFileDescriptor.MODE_CREATE);
+
+                backupData = ParcelFileDescriptor.open(backupDataName,
+                            ParcelFileDescriptor.MODE_READ_ONLY);
+
+                boolean success = false;
+                try {
+                    agent.doRestore(backupData, newState);
+                    success = true;
+                } catch (Exception e) {
+                    Log.e(TAG, "Restore failed for " + packageName);
+                    e.printStackTrace();
+                } finally {
+                    newState.close();
+                    backupData.close();
+                }
+
+                // if everything went okay, remember the recorded state now
+                if (success) {
+                    File savedStateName = new File(mStateDir, packageName);
+                    newStateName.renameTo(savedStateName);
+                }
+            } catch (FileNotFoundException fnfe) {
+                Log.v(TAG, "Couldn't open file for restore: " + fnfe);
+            } catch (IOException ioe) {
+                Log.e(TAG, "Unable to process restore file: " + ioe);
+            } catch (Exception e) {
+                Log.e(TAG, "Final exception guard in restore:");
+                e.printStackTrace();
+            }
         }
     }
 
@@ -761,6 +883,9 @@
 
         // --- Binder interface ---
         public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
+            mContext.enforceCallingPermission("android.permission.BACKUP",
+                    "getAvailableRestoreSets");
+
             synchronized(this) {
                 if (mRestoreSets == null) {
                     mRestoreSets = mRestoreTransport.getAvailableRestoreSets();
@@ -770,10 +895,26 @@
         }
 
         public int performRestore(int token) 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);
+                        msg.arg1 = token;
+                        mBackupHandler.sendMessage(msg);
+                        return 0;
+                    }
+                }
+            }
             return -1;
         }
 
         public void endRestoreSession() throws android.os.RemoteException {
+            mContext.enforceCallingPermission("android.permission.BACKUP",
+                    "endRestoreSession");
+
             mRestoreTransport.endSession();
             mRestoreTransport = null;
         }
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index adf07dd..fc37290 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -769,8 +769,8 @@
                 if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
                     if (deadReceivers == null) {
                         deadReceivers = new ArrayList<Receiver>();
-                        deadReceivers.add(record.mReceiver);
                     }
+                    deadReceivers.add(record.mReceiver);
                 }
                 listeners++;
             }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 14dcfd5..6a81178 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -10422,11 +10422,14 @@
             // Not backing this app up any more; reset its OOM adjustment
             updateOomAdjLocked(proc);
 
-            try {
-                proc.thread.scheduleDestroyBackupAgent(appInfo);
-            } catch (Exception e) {
-                Log.e(TAG, "Exception when unbinding backup agent:");
-                e.printStackTrace();
+            // If the app crashed during backup, 'thread' will be null here
+            if (proc.thread != null) {
+                try {
+                    proc.thread.scheduleDestroyBackupAgent(appInfo);
+                } catch (Exception e) {
+                    Log.e(TAG, "Exception when unbinding backup agent:");
+                    e.printStackTrace();
+                }
             }
         }
     }