Merge "Removes some storage tests from the DumpRenderTree skipped lists, now that Android-specific results are provided"
diff --git a/api/current.xml b/api/current.xml
index 1f7be9e..212e661 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -24220,6 +24220,17 @@
  visibility="public"
 >
 </field>
+<field name="CURSOR_EXTRA_KEY_IN_PROGRESS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;in_progress&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="EXTRA_DATA_KEY"
  type="java.lang.String"
  transient="false"
@@ -83832,6 +83843,19 @@
  visibility="public"
 >
 </method>
+<method name="setAudioChannels"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="numChannels" type="int">
+</parameter>
+</method>
 <method name="setAudioEncoder"
  return="void"
  abstract="false"
@@ -83847,6 +83871,32 @@
 <exception name="IllegalStateException" type="java.lang.IllegalStateException">
 </exception>
 </method>
+<method name="setAudioEncodingBitRate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bitRate" type="int">
+</parameter>
+</method>
+<method name="setAudioSamplingRate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="samplingRate" type="int">
+</parameter>
+</method>
 <method name="setAudioSource"
  return="void"
  abstract="false"
@@ -84004,6 +84054,19 @@
 <exception name="IllegalStateException" type="java.lang.IllegalStateException">
 </exception>
 </method>
+<method name="setVideoEncodingBitRate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bitRate" type="int">
+</parameter>
+</method>
 <method name="setVideoFrameRate"
  return="void"
  abstract="false"
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index a447f53..c8d1397 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -674,14 +674,19 @@
     return 0;
 }
 
-void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid)
+void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid,
+        struct stat* statbuf)
 {
     while (path[basepos] != 0) {
         if (path[basepos] == '/') {
             path[basepos] = 0;
-            LOGI("Making directory: %s\n", path);
-            if (mkdir(path, mode) == 0) {
-                chown(path, uid, gid);
+            if (lstat(path, statbuf) < 0) {
+                LOGI("Making directory: %s\n", path);
+                if (mkdir(path, mode) == 0) {
+                    chown(path, uid, gid);
+                } else {
+                    LOGW("Unable to make directory %s: %s\n", path, strerror(errno));
+                }
             }
             path[basepos] = '/';
             basepos++;
@@ -690,8 +695,8 @@
     }
 }
 
-int movefileordir(char* srcpath, char* dstpath, int dstuid, int dstgid,
-        struct stat* statbuf)
+int movefileordir(char* srcpath, char* dstpath, int dstbasepos,
+        int dstuid, int dstgid, struct stat* statbuf)
 {
     DIR *d;
     struct dirent *de;
@@ -706,8 +711,9 @@
     }
     
     if ((statbuf->st_mode&S_IFDIR) == 0) {
+        mkinnerdirs(dstpath, dstbasepos, S_IRWXU|S_IRWXG|S_IXOTH,
+                dstuid, dstgid, statbuf);
         LOGI("Renaming %s to %s (uid %d)\n", srcpath, dstpath, dstuid);
-        mkinnerdirs(dstpath, dstend-1, S_IRWXU|S_IRWXG|S_IXOTH, dstuid, dstgid);
         if (rename(srcpath, dstpath) >= 0) {
             if (chown(dstpath, dstuid, dstgid) < 0) {
                 LOGE("cannot chown %s: %s\n", dstpath, strerror(errno));
@@ -752,7 +758,7 @@
         strcpy(srcpath+srcend+1, name);
         strcpy(dstpath+dstend+1, name);
         
-        if (movefileordir(srcpath, dstpath, dstuid, dstgid, statbuf) != 0) {
+        if (movefileordir(srcpath, dstpath, dstbasepos, dstuid, dstgid, statbuf) != 0) {
             res = 1;
         }
         
@@ -834,7 +840,9 @@
                             LOGV("Move file: %s (from %s to %s)\n", buf+bufp, srcpkg, dstpkg);
                             if (!create_move_path(srcpath, PKG_DIR_PREFIX, srcpkg, buf+bufp) &&
                                     !create_move_path(dstpath, PKG_DIR_PREFIX, dstpkg, buf+bufp)) {
-                                movefileordir(srcpath, dstpath, dstuid, dstgid, &s);
+                                movefileordir(srcpath, dstpath,
+                                        strlen(dstpath)-strlen(buf+bufp),
+                                        dstuid, dstgid, &s);
                             }
                         }
                     } else {
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index af68689..c2afd07 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -464,15 +464,14 @@
         }
     }
 
-    /**    
-     * Update the text in the search button.  Note: This is deprecated functionality, for 
-     * 1.0 compatibility only.
-     */  
-    private void updateSearchButton() { 
+    private void updateSearchButton() {
         String textLabel = null;
         Drawable iconLabel = null;
         int textId = mSearchable.getSearchButtonText(); 
-        if (textId != 0) {
+        if (isBrowserSearch()){
+            iconLabel = getContext().getResources()
+                    .getDrawable(com.android.internal.R.drawable.ic_btn_search_play);
+        } else if (textId != 0) {
             textLabel = mActivityContext.getResources().getString(textId);  
         } else {
             iconLabel = getContext().getResources().
@@ -483,10 +482,6 @@
     }
     
     private void updateSearchAppIcon() {
-        // In Donut, we special-case the case of the browser to hide the app icon as if it were
-        // global search, for extra space for url entry.
-        //
-        // TODO: Remove this special case once the issue has been reconciled in Eclair. 
         if (isBrowserSearch()) {
             mAppIcon.setImageResource(0);
             mAppIcon.setVisibility(View.GONE);
@@ -583,8 +578,7 @@
     }
     
     /**
-     * Hack to determine whether this is the browser, so we can remove the browser icon
-     * to the left of the search field.
+     * Hack to determine whether this is the browser, so we can adjust the UI.
      */
     private boolean isBrowserSearch() {
         return mLaunchComponent.flattenToShortString().startsWith("com.android.browser/");
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 67f9629..0ed572a 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1300,6 +1300,14 @@
     public final static String EXTRA_SELECT_QUERY = "select_query";
 
     /**
+     * Boolean extra data key for a suggestion provider to return in {@link Cursor#getExtras} to
+     * indicate that the search is not complete yet. This can be used by the search UI
+     * to indicate that a search is in progress. The suggestion provider can return partial results
+     * this way and send a change notification on the cursor when more results are available.
+     */
+    public final static String CURSOR_EXTRA_KEY_IN_PROGRESS = "in_progress";
+
+    /**
      * Intent extra data key: Use this key with Intent.ACTION_SEARCH and
      * {@link android.content.Intent#getStringExtra content.Intent.getStringExtra()}
      * to obtain the action message that was defined for a particular search action key and/or
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
index 07e9793..3e11a3f 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -159,19 +159,23 @@
          * for in app search we show the progress spinner until the cursor is returned with
          * the results.
          */
+        Cursor cursor = null;
         mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable);
         try {
-            final Cursor cursor = mSearchManager.getSuggestions(mSearchable, query, QUERY_LIMIT);
+            cursor = mSearchManager.getSuggestions(mSearchable, query, QUERY_LIMIT);
             // trigger fill window so the spinner stays up until the results are copied over and
             // closer to being ready
-            if (cursor != null) cursor.getCount();
-            return cursor;
+            if (cursor != null) {
+                cursor.getCount();
+                return cursor;
+            }
         } catch (RuntimeException e) {
             Log.w(LOG_TAG, "Search suggestions query threw an exception.", e);
-            return null;
-        } finally {
-            mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable);
         }
+        // If cursor is null or an exception was thrown, stop the spinner and return null.
+        // changeCursor doesn't get called if cursor is null
+        mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable);
+        return null;
     }
 
     public void close() {
@@ -180,6 +184,39 @@
         mClosed = true;
     }
 
+    @Override
+    public void notifyDataSetChanged() {
+        if (DBG) Log.d(LOG_TAG, "notifyDataSetChanged");
+        super.notifyDataSetChanged();
+
+        updateSpinnerState(getCursor());
+    }
+
+    @Override
+    public void notifyDataSetInvalidated() {
+        if (DBG) Log.d(LOG_TAG, "notifyDataSetInvalidated");
+        super.notifyDataSetInvalidated();
+
+        updateSpinnerState(getCursor());
+    }
+
+    private void updateSpinnerState(Cursor cursor) {
+        if (DBG) {
+            Log.d(LOG_TAG, "updateSpinnerState - extra = "
+                + (cursor != null
+                        ? cursor.getExtras().getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)
+                        : null));
+        }
+        // Check if the Cursor indicates that the query is not complete and show the spinner
+        if (cursor != null
+                && cursor.getExtras().getBoolean(SearchManager.CURSOR_EXTRA_KEY_IN_PROGRESS)) {
+            mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable);
+            return;
+        }
+        // If cursor is null or is done, stop the spinner
+        mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable);
+    }
+
     /**
      * Cache columns.
      */
@@ -202,7 +239,8 @@
                 mText2UrlCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_2_URL);
                 mIconName1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_1);
                 mIconName2Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_ICON_2);
-                mBackgroundColorCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_BACKGROUND_COLOR);
+                mBackgroundColorCol =
+                        c.getColumnIndex(SearchManager.SUGGEST_COLUMN_BACKGROUND_COLOR);
             }
         } catch (Exception e) {
             Log.e(LOG_TAG, "error changing cursor and caching columns", e);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 90e8c14..179b807 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1632,12 +1632,20 @@
 
     /**
      * Broadcast Action:  External media is unmounted because it is being shared via USB mass storage.
-     * The path to the mount point for the removed media is contained in the Intent.mData field.
+     * The path to the mount point for the shared media is contained in the Intent.mData field.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_MEDIA_SHARED = "android.intent.action.MEDIA_SHARED";
 
     /**
+     * Broadcast Action:  External media is no longer being shared via USB mass storage.
+     * The path to the mount point for the previously shared media is contained in the Intent.mData field.
+     *
+     * @hide
+     */
+    public static final String ACTION_MEDIA_UNSHARED = "android.intent.action.MEDIA_UNSHARED";
+
+    /**
      * Broadcast Action:  External media was removed from SD card slot, but mount point was not unmounted.
      * The path to the mount point for the removed media is contained in the Intent.mData field.
      */
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b07bafcf..7a0337cd 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -967,8 +967,11 @@
                 String orig =sa.getNonResourceString(
                         com.android.internal.R.styleable.AndroidManifestOriginalPackage_name);
                 if (!pkg.packageName.equals(orig)) {
-                    pkg.mOriginalPackage = orig;
-                    pkg.mRealPackage = pkg.packageName;
+                    if (pkg.mOriginalPackages == null) {
+                        pkg.mOriginalPackages = new ArrayList<String>();
+                        pkg.mRealPackage = pkg.packageName;
+                    }
+                    pkg.mOriginalPackages.add(orig);
                 }
 
                 sa.recycle();
@@ -2579,7 +2582,7 @@
         public ArrayList<String> usesOptionalLibraries = null;
         public String[] usesLibraryFiles = null;
 
-        public String mOriginalPackage = null;
+        public ArrayList<String> mOriginalPackages = null;
         public String mRealPackage = null;
         public ArrayList<String> mAdoptPermissions = null;
         
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c76aca1..a6668e7 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -364,7 +364,7 @@
     /**
      * Sets the persisted value for enabling/disabling Mobile data.
      *
-     * @param allowMobileData Whether the mobile data connection should be
+     * @param enabled Whether the mobile data connection should be
      *            used or not.
      * @hide
      */
@@ -418,22 +418,35 @@
     /**
      * {@hide}
      */
-    public boolean tether(String iface) {
+    public String[] getTetheringErroredIfaces() {
         try {
-            return mService.tether(iface);
+            return mService.getTetheringErroredIfaces();
         } catch (RemoteException e) {
-            return false;
+            return new String[0];
         }
     }
 
     /**
+     * @return error A TETHER_ERROR value indicating success or failure type
      * {@hide}
      */
-    public boolean untether(String iface) {
+    public int tether(String iface) {
+        try {
+            return mService.tether(iface);
+        } catch (RemoteException e) {
+            return TETHER_ERROR_SERVICE_UNAVAIL;
+        }
+    }
+
+    /**
+     * @return error A TETHER_ERROR value indicating success or failure type
+     * {@hide}
+     */
+    public int untether(String iface) {
         try {
             return mService.untether(iface);
         } catch (RemoteException e) {
-            return false;
+            return TETHER_ERROR_SERVICE_UNAVAIL;
         }
     }
 
@@ -469,4 +482,41 @@
             return new String[0];
         }
     }
+
+    /** {@hide} */
+    public static final int TETHER_ERROR_NO_ERROR           = 0;
+    /** {@hide} */
+    public static final int TETHER_ERROR_UNKNOWN_IFACE      = 1;
+    /** {@hide} */
+    public static final int TETHER_ERROR_SERVICE_UNAVAIL    = 2;
+    /** {@hide} */
+    public static final int TETHER_ERROR_UNSUPPORTED        = 3;
+    /** {@hide} */
+    public static final int TETHER_ERROR_UNAVAIL_IFACE      = 4;
+    /** {@hide} */
+    public static final int TETHER_ERROR_MASTER_ERROR       = 5;
+    /** {@hide} */
+    public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6;
+    /** {@hide} */
+    public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7;
+    /** {@hide} */
+    public static final int TETHER_ERROR_ENABLE_NAT_ERROR     = 8;
+    /** {@hide} */
+    public static final int TETHER_ERROR_DISABLE_NAT_ERROR    = 9;
+    /** {@hide} */
+    public static final int TETHER_ERROR_IFACE_CFG_ERROR      = 10;
+
+    /**
+     * @param iface The name of the interface we're interested in
+     * @return error The error code of the last error tethering or untethering the named
+     *               interface
+     * {@hide}
+     */
+    public int getLastTetherError(String iface) {
+        try {
+            return mService.getLastTetherError(iface);
+        } catch (RemoteException e) {
+            return TETHER_ERROR_SERVICE_UNAVAIL;
+        }
+   }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 2514693..b05c2ed 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -55,9 +55,11 @@
 
     void setMobileDataEnabled(boolean enabled);
 
-    boolean tether(String iface);
+    int tether(String iface);
 
-    boolean untether(String iface);
+    int untether(String iface);
+
+    int getLastTetherError(String iface);
 
     boolean isTetheringSupported();
 
@@ -65,6 +67,8 @@
 
     String[] getTetheredIfaces();
 
+    String[] getTetheringErroredIfaces();
+
     String[] getTetherableUsbRegexs();
 
     String[] getTetherableWifiRegexs();
diff --git a/core/java/android/pim/vcard/VCardConfig.java b/core/java/android/pim/vcard/VCardConfig.java
index ecd089a..3442ae7 100644
--- a/core/java/android/pim/vcard/VCardConfig.java
+++ b/core/java/android/pim/vcard/VCardConfig.java
@@ -330,7 +330,7 @@
         (VCARD_TYPE_V21_JAPANESE_MOBILE | FLAG_DOCOMO);
 
     /* package */ static final String VCARD_TYPE_DOCOMO_STR = "docomo";
-    
+
     public static int VCARD_TYPE_DEFAULT = VCARD_TYPE_V21_GENERIC_UTF8;
 
     private static final Map<String, Integer> sVCardTypeMap;
@@ -363,6 +363,8 @@
         final String loweredKey = vcardTypeString.toLowerCase();
         if (sVCardTypeMap.containsKey(loweredKey)) {
             return sVCardTypeMap.get(loweredKey);
+        } else if ("default".equalsIgnoreCase(vcardTypeString)) {
+            return VCARD_TYPE_DEFAULT;
         } else {
             Log.e(LOG_TAG, "Unknown vCard type String: \"" + vcardTypeString + "\"");
             return VCARD_TYPE_DEFAULT;
diff --git a/core/java/android/pim/vcard/VCardEntry.java b/core/java/android/pim/vcard/VCardEntry.java
index 1cf3144..f6135fc 100644
--- a/core/java/android/pim/vcard/VCardEntry.java
+++ b/core/java/android/pim/vcard/VCardEntry.java
@@ -61,7 +61,7 @@
     private final static int DEFAULT_ORGANIZATION_TYPE = Organization.TYPE_WORK;
 
     private static final String ACCOUNT_TYPE_GOOGLE = "com.google";
-    private static final String GOOGLE_MY_CONTACTS_GROUP = "System Group: My Contacts";
+    private static final String GOOGLE_MY_CONTACTS_GROUP = "My Contacts";
 
     private static final Map<String, Integer> sImMap = new HashMap<String, Integer>();
 
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 8140d61..d8b6d1f 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -154,9 +154,8 @@
         boolean handled = true;
 
         if (!mGestureInProgress) {
-            if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
-                    action == MotionEvent.ACTION_POINTER_2_DOWN) &&
-                    event.getPointerCount() >= 2) {
+            switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_POINTER_DOWN: {
                 // We have a new multi-finger gesture
 
                 // as orientation can change, query the metrics in touch down
@@ -189,7 +188,7 @@
                 boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
                         || x1 > rightSlop || y1 > bottomSlop;
 
-                if(p0sloppy && p1sloppy) {
+                if (p0sloppy && p1sloppy) {
                     mFocusX = -1;
                     mFocusY = -1;
                     mSloppyGesture = true;
@@ -204,54 +203,61 @@
                 } else {
                     mGestureInProgress = mListener.onScaleBegin(this);
                 }
-            } else if (action == MotionEvent.ACTION_MOVE && mSloppyGesture) {
-                // Initiate sloppy gestures if we've moved outside of the slop area.
-                final float edgeSlop = mEdgeSlop;
-                final float rightSlop = mRightSlopEdge;
-                final float bottomSlop = mBottomSlopEdge;
-                final float x0 = event.getRawX();
-                final float y0 = event.getRawY();
-                final float x1 = getRawX(event, 1);
-                final float y1 = getRawY(event, 1);
+            }
+            break;
+            
+            case MotionEvent.ACTION_MOVE:
+                if (mSloppyGesture) {
+                    // Initiate sloppy gestures if we've moved outside of the slop area.
+                    final float edgeSlop = mEdgeSlop;
+                    final float rightSlop = mRightSlopEdge;
+                    final float bottomSlop = mBottomSlopEdge;
+                    final float x0 = event.getRawX();
+                    final float y0 = event.getRawY();
+                    final float x1 = getRawX(event, 1);
+                    final float y1 = getRawY(event, 1);
 
-                boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop
-                        || x0 > rightSlop || y0 > bottomSlop;
-                boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
-                        || x1 > rightSlop || y1 > bottomSlop;
+                    boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop
+                    || x0 > rightSlop || y0 > bottomSlop;
+                    boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
+                    || x1 > rightSlop || y1 > bottomSlop;
 
-                if(p0sloppy && p1sloppy) {
-                    mFocusX = -1;
-                    mFocusY = -1;
-                } else if (p0sloppy) {
-                    mFocusX = event.getX(1);
-                    mFocusY = event.getY(1);
-                } else if (p1sloppy) {
-                    mFocusX = event.getX(0);
-                    mFocusY = event.getY(0);
-                } else {
-                    mSloppyGesture = false;
-                    mGestureInProgress = mListener.onScaleBegin(this);
+                    if(p0sloppy && p1sloppy) {
+                        mFocusX = -1;
+                        mFocusY = -1;
+                    } else if (p0sloppy) {
+                        mFocusX = event.getX(1);
+                        mFocusY = event.getY(1);
+                    } else if (p1sloppy) {
+                        mFocusX = event.getX(0);
+                        mFocusY = event.getY(0);
+                    } else {
+                        mSloppyGesture = false;
+                        mGestureInProgress = mListener.onScaleBegin(this);
+                    }
                 }
-            } else if ((action == MotionEvent.ACTION_POINTER_1_UP
-                    || action == MotionEvent.ACTION_POINTER_2_UP)
-                    && mSloppyGesture) {
-                // Set focus point to the remaining finger
-                int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
-                        >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
-                mFocusX = event.getX(id);
-                mFocusY = event.getY(id);
+                break;
+                
+            case MotionEvent.ACTION_POINTER_UP:
+                if (mSloppyGesture) {
+                    // Set focus point to the remaining finger
+                    int id = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
+                            >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0;
+                    mFocusX = event.getX(id);
+                    mFocusY = event.getY(id);
+                }
+                break;
             }
         } else {
             // Transform gesture in progress - attempt to handle it
-            switch (action) {
-                case MotionEvent.ACTION_POINTER_1_UP:
-                case MotionEvent.ACTION_POINTER_2_UP:
+            switch (action & MotionEvent.ACTION_MASK) {
+                case MotionEvent.ACTION_POINTER_UP:
                     // Gesture ended
                     setContext(event);
 
                     // Set focus point to the remaining finger
-                    int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
-                            >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
+                    int id = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
+                            >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0;
                     mFocusX = event.getX(id);
                     mFocusY = event.getY(id);
 
diff --git a/core/java/android/widget/ExpandableListConnector.java b/core/java/android/widget/ExpandableListConnector.java
index 9c43e9b..01d3a4a 100644
--- a/core/java/android/widget/ExpandableListConnector.java
+++ b/core/java/android/widget/ExpandableListConnector.java
@@ -67,7 +67,7 @@
     private int mMaxExpGroupCount = Integer.MAX_VALUE;
 
     /** Change observer used to have ExpandableListAdapter changes pushed to us */
-    private DataSetObserver mDataSetObserver = new MyDataSetObserver();
+    private final DataSetObserver mDataSetObserver = new MyDataSetObserver();
 
     /**
      * Constructs the connector
@@ -849,7 +849,7 @@
      * position to either a) group position for groups, or b) child position for
      * children
      */
-    static class GroupMetadata implements Parcelable, Comparable {
+    static class GroupMetadata implements Parcelable, Comparable<GroupMetadata> {
         final static int REFRESH = -1;
         
         /** This group's flat list position */
@@ -885,12 +885,12 @@
             return gm;
         }
         
-        public int compareTo(Object another) {
-            if (another == null || !(another instanceof GroupMetadata)) {
-                throw new ClassCastException();
+        public int compareTo(GroupMetadata another) {
+            if (another == null) {
+                throw new IllegalArgumentException();
             }
             
-            return gPos - ((GroupMetadata) another).gPos;
+            return gPos - another.gPos;
         }
 
         public int describeContents() {
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index 9cc8bd5..8bd797b 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -389,7 +389,8 @@
         // Only proceed as possible child if the divider isn't above all items (if it is above
         // all items, then the item below it has to be a group)
         if (flatListPosition >= 0) {
-            PositionMetadata pos = mConnector.getUnflattenedPos(flatListPosition);
+            final int adjustedPosition = getFlatPositionForConnector(flatListPosition);
+            PositionMetadata pos = mConnector.getUnflattenedPos(adjustedPosition);
             // If this item is a child, or it is a non-empty group that is expanded
             if ((pos.position.type == ExpandableListPosition.CHILD) || (pos.isExpanded() &&
                     pos.groupMetadata.lastChildFlPos != pos.groupMetadata.flPos)) {
@@ -482,11 +483,37 @@
         return mAdapter;
     }
     
+    /**
+     * @param position An absolute (including header and footer) flat list position.
+     * @return true if the position corresponds to a header or a footer item.
+     */
     private boolean isHeaderOrFooterPosition(int position) {
         final int footerViewsStart = mItemCount - getFooterViewsCount();
         return (position < getHeaderViewsCount() || position >= footerViewsStart);
     }
 
+    /**
+     * Converts an absolute item flat position into a group/child flat position, shifting according
+     * to the number of header items.
+     * 
+     * @param flatListPosition The absolute flat position
+     * @return A group/child flat position as expected by the connector.
+     */
+    private int getFlatPositionForConnector(int flatListPosition) {
+        return flatListPosition - getHeaderViewsCount();
+    }
+
+    /**
+     * Converts a group/child flat position into an absolute flat position, that takes into account
+     * the possible headers.
+     * 
+     * @param flatListPosition The child/group flat position
+     * @return An absolute flat position.
+     */
+    private int getAbsoluteFlatPosition(int flatListPosition) {
+        return flatListPosition + getHeaderViewsCount();
+    }
+
     @Override
     public boolean performItemClick(View v, int position, long id) {
         // Ignore clicks in header/footers
@@ -496,8 +523,8 @@
         }
         
         // Internally handle the item click
-        final int headerViewsCount = getHeaderViewsCount();
-        return handleItemClick(v, position - headerViewsCount, id);
+        final int adjustedPosition = getFlatPositionForConnector(position);
+        return handleItemClick(v, adjustedPosition, id);
     }
     
     /**
@@ -711,8 +738,8 @@
             return PACKED_POSITION_VALUE_NULL;
         }
 
-        final int shiftedPosition = flatListPosition - getHeaderViewsCount();
-        PositionMetadata pm = mConnector.getUnflattenedPos(shiftedPosition);
+        final int adjustedPosition = getFlatPositionForConnector(flatListPosition);
+        PositionMetadata pm = mConnector.getUnflattenedPos(adjustedPosition);
         long packedPos = pm.position.getPackedPosition();
         pm.recycle();
         return packedPos;
@@ -732,9 +759,9 @@
     public int getFlatListPosition(long packedPosition) {
         PositionMetadata pm = mConnector.getFlattenedPos(ExpandableListPosition
                 .obtainPosition(packedPosition));
-        int retValue = pm.position.flatListPos;
+        final int flatListPosition = pm.position.flatListPos;
         pm.recycle();
-        return retValue + getHeaderViewsCount();
+        return getAbsoluteFlatPosition(flatListPosition);
     }
 
     /**
@@ -783,7 +810,8 @@
                 .obtainGroupPosition(groupPosition);
         PositionMetadata pm = mConnector.getFlattenedPos(elGroupPos);
         elGroupPos.recycle();
-        super.setSelection(pm.position.flatListPos);
+        final int absoluteFlatPosition = getAbsoluteFlatPosition(pm.position.flatListPos);
+        super.setSelection(absoluteFlatPosition);
         pm.recycle();
     }
     
@@ -819,7 +847,8 @@
             }
         }
         
-        super.setSelection(flatChildPos.position.flatListPos);
+        int absoluteFlatPosition = getAbsoluteFlatPosition(flatChildPos.position.flatListPos);
+        super.setSelection(absoluteFlatPosition);
         
         elChildPos.recycle();
         flatChildPos.recycle();
@@ -937,7 +966,7 @@
             return new AdapterContextMenuInfo(view, flatListPosition, id);
         }
 
-        final int adjustedPosition = flatListPosition - getHeaderViewsCount();
+        final int adjustedPosition = getFlatPositionForConnector(flatListPosition);
         PositionMetadata pm = mConnector.getUnflattenedPos(adjustedPosition);
         ExpandableListPosition pos = pm.position;
         pm.recycle();
diff --git a/core/java/com/android/internal/app/TetherActivity.java b/core/java/com/android/internal/app/TetherActivity.java
index 5d71231..7f83b2b 100644
--- a/core/java/com/android/internal/app/TetherActivity.java
+++ b/core/java/com/android/internal/app/TetherActivity.java
@@ -62,6 +62,7 @@
         // determine if we advertise tethering or untethering
         ConnectivityManager cm =
                 (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+
         mTethered = cm.getTetheredIfaces().length;
         int tetherable = cm.getTetherableIfaces().length;
         if ((mTethered == 0) && (tetherable == 0)) {
@@ -116,7 +117,7 @@
      * {@inheritDoc}
      */
     public void onClick(DialogInterface dialog, int which) {
-        boolean error =  false;
+        int error = ConnectivityManager.TETHER_ERROR_NO_ERROR;
 
         if (which == POSITIVE_BUTTON) {
             ConnectivityManager cm =
@@ -130,24 +131,17 @@
                 for (String t : tetherable) {
                     for (String r : usbRegexs) {
                         if (t.matches(r)) {
-                            if (!cm.tether(t))
-                                error = true;
+                            error = cm.tether(t);
                             break;
                         }
                     }
                 }
-                if (error) {
-                    showTetheringError();
-                }
+                showTetheringError(error);
             } else {
                 for (String t : tethered) {
-                    if (!cm.untether(t)) {
-                        error = true;
-                    }
+                    error = cm.untether(t);
                 }
-                if (error) {
-                    showUnTetheringError();
-                }
+                showUnTetheringError(error);
             }
         }
         // No matter what, finish the activity
@@ -163,14 +157,23 @@
         }
     }
 
-    private void showTetheringError() {
-        Toast.makeText(this, com.android.internal.R.string.tether_error_message,
-                Toast.LENGTH_LONG).show();
+    private void showTetheringError(int error) {
+        switch(error) {
+        case ConnectivityManager.TETHER_ERROR_NO_ERROR:
+            return;
+        default:
+            Toast.makeText(this, com.android.internal.R.string.tether_error_message,
+                    Toast.LENGTH_LONG).show();
+        }
     }
 
-    private void showUnTetheringError() {
-        Toast.makeText(this, com.android.internal.R.string.tether_stop_error_message,
-                Toast.LENGTH_LONG).show();
+    private void showUnTetheringError(int error) {
+        switch(error) {
+        case ConnectivityManager.TETHER_ERROR_NO_ERROR:
+            return;
+        default:
+            Toast.makeText(this, com.android.internal.R.string.tether_stop_error_message,
+                    Toast.LENGTH_LONG).show();
+        }
     }
-
 }
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index c5db83f..04a10b9 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -54,11 +54,13 @@
         // Create mount point via MountService
         IMountService mountService = getMountService();
         long len = tmpPackageFile.length();
-        int mbLen = (int) (len/(1024*1024));
+        int mbLen = (int) (len >> 20);
         if ((len - (mbLen * 1024 * 1024)) > 0) {
             mbLen++;
         }
-        if (localLOGV) Log.i(TAG, "Size of resource " + mbLen);
+        // Add buffer size
+        mbLen++;
+        if (localLOGV) Log.i(TAG, "Size of container " + mbLen + " MB " + len + " bytes");
 
         try {
             int rc = mountService.createSecureContainer(
diff --git a/core/res/res/drawable-hdpi/ic_btn_search_play.png b/core/res/res/drawable-hdpi/ic_btn_search_play.png
new file mode 100644
index 0000000..fb1b974
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_search_play.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_btn_search_play.png b/core/res/res/drawable-mdpi/ic_btn_search_play.png
new file mode 100644
index 0000000..dc25dae
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_btn_search_play.png
Binary files differ
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 02961f0..84cd4c3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -267,6 +267,11 @@
          it will be removed when the lower-level touch driver generates better
          data. -->
     <bool name="config_filterTouchEvents">false</bool>
+
+    <!-- Enables special filtering code in the framework for raw touch events
+         from the touch driver. This code exists for one particular device,
+         and should not be enabled for any others. -->
+    <bool name="config_filterJumpyTouchEvents">false</bool>
     
     <!-- Component name of the default wallpaper. This will be ImageWallpaper if not 
          specified -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 35ea0cc..d34599f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2249,8 +2249,10 @@
     <string name="tether_button">Tether</string>
     <!-- See TETHER.   This is the button text to ignore the plugging in of the phone.. -->
     <string name="tether_button_cancel">Cancel</string>
-    <!-- See TETHER.  If there was an error mounting, this is the text. -->
-    <string name="tether_error_message">There is a problem tethering.</string>
+
+    <!-- See TETHER.  If there was a recoverable error, this is the text. -->
+    <string name="tether_error_message">We\'ve encountered a problem turning on Tethering.  Please try again.</string>
+
     <!-- TETHER: When the user connects the phone to a computer, we show a notification asking if he wants to share his cellular network connection.  This is the title -->
     <string name="tether_available_notification_title">USB tethering available</string>
     <!-- See USB_STORAGE. This is the message. -->
@@ -2270,7 +2272,8 @@
     <string name="tether_stop_button">Disconnect</string>
     <!-- See TETHER_STOP.   This is the button text to cancel disconnecting the tether. -->
     <string name="tether_stop_button_cancel">Cancel</string>
-    <!-- See TETHER_STOP_DIALOG.  If there was an error disconnect, this is the text. -->
+
+    <!-- See TETHER_STOP.  If there was an error disconnect, this is the text. -->
     <string name="tether_stop_error_message">We\'ve encountered a problem turning off Tethering. Please try again.</string>
 
     <!-- Strings for car mode notification -->
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index cd8361c..d280f50 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -202,6 +202,12 @@
     RS_PRIMITIVE_TRIANGLE_FAN
 };
 
+enum RsError {
+    RS_ERROR_NONE,
+    RS_ERROR_BAD_SHADER,
+    RS_ERROR_BAD_SCRIPT
+};
+
 #ifndef NO_RS_FUNCS
 #include "rsgApiFuncDecl.h"
 #endif
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 4d97c0f..cb9937c 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -36,6 +36,11 @@
 	param int32_t bits
 }
 
+ContextGetError {
+	param RsError *err
+	ret const char *
+	}
+
 ContextSetPriority {
 	param int32_t priority
 	}
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index cc3a74fb..d8a9a99 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -178,6 +178,11 @@
     uint32_t ret = runScript(mRootScript.get(), 0);
 
     checkError("runRootScript");
+    if (mError != RS_ERROR_NONE) {
+        // If we have an error condition we stop rendering until
+        // somthing changes that might fix it.
+        ret = 0;
+    }
     return ret;
 }
 
@@ -240,10 +245,13 @@
     }
 }
 
-void Context::setupCheck()
+bool Context::setupCheck()
 {
     if (checkVersion2_0()) {
-        mShaderCache.lookup(this, mVertex.get(), mFragment.get());
+        if (!mShaderCache.lookup(this, mVertex.get(), mFragment.get())) {
+            LOGE("Context::setupCheck() 1 fail");
+            return false;
+        }
 
         mFragmentStore->setupGL2(this, &mStateFragmentStore);
         mFragment->setupGL2(this, &mStateFragment, &mShaderCache);
@@ -256,6 +264,7 @@
         mRaster->setupGL(this, &mStateRaster);
         mVertex->setupGL(this, &mStateVertex);
     }
+    return true;
 }
 
 static bool getProp(const char *str)
@@ -389,6 +398,9 @@
     mUseDepth = useDepth;
     mPaused = false;
     mObjHead = NULL;
+    mError = RS_ERROR_NONE;
+    mErrorMsg = NULL;
+
     memset(&mEGL, 0, sizeof(mEGL));
     memset(&mGL, 0, sizeof(mGL));
     mIsGraphicsContext = isGraphics;
@@ -764,6 +776,23 @@
     mIO.mToClient.shutdown();
 }
 
+const char * Context::getError(RsError *err)
+{
+    *err = mError;
+    mError = RS_ERROR_NONE;
+    if (*err != RS_ERROR_NONE) {
+        return mErrorMsg;
+    }
+    return NULL;
+}
+
+void Context::setError(RsError e, const char *msg)
+{
+    mError = e;
+    mErrorMsg = msg;
+}
+
+
 void Context::dumpDebug() const
 {
     LOGE("RS Context debug %p", this);
@@ -874,6 +903,15 @@
     ObjectBase::dumpAll(rsc);
 }
 
+const char * rsi_ContextGetError(Context *rsc, RsError *e)
+{
+    const char *msg = rsc->getError(e);
+    if (*e != RS_ERROR_NONE) {
+        LOGE("RS Error %i %s", *e, msg);
+    }
+    return msg;
+}
+
 }
 }
 
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 04bd748..82c3687 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -93,7 +93,7 @@
     const ProgramRaster * getRaster() {return mRaster.get();}
     const ProgramVertex * getVertex() {return mVertex.get();}
 
-    void setupCheck();
+    bool setupCheck();
     bool checkDriver() const {return mEGL.mSurface != 0;}
 
     void pause();
@@ -160,6 +160,8 @@
 
     void dumpDebug() const;
     void checkError(const char *) const;
+    const char * getError(RsError *);
+    void setError(RsError e, const char *msg);
 
     mutable const ObjectBase * mObjHead;
 
@@ -211,6 +213,8 @@
     bool mExit;
     bool mUseDepth;
     bool mPaused;
+    RsError mError;
+    const char *mErrorMsg;
 
     pthread_t mThreadId;
     pid_t mNativeThreadId;
diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp
index 656a3c3..478a6dc 100644
--- a/libs/rs/rsProgram.cpp
+++ b/libs/rs/rsProgram.cpp
@@ -39,6 +39,7 @@
     mInputCount = 0;
     mOutputCount = 0;
     mConstantCount = 0;
+    mIsValid = false;
 }
 
 Program::Program(Context *rsc, const char * shaderText, uint32_t shaderLength,
@@ -216,6 +217,7 @@
                 }
                 glDeleteShader(mShaderID);
                 mShaderID = 0;
+                rsc->setError(RS_ERROR_BAD_SHADER, "Error returned from GL driver loading shader text,");
                 return false;
             }
         }
@@ -224,6 +226,7 @@
     if (rsc->props.mLogShaders) {
         LOGV("--Shader load result %x ", glGetError());
     }
+    mIsValid = true;
     return true;
 }
 
diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h
index a34e89f..86f85fb 100644
--- a/libs/rs/rsProgram.h
+++ b/libs/rs/rsProgram.h
@@ -59,6 +59,8 @@
     String8 getGLSLOutputString() const;
     String8 getGLSLConstantString() const;
 
+    bool isValid() const {return mIsValid;}
+
 protected:
     // Components not listed in "in" will be passed though
     // unless overwritten by components in out.
@@ -68,6 +70,7 @@
     uint32_t mInputCount;
     uint32_t mOutputCount;
     uint32_t mConstantCount;
+    bool mIsValid;
 
     ObjectBaseRef<Allocation> mConstants[MAX_UNIFORMS];
 
diff --git a/libs/rs/rsScript.cpp b/libs/rs/rsScript.cpp
index cb1436b..a33933b 100644
--- a/libs/rs/rsScript.cpp
+++ b/libs/rs/rsScript.cpp
@@ -96,6 +96,10 @@
 void rsi_ScriptInvoke(Context *rsc, RsScript vs, uint32_t slot)
 {
     Script *s = static_cast<Script *>(vs);
+    if (s->mEnviroment.mInvokables[slot] == NULL) {
+        rsc->setError(RS_ERROR_BAD_SCRIPT, "Calling invoke on bad script");
+        return;
+    }
     s->setupScript();
     s->mEnviroment.mInvokables[slot]();
 }
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index b7e0b86..1f23773 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -62,6 +62,11 @@
 
 uint32_t ScriptC::run(Context *rsc, uint32_t launchIndex)
 {
+    if (mProgram.mScript == NULL) {
+        rsc->setError(RS_ERROR_BAD_SCRIPT, "Attempted to run bad script");
+        return 0;
+    }
+
     Context::ScriptTLSStruct * tls =
     (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey);
     rsAssert(tls);
@@ -154,7 +159,9 @@
         ACCchar buf[4096];
         ACCsizei len;
         accGetScriptInfoLog(s->mAccScript, sizeof(buf), &len, buf);
-        LOGV(buf);
+        LOGE(buf);
+        rsc->setError(RS_ERROR_BAD_SCRIPT, "Error compiling user script.");
+        return;
     }
 
     if (s->mProgram.mInit) {
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 235c153..202ca3d 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -683,7 +683,9 @@
                         float x2, float y2, float z2)
 {
     GET_TLS();
-    rsc->setupCheck();
+    if (!rsc->setupCheck()) {
+        return;
+    }
 
     float vtx[] = { x1, y1, z1, x2, y2, z2 };
     VertexArray va;
@@ -700,7 +702,9 @@
 static void SC_drawPoint(float x, float y, float z)
 {
     GET_TLS();
-    rsc->setupCheck();
+    if (!rsc->setupCheck()) {
+        return;
+    }
 
     float vtx[] = { x, y, z };
 
@@ -725,7 +729,9 @@
                                  float u4, float v4)
 {
     GET_TLS();
-    rsc->setupCheck();
+    if (!rsc->setupCheck()) {
+        return;
+    }
 
     //LOGE("Quad");
     //LOGE("%4.2f, %4.2f, %4.2f", x1, y1, z1);
@@ -782,7 +788,9 @@
         float cx0, float cy0, float cx1, float cy1)
 {
     GET_TLS();
-    rsc->setupCheck();
+    if (!rsc->setupCheck()) {
+        return;
+    }
 
     GLint crop[4] = {cx0, cy0, cx1, cy1};
     glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
@@ -831,7 +839,9 @@
 {
     GET_TLS();
     SimpleMesh *sm = static_cast<SimpleMesh *>(vsm);
-    rsc->setupCheck();
+    if (!rsc->setupCheck()) {
+        return;
+    }
     sm->render(rsc);
 }
 
@@ -839,7 +849,9 @@
 {
     GET_TLS();
     SimpleMesh *sm = static_cast<SimpleMesh *>(vsm);
-    rsc->setupCheck();
+    if (!rsc->setupCheck()) {
+        return;
+    }
     sm->renderRange(rsc, start, len);
 }
 
diff --git a/libs/rs/rsShaderCache.cpp b/libs/rs/rsShaderCache.cpp
index 3a1f370..4711d1b 100644
--- a/libs/rs/rsShaderCache.cpp
+++ b/libs/rs/rsShaderCache.cpp
@@ -123,6 +123,8 @@
                 }
             }
             glDeleteProgram(pgm);
+            rsc->setError(RS_ERROR_BAD_SHADER, "Error linking GL Programs");
+            return false;
         }
         if (vtx->isUserProgram()) {
             for (uint32_t ct=0; ct < vtx->getAttribCount(); ct++) {
@@ -146,6 +148,7 @@
         }
     }
 
+    e->mIsValid = true;
     //LOGV("SC made program %i", e->program);
     glUseProgram(e->program);
     mEntryCount++;
diff --git a/libs/rs/rsShaderCache.h b/libs/rs/rsShaderCache.h
index 7aa8183..df99ccc 100644
--- a/libs/rs/rsShaderCache.h
+++ b/libs/rs/rsShaderCache.h
@@ -56,6 +56,7 @@
         int32_t mFragAttribSlots[Program::MAX_ATTRIBS];
         int32_t mFragUniformSlots[Program::MAX_UNIFORMS];
         bool mUserVertexProgram;
+        bool mIsValid;
     } entry_t;
     entry_t *mEntries;
     entry_t *mCurrent;
diff --git a/libs/rs/spec.l b/libs/rs/spec.l
index d81d47e..6a9010fe 100644
--- a/libs/rs/spec.l
+++ b/libs/rs/spec.l
@@ -148,6 +148,9 @@
     BEGIN(api_entry2);
     }
 
+<api_entry2>"*" {
+    currType->ptrLevel ++;
+    }
 
 <api_entry2>"}" {
     apiCount++;
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index da760a1..9e4a16b 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -317,7 +317,7 @@
         List<String> providers = getProviders(enabledOnly);
         for (String providerName : providers) {
             LocationProvider provider = getProvider(providerName);
-            if (provider.meetsCriteria(criteria)) {
+            if (provider != null && provider.meetsCriteria(criteria)) {
                 if (goodProviders.isEmpty()) {
                     goodProviders = new ArrayList<String>();
                 }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index a36ee85..e586869 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -450,10 +450,20 @@
         VolumeStreamState streamState = mStreamStates[streamType];
         if (streamState.setIndex(index, lastAudible) || force) {
             // Post message to set system volume (it in turn will post a message
-            // to persist). Do not change volume if stream is muted.
-            if (streamState.muteCount() == 0) {
+            // to persist).
+            // If stream is muted or we are in silent mode and stream is affected by ringer mode
+            // and the new volume is not 0, just persist the new volume but do not change
+            // current value
+            if (streamState.muteCount() == 0 &&
+                (mRingerMode == AudioManager.RINGER_MODE_NORMAL ||
+                !isStreamAffectedByRingerMode(streamType) ||
+                index == 0)) {
                 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
                         streamState, 0);
+            } else {
+                // Post a persist volume msg
+                sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
+                        SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
             }
         }
     }
@@ -512,7 +522,7 @@
                 if (!isStreamAffectedByRingerMode(streamType)) continue;
                 // Bring back last audible volume
                 setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
-                                   false, false);
+                                   true, false);
             }
         } else {
             for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
@@ -524,7 +534,7 @@
                     // to non affected by ringer mode. Does not arm to do it for streams that
                     // are not affected as well.
                     setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
-                            false, false);
+                            true, false);
                 }
             }
         }
@@ -1269,14 +1279,18 @@
 
             // Post a persist volume msg
             sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
-                    SENDMSG_REPLACE, 0, 0, streamState, PERSIST_DELAY);
+                    SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY);
         }
 
-        private void persistVolume(VolumeStreamState streamState) {
-            System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
-                    (streamState.mIndex + 5)/ 10);
-            System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
+        private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) {
+            if (current) {
+                System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
+                              (streamState.mIndex + 5)/ 10);
+            }
+            if (lastAudible) {
+                System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
                     (streamState.mLastAudibleIndex + 5) / 10);
+            }
         }
 
         private void persistRingerMode() {
@@ -1361,7 +1375,7 @@
                     break;
 
                 case MSG_PERSIST_VOLUME:
-                    persistVolume((VolumeStreamState) msg.obj);
+                    persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0));
                     break;
 
                 case MSG_PERSIST_RINGER_MODE:
@@ -1469,7 +1483,7 @@
                         //  and persist with no delay as there might be registered observers of the persisted
                         //  notification volume.
                         sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION,
-                                SENDMSG_REPLACE, 0, 0, mStreamStates[AudioSystem.STREAM_NOTIFICATION], 0);
+                                SENDMSG_REPLACE, 1, 1, mStreamStates[AudioSystem.STREAM_NOTIFICATION], 0);
                     }
                 }
             }
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 15e35010..2c5cbf6 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -225,46 +225,6 @@
         public static final int MPEG_4_SP = 3;
     }
 
-
-    /**
-     * @hide Defines the audio sampling rate. This must be set before
-     * setAudioEncoder() or it will be ignored.
-     * This parameter is used with
-     * {@link MediaRecorder#setParameters(String)}.
-     */
-    public final class AudioParamSamplingRate {
-      /* Do not change these values without updating their counterparts
-       * in include/media/mediarecorder.h!
-       */
-        private AudioParamSamplingRate() {}
-        public static final String AUDIO_PARAM_SAMPLING_RATE_KEY = "audio-param-sampling-rate=";
-    }
-
-     /**
-     * @hide Defines the audio number of channels. This must be set before
-     * setAudioEncoder() or it will be ignored.
-     * This parameter is used with
-     * {@link MediaRecorder#setParameters(String)}.
-     */
-    public final class AudioParamChannels {
-      /* Do not change these values without updating their counterparts
-       * in include/media/mediarecorder.h!
-       */
-        private AudioParamChannels() {}
-        public static final String AUDIO_PARAM_NUMBER_OF_CHANNELS = "audio-param-number-of-channels=";
-    }
-
-     /**
-     * @hide Defines the audio encoding bitrate. This must be set before
-     * setAudioEncoder() or it will be ignored.
-     * This parameter is used with
-     * {@link MediaRecorder#setParameters(String)}.
-     */
-    public final class AudioParamEncodingBitrate{
-        private AudioParamEncodingBitrate() {}
-        public static final String AUDIO_PARAM_ENCODING_BITRATE = "audio-param-encoding-bitrate=";
-    }
-
     /**
      * Sets the audio source to be used for recording. If this method is not
      * called, the output file will not contain an audio track. The source needs
@@ -399,14 +359,69 @@
             throws IllegalStateException;
 
     /**
-     * @hide Sets a parameter in the author engine.
+     * Sets the audio sampling rate for recording. Call this method before prepare().
+     * Prepare() may perform additional checks on the parameter to make sure whether
+     * the specified audio sampling rate is applicable. The sampling rate really depends
+     * on the format for the audio recording, as well as the capabilities of the platform.
+     * For instance, the sampling rate supported by AAC audio coding standard ranges
+     * from 8 to 96 kHz. Please consult with the related audio coding standard for the
+     * supported audio sampling rate.
      *
-     * @param params the parameter to set.
-     * @see android.media.MediaRecorder.AudioParamSamplingRate
-     * @see android.media.MediaRecorder.AudioParamChannels
-     * @see android.media.MediaRecorder.AudioParamEncodingBitrate
+     * @param samplingRate the sampling rate for audio in samples per second.
      */
-    public native void setParameters(String params);
+    public void setAudioSamplingRate(int samplingRate) {
+        if (samplingRate <= 0) {
+            throw new IllegalArgumentException("Audio sampling rate is not positive");
+        }
+        setParameter(String.format("audio-param-sampling-rate=%d", samplingRate));
+    }
+
+    /**
+     * Sets the number of audio channels for recording. Call this method before prepare().
+     * Prepare() may perform additional checks on the parameter to make sure whether the
+     * specified number of audio channels are applicable.
+     *
+     * @param numChannels the number of audio channels. Usually it is either 1 (mono) or 2
+     * (stereo).
+     */
+    public void setAudioChannels(int numChannels) {
+        if (numChannels <= 0) {
+            throw new IllegalArgumentException("Number of channels is not positive");
+        }
+        setParameter(String.format("audio-param-number-of-channels=%d", numChannels));
+    }
+
+    /**
+     * Sets the audio encoding bit rate for recording. Call this method before prepare().
+     * Prepare() may perform additional checks on the parameter to make sure whether the
+     * specified bit rate is applicable, and sometimes the passed bitRate will be clipped
+     * internally to ensure the audio recording can proceed smoothly based on the
+     * capabilities of the platform.
+     *
+     * @param bitRate the audio encoding bit rate in bits per second.
+     */
+    public void setAudioEncodingBitRate(int bitRate) {
+        if (bitRate <= 0) {
+            throw new IllegalArgumentException("Audio encoding bit rate is not positive");
+        }
+        setParameter(String.format("audio-param-encoding-bitrate=%d", bitRate));
+    }
+
+    /**
+     * Sets the video encoding bit rate for recording. Call this method before prepare().
+     * Prepare() may perform additional checks on the parameter to make sure whether the
+     * specified bit rate is applicable, and sometimes the passed bitRate will be
+     * clipped internally to ensure the video recording can proceed smoothly based on
+     * the capabilities of the platform.
+     *
+     * @param bitRate the video encoding bit rate in bits per second.
+     */
+    public void setVideoEncodingBitRate(int bitRate) {
+        if (bitRate <= 0) {
+            throw new IllegalArgumentException("Video encoding bit rate is not positive");
+        }
+        setParameter(String.format("video-param-encoding-bitrate=%d", bitRate));
+    }
 
     /**
      * Pass in the file descriptor of the file to be written. Call this after
@@ -670,6 +685,8 @@
 
     private native final void native_finalize();
 
+    private native void setParameter(String nameValuePair);
+
     @Override
     protected void finalize() { native_finalize(); }
 }
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 3063f15..00af3a2 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -223,9 +223,9 @@
 }
 
 static void
-android_media_MediaRecorder_setParameters(JNIEnv *env, jobject thiz, jstring params)
+android_media_MediaRecorder_setParameter(JNIEnv *env, jobject thiz, jstring params)
 {
-    LOGV("setParameters()");
+    LOGV("setParameter()");
     if (params == NULL)
     {
         LOGE("Invalid or empty params string.  This parameter will be ignored.");
@@ -455,7 +455,7 @@
     {"setOutputFormat",      "(I)V",                            (void *)android_media_MediaRecorder_setOutputFormat},
     {"setVideoEncoder",      "(I)V",                            (void *)android_media_MediaRecorder_setVideoEncoder},
     {"setAudioEncoder",      "(I)V",                            (void *)android_media_MediaRecorder_setAudioEncoder},
-    {"setParameters",        "(Ljava/lang/String;)V",           (void *)android_media_MediaRecorder_setParameters},
+    {"setParameter",         "(Ljava/lang/String;)V",           (void *)android_media_MediaRecorder_setParameter},
     {"_setOutputFile",       "(Ljava/io/FileDescriptor;JJ)V",   (void *)android_media_MediaRecorder_setOutputFileFD},
     {"setVideoSize",         "(II)V",                           (void *)android_media_MediaRecorder_setVideoSize},
     {"setVideoFrameRate",    "(I)V",                            (void *)android_media_MediaRecorder_setVideoFrameRate},
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
index 39caccd..a52fd76 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java
@@ -141,14 +141,10 @@
             mMediaRecorder.setOutputFile(filename);
             mMediaRecorder.setVideoFrameRate(videoFps);
             mMediaRecorder.setVideoSize(videoWidth, videoHeight);
-            mMediaRecorder.setParameters(String.format("video-param-encoding-bitrate=%d",
-                    videoBitrate));
-            mMediaRecorder.setParameters(String.format("audio-param-encoding-bitrate=%d",
-                    audioBitrate));
-            mMediaRecorder.setParameters(String.format("audio-param-number-of-channels=%d",
-                    audioChannels));
-            mMediaRecorder.setParameters(String.format("audio-param-sampling-rate=%d",
-                    audioSamplingRate));
+            mMediaRecorder.setVideoEncodingBitRate(videoBitrate);
+            mMediaRecorder.setAudioEncodingBitRate(audioBitrate);
+            mMediaRecorder.setAudioChannels(audioChannels);
+            mMediaRecorder.setAudioSamplingRate(audioSamplingRate);
             mMediaRecorder.setVideoEncoder(videoEncoder);
             mMediaRecorder.setAudioEncoder(audioEncoder);
             mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
diff --git a/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp b/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp
index 2d05a49..7537e35 100644
--- a/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp
+++ b/media/tests/omxjpegdecoder/jpeg_decoder_bench.cpp
@@ -32,8 +32,7 @@
     }
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm,
-                          SkBitmap::Config pref, Mode);
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
 };
 
 int nullObjectReturn(const char msg[]) {
diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
index f229f9d..209e71c 100644
--- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
+++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
@@ -97,14 +97,14 @@
 }
 
 bool OmxJpegImageDecoder::onDecode(SkStream* stream,
-        SkBitmap* bm, SkBitmap::Config pref, Mode mode) {
+        SkBitmap* bm, Mode mode) {
     sp<MediaSource> source = prepareMediaSource(stream);
     sp<MetaData> meta = source->getFormat();
     int width;
     int height;
     meta->findInt32(kKeyWidth, &width);
     meta->findInt32(kKeyHeight, &height);
-    configBitmapSize(bm, pref, width, height);
+    configBitmapSize(bm, getPrefConfig(k32Bit_SrcDepth, false), width, height);
 
     // mode == DecodeBounds
     if (mode == SkImageDecoder::kDecodeBounds_Mode) {
diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.h b/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
index 7d8bac09c..a313877 100644
--- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
+++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
@@ -42,8 +42,7 @@
     }
 
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm,
-            SkBitmap::Config pref, Mode mode);
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
 
 private:
     JPEGSource* prepareMediaSource(SkStream* stream);
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index a79f0cd..c826973 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -166,62 +166,41 @@
         String codePath = packageURI.getPath();
         File codeFile = new File(codePath);
         String newCachePath = null;
-        final int CREATE_FAILED = 1;
-        final int COPY_FAILED = 2;
-        final int FINALIZE_FAILED = 3;
-        final int PASS = 4;
-        int errCode = CREATE_FAILED;
         // Create new container
         if ((newCachePath = PackageHelper.createSdDir(codeFile,
-                newCid, key, Process.myUid())) != null) {
-            if (localLOGV) Log.i(TAG, "Created container for " + newCid
-                    + " at path : " + newCachePath);
-            File resFile = new File(newCachePath, resFileName);
-            errCode = COPY_FAILED;
-            // Copy file from codePath
-            if (FileUtils.copyFile(new File(codePath), resFile)) {
-                if (localLOGV) Log.i(TAG, "Copied " + codePath + " to " + resFile);
-                errCode = FINALIZE_FAILED;
-                if (PackageHelper.finalizeSdDir(newCid)) {
-                    if (localLOGV) Log.i(TAG, "Finalized container " + newCid);
-                    errCode = PASS;
-                }
-            }
-        }
-        // Print error based on errCode
-        String errMsg = "";
-        switch (errCode) {
-            case CREATE_FAILED:
-                errMsg = "CREATE_FAILED";
-                break;
-            case COPY_FAILED:
-                errMsg = "COPY_FAILED";
-                if (localLOGV) Log.i(TAG, "Destroying " + newCid +
-                        " at path " + newCachePath + " after " + errMsg);
-                PackageHelper.destroySdDir(newCid);
-                break;
-            case FINALIZE_FAILED:
-                errMsg = "FINALIZE_FAILED";
-                if (localLOGV) Log.i(TAG, "Destroying " + newCid +
-                        " at path " + newCachePath + " after " + errMsg);
-                PackageHelper.destroySdDir(newCid);
-                break;
-            default:
-                errMsg = "PASS";
-                if (PackageHelper.isContainerMounted(newCid)) {
-                    if (localLOGV) Log.i(TAG, "Unmounting " + newCid +
-                            " at path " + newCachePath + " after " + errMsg);
-                    // Force a gc to avoid being killed.
-                    Runtime.getRuntime().gc();
-                    PackageHelper.unMountSdDir(newCid);
-                } else {
-                    if (localLOGV) Log.i(TAG, "Container " + newCid + " not mounted");
-                }
-                break;
-        }
-        if (errCode != PASS) {
+                newCid, key, Process.myUid())) == null) {
+            Log.e(TAG, "Failed creating container " + newCid);
             return null;
         }
+        if (localLOGV) Log.i(TAG, "Created container for " + newCid
+                + " at path : " + newCachePath);
+        File resFile = new File(newCachePath, resFileName);
+        // Copy file from codePath
+        if (!FileUtils.copyFile(new File(codePath), resFile)) {
+            Log.e(TAG, "Failed to copy " + codePath + " to " + resFile);
+            // Clean up created container
+            PackageHelper.destroySdDir(newCid);
+            return null;
+        }
+        if (localLOGV) Log.i(TAG, "Copied " + codePath + " to " + resFile);
+        // Finalize container now
+        if (!PackageHelper.finalizeSdDir(newCid)) {
+            Log.e(TAG, "Failed to finalize " + newCid + " at cache path " + newCachePath);
+            // Clean up created container
+            PackageHelper.destroySdDir(newCid);
+            return null;
+        }
+        if (localLOGV) Log.i(TAG, "Finalized container " + newCid);
+        // Force a gc to avoid being killed.
+        Runtime.getRuntime().gc();
+        // Unmount container
+        if (PackageHelper.isContainerMounted(newCid)) {
+            if (localLOGV) Log.i(TAG, "Unmounting " + newCid +
+                    " at path " + newCachePath);
+            PackageHelper.unMountSdDir(newCid);
+        } else {
+            if (localLOGV) Log.i(TAG, "Container " + newCid + " not mounted");
+        }
         return newCachePath;
     }
 
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 67b6200..a1c45dc 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -1457,15 +1457,36 @@
     }
 
     // javadoc from interface
-    public boolean tether(String iface) {
+    public int tether(String iface) {
         enforceTetherChangePermission();
-        return isTetheringSupported() && mTethering.tether(iface);
+
+        if (isTetheringSupported()) {
+            return mTethering.tether(iface);
+        } else {
+            return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
+        }
     }
 
     // javadoc from interface
-    public boolean untether(String iface) {
+    public int untether(String iface) {
         enforceTetherChangePermission();
-        return isTetheringSupported() && mTethering.untether(iface);
+
+        if (isTetheringSupported()) {
+            return mTethering.untether(iface);
+        } else {
+            return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
+        }
+    }
+
+    // javadoc from interface
+    public int getLastTetherError(String iface) {
+        enforceTetherAccessPermission();
+
+        if (isTetheringSupported()) {
+            return mTethering.getLastTetherError(iface);
+        } else {
+            return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
+        }
     }
 
     // TODO - proper iface API for selection by property, inspection, etc
@@ -1499,6 +1520,11 @@
         return mTethering.getTetheredIfaces();
     }
 
+    public String[] getTetheringErroredIfaces() {
+        enforceTetherAccessPermission();
+        return mTethering.getErroredIfaces();
+    }
+
     // if ro.tether.denied = true we default to no tethering
     // gservices could set the secure setting to 1 though to enable it on a build where it
     // had previously been turned off.
diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java
index d5e94ec..ed7eed0 100644
--- a/services/java/com/android/server/InputDevice.java
+++ b/services/java/com/android/server/InputDevice.java
@@ -34,6 +34,16 @@
     /** Maximum number of pointers we will track and report. */
     static final int MAX_POINTERS = 10;
     
+    /**
+     * Slop distance for jumpy pointer detection.
+     * This is in touchscreen coordinates, not pixels or dips.
+     */
+    private static final int JUMPY_EPSILON = 30;
+    
+    /** Number of jumpy points to drop for touchscreens that need it. */
+    private static final int JUMPY_TRANSITION_DROPS = 3;
+    private static final int JUMPY_DROP_LIMIT = 3;
+    
     final int id;
     final int classes;
     final String name;
@@ -84,6 +94,9 @@
         // Used to determine whether we dropped bad data, to avoid doing
         // it repeatedly.
         final boolean[] mDroppedBadPoint = new boolean[MAX_POINTERS];
+
+        // Used to count the number of jumpy points dropped.
+        private int mJumpyPointsDropped = 0;
         
         // Used to perform averaging of reported coordinates, to smooth
         // the data and filter out transients during a release.
@@ -232,6 +245,158 @@
             }
         }
         
+        void dropJumpyPoint(InputDevice dev) {
+            final int nextNumPointers = mNextNumPointers;
+            final int lastNumPointers = mLastNumPointers;
+            final int[] nextData = mNextData;
+            final int[] lastData = mLastData;
+
+            if (nextNumPointers != mLastNumPointers) {
+                if (DEBUG_HACKS) {
+                    Slog.d("InputDevice", "Different pointer count " + lastNumPointers + 
+                            " -> " + nextNumPointers);
+                    for (int i = 0; i < nextNumPointers; i++) {
+                        int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
+                        Slog.d("InputDevice", "Pointer " + i + " (" + 
+                                mNextData[ioff + MotionEvent.SAMPLE_X] + ", " +
+                                mNextData[ioff + MotionEvent.SAMPLE_Y] + ")");
+                    }
+                }
+                
+                // Just drop the first few events going from 1 to 2 pointers.
+                // They're bad often enough that they're not worth considering.
+                if (lastNumPointers == 1 && nextNumPointers == 2
+                        && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
+                    mNextNumPointers = 1;
+                    mJumpyPointsDropped++;
+                } else if (lastNumPointers == 2 && nextNumPointers == 1
+                        && mJumpyPointsDropped < JUMPY_TRANSITION_DROPS) {
+                    // The event when we go from 2 -> 1 tends to be messed up too
+                    System.arraycopy(lastData, 0, nextData, 0, 
+                            lastNumPointers * MotionEvent.NUM_SAMPLE_DATA);
+                    mNextNumPointers = lastNumPointers;
+                    mJumpyPointsDropped++;
+                    
+                    if (DEBUG_HACKS) {
+                        for (int i = 0; i < mNextNumPointers; i++) {
+                            int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
+                            Slog.d("InputDevice", "Pointer " + i + " replaced (" + 
+                                    mNextData[ioff + MotionEvent.SAMPLE_X] + ", " +
+                                    mNextData[ioff + MotionEvent.SAMPLE_Y] + ")");
+                        }
+                    }
+                } else {
+                    mJumpyPointsDropped = 0;
+                    
+                    if (DEBUG_HACKS) {
+                        Slog.d("InputDevice", "Transition - drop limit reset");
+                    }
+                }
+                return;
+            }
+            
+            // A 'jumpy' point is one where the coordinate value for one axis
+            // has jumped to the other pointer's location. No need to do anything
+            // else if we only have one pointer.
+            if (nextNumPointers < 2) {
+                return;
+            }
+            
+            int badPointerIndex = -1;
+            int badPointerReplaceXWith = 0;
+            int badPointerReplaceYWith = 0;
+            int badPointerDistance = Integer.MIN_VALUE;
+            for (int i = nextNumPointers - 1; i >= 0; i--) {
+                boolean dropx = false;
+                boolean dropy = false;
+                
+                // Limit how many times a jumpy point can get dropped.
+                if (mJumpyPointsDropped < JUMPY_DROP_LIMIT) {
+                    final int ioff = i * MotionEvent.NUM_SAMPLE_DATA;
+                    final int x = nextData[ioff + MotionEvent.SAMPLE_X];
+                    final int y = nextData[ioff + MotionEvent.SAMPLE_Y];
+                    
+                    if (DEBUG_HACKS) {
+                        Slog.d("InputDevice", "Point " + i + " (" + x + ", " + y + ")");
+                    }
+
+                    // Check if a touch point is too close to another's coordinates
+                    for (int j = 0; j < nextNumPointers && !dropx && !dropy; j++) {
+                        if (j == i) {
+                            continue;
+                        }
+
+                        final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
+                        final int xOther = nextData[joff + MotionEvent.SAMPLE_X];
+                        final int yOther = nextData[joff + MotionEvent.SAMPLE_Y];
+
+                        dropx = Math.abs(x - xOther) <= JUMPY_EPSILON;
+                        dropy = Math.abs(y - yOther) <= JUMPY_EPSILON;
+                    }
+                    
+                    if (dropx) {
+                        int xreplace = lastData[MotionEvent.SAMPLE_X];
+                        int yreplace = lastData[MotionEvent.SAMPLE_Y];
+                        int distance = Math.abs(yreplace - y);
+                        for (int j = 1; j < lastNumPointers; j++) {
+                            final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
+                            int lasty = lastData[joff + MotionEvent.SAMPLE_Y];   
+                            int currDist = Math.abs(lasty - y);
+                            if (currDist < distance) {
+                                xreplace = lastData[joff + MotionEvent.SAMPLE_X];
+                                yreplace = lasty;
+                                distance = currDist;
+                            }
+                        }
+                        
+                        int badXDelta = Math.abs(xreplace - x);
+                        if (badXDelta > badPointerDistance) {
+                            badPointerDistance = badXDelta;
+                            badPointerIndex = i;
+                            badPointerReplaceXWith = xreplace;
+                            badPointerReplaceYWith = yreplace;
+                        }
+                    } else if (dropy) {
+                        int xreplace = lastData[MotionEvent.SAMPLE_X];
+                        int yreplace = lastData[MotionEvent.SAMPLE_Y];
+                        int distance = Math.abs(xreplace - x);
+                        for (int j = 1; j < lastNumPointers; j++) {
+                            final int joff = j * MotionEvent.NUM_SAMPLE_DATA;
+                            int lastx = lastData[joff + MotionEvent.SAMPLE_X];   
+                            int currDist = Math.abs(lastx - x);
+                            if (currDist < distance) {
+                                xreplace = lastx;
+                                yreplace = lastData[joff + MotionEvent.SAMPLE_Y];
+                                distance = currDist;
+                            }
+                        }
+                        
+                        int badYDelta = Math.abs(yreplace - y);
+                        if (badYDelta > badPointerDistance) {
+                            badPointerDistance = badYDelta;
+                            badPointerIndex = i;
+                            badPointerReplaceXWith = xreplace;
+                            badPointerReplaceYWith = yreplace;
+                        }
+                    }
+                }
+            }
+            if (badPointerIndex >= 0) {
+                if (DEBUG_HACKS) {
+                    Slog.d("InputDevice", "Replacing bad pointer " + badPointerIndex +
+                            " with (" + badPointerReplaceXWith + ", " + badPointerReplaceYWith +
+                            ")");
+                }
+
+                final int offset = badPointerIndex * MotionEvent.NUM_SAMPLE_DATA;
+                nextData[offset + MotionEvent.SAMPLE_X] = badPointerReplaceXWith;
+                nextData[offset + MotionEvent.SAMPLE_Y] = badPointerReplaceYWith;
+                mJumpyPointsDropped++;
+            } else {
+                mJumpyPointsDropped = 0;
+            }
+        }
+        
         /**
          * Special hack for devices that have bad screen data: aggregate and
          * compute averages of the coordinate data, to reduce the amount of
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
index a08258a..8cd9578 100644
--- a/services/java/com/android/server/KeyInputQueue.java
+++ b/services/java/com/android/server/KeyInputQueue.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.os.Environment;
 import android.os.LatencyTimer;
 import android.os.PowerManager;
@@ -60,6 +61,12 @@
      */
     static boolean BAD_TOUCH_HACK = false;
     
+    /**
+     * Turn on some hacks to improve touch interaction with another device
+     * where touch coordinate data can get corrupted.
+     */
+    static boolean JUMPY_TOUCH_HACK = false;
+    
     private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
 
     final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
@@ -284,8 +291,10 @@
             lt = new LatencyTimer(100, 1000);
         }
 
-        BAD_TOUCH_HACK = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_filterTouchEvents);
+        Resources r = context.getResources();
+        BAD_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterTouchEvents);
+        
+        JUMPY_TOUCH_HACK = r.getBoolean(com.android.internal.R.bool.config_filterJumpyTouchEvents);
         
         mHapticFeedbackCallback = hapticFeedbackCallback;
         
@@ -758,6 +767,9 @@
                                     if (BAD_TOUCH_HACK) {
                                         ms.dropBadPoint(di);
                                     }
+                                    if (JUMPY_TOUCH_HACK) {
+                                        ms.dropJumpyPoint(di);
+                                    }
                                     
                                     boolean doMotion = !monitorVirtualKey(di,
                                             ev, curTime, curTimeNano);
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 41f3850..0974f7f 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -523,6 +523,11 @@
 
         Intent in = null;
 
+        if (oldState == VolumeState.Shared && newState != oldState) {
+            mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_UNSHARED,
+                                                Uri.parse("file://" + path)));
+        }
+
         if (newState == VolumeState.Init) {
         } else if (newState == VolumeState.NoMedia) {
             // NoMedia is handled via Disk Remove events
@@ -1171,13 +1176,6 @@
         } catch (NativeDaemonConnectorException e) {
             rc = StorageResultCode.OperationFailedInternalError;
         }
-        if (rc == StorageResultCode.OperationSucceeded) {
-            synchronized (mAsecMountSet) {
-                if (!mAsecMountSet.contains(newId)) {
-                    mAsecMountSet.add(newId);
-                }
-            }
-        }
 
         return rc;
     }
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 6d121c3..53076de 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -447,7 +447,7 @@
             String []tok = line.split(" ");
             int code = Integer.parseInt(tok[0]);
             if (code == NetdResponseCode.UsbRNDISStatusResult) {
-                if (tok[2].equals("started"))
+                if (tok[3].equals("started"))
                     return true;
                 return false;
             } else {
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index bf2b1c7..664f028 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -318,6 +318,11 @@
     // Set of pending broadcasts for aggregating enable/disable of components.
     final HashMap<String, ArrayList<String>> mPendingBroadcasts
             = new HashMap<String, ArrayList<String>>();
+    // Service Connection to remote media container service to copy
+    // package uri's from external media onto secure containers
+    // or internal storage.
+    private IMediaContainerService mContainerService = null;
+
     static final int SEND_PENDING_BROADCAST = 1;
     static final int MCS_BOUND = 3;
     static final int END_COPY = 4;
@@ -326,17 +331,23 @@
     static final int START_CLEANING_PACKAGE = 7;
     static final int FIND_INSTALL_LOC = 8;
     static final int POST_INSTALL = 9;
+    static final int MCS_RECONNECT = 10;
+    static final int MCS_GIVE_UP = 11;
+
     // Delay time in millisecs
     static final int BROADCAST_DELAY = 10 * 1000;
-    private ServiceConnection mDefContainerConn = new ServiceConnection() {
+    final private DefaultContainerConnection mDefContainerConn =
+            new DefaultContainerConnection();
+    class DefaultContainerConnection implements ServiceConnection {
         public void onServiceConnected(ComponentName name, IBinder service) {
+            if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
             IMediaContainerService imcs =
                 IMediaContainerService.Stub.asInterface(service);
-            Message msg = mHandler.obtainMessage(MCS_BOUND, imcs);
-            mHandler.sendMessage(msg);
+            mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
         }
 
         public void onServiceDisconnected(ComponentName name) {
+            if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
         }
     };
 
@@ -355,12 +366,27 @@
     int mNextInstallToken = 1;  // nonzero; will be wrapped back to 1 when ++ overflows
 
     class PackageHandler extends Handler {
+        private boolean mBound = false;
         final ArrayList<HandlerParams> mPendingInstalls =
             new ArrayList<HandlerParams>();
-        // Service Connection to remote media container service to copy
-        // package uri's from external media onto secure containers
-        // or internal storage.
-        private IMediaContainerService mContainerService = null;
+
+        private boolean connectToService() {
+            if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" +
+                    " DefaultContainerService");
+            Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
+            if (mContext.bindService(service, mDefContainerConn,
+                    Context.BIND_AUTO_CREATE)) {
+                mBound = true;
+                return true;
+            }
+            return false;
+        }
+
+        private void disconnectService() {
+            mContainerService = null;
+            mBound = false;
+            mContext.unbindService(mDefContainerConn);
+        }
 
         PackageHandler(Looper looper) {
             super(looper);
@@ -368,41 +394,99 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case INIT_COPY: {
+                    if (DEBUG_SD_INSTALL) Log.i(TAG, "init_copy");
                     HandlerParams params = (HandlerParams) msg.obj;
-                    Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
-                    if (mContainerService != null) {
-                        // No need to add to pending list. Use remote stub directly
-                        params.handleStartCopy(mContainerService);
-                    } else {
-                        if (mContext.bindService(service, mDefContainerConn,
-                                Context.BIND_AUTO_CREATE)) {
-                            mPendingInstalls.add(params);
-                        } else {
+                    int idx = mPendingInstalls.size();
+                    if (DEBUG_SD_INSTALL) Log.i(TAG, "idx=" + idx);
+                    // If a bind was already initiated we dont really
+                    // need to do anything. The pending install
+                    // will be processed later on.
+                    if (!mBound) {
+                        // If this is the only one pending we might
+                        // have to bind to the service again.
+                        if (!connectToService()) {
                             Log.e(TAG, "Failed to bind to media container service");
-                            // Indicate service bind error
-                            params.handleServiceError();
+                            params.serviceError();
+                            return;
+                        } else {
+                            // Once we bind to the service, the first
+                            // pending request will be processed.
+                            mPendingInstalls.add(idx, params);
+                        }
+                    } else {
+                        mPendingInstalls.add(idx, params);
+                        // Already bound to the service. Just make
+                        // sure we trigger off processing the first request.
+                        if (idx == 0) {
+                            mHandler.sendEmptyMessage(MCS_BOUND);
                         }
                     }
                     break;
                 }
                 case MCS_BOUND: {
-                    // Initialize mContainerService if needed.
+                    if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_bound");
                     if (msg.obj != null) {
                         mContainerService = (IMediaContainerService) msg.obj;
                     }
-                    if (mPendingInstalls.size() > 0) {
-                        HandlerParams params = mPendingInstalls.remove(0);
+                    if (mContainerService == null) {
+                        // Something seriously wrong. Bail out
+                        Log.e(TAG, "Cannot bind to media container service");
+                        for (HandlerParams params : mPendingInstalls) {
+                            mPendingInstalls.remove(0);
+                            // Indicate service bind error
+                            params.serviceError();
+                        }
+                        mPendingInstalls.clear();
+                    } else if (mPendingInstalls.size() > 0) {
+                        HandlerParams params = mPendingInstalls.get(0);
                         if (params != null) {
-                            params.handleStartCopy(mContainerService);
+                            params.startCopy();
+                        }
+                    } else {
+                        // Should never happen ideally.
+                        Log.w(TAG, "Empty queue");
+                    }
+                    break;
+                }
+                case MCS_RECONNECT : {
+                    if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_reconnect");
+                    if (mPendingInstalls.size() > 0) {
+                        if (mBound) {
+                            disconnectService();
+                        }
+                        if (!connectToService()) {
+                            Log.e(TAG, "Failed to bind to media container service");
+                            for (HandlerParams params : mPendingInstalls) {
+                                mPendingInstalls.remove(0);
+                                // Indicate service bind error
+                                params.serviceError();
+                            }
+                            mPendingInstalls.clear();
                         }
                     }
                     break;
                 }
                 case MCS_UNBIND : {
-                    if (mPendingInstalls.size() == 0) {
-                        mContext.unbindService(mDefContainerConn);
-                        mContainerService = null;
+                    if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_unbind");
+                    // Delete pending install
+                    if (mPendingInstalls.size() > 0) {
+                        mPendingInstalls.remove(0);
                     }
+                    if (mPendingInstalls.size() == 0) {
+                        if (mBound) {
+                            disconnectService();
+                        }
+                    } else {
+                        // There are more pending requests in queue.
+                        // Just post MCS_BOUND message to trigger processing
+                        // of next pending install.
+                        mHandler.sendEmptyMessage(MCS_BOUND);
+                    }
+                    break;
+                }
+                case MCS_GIVE_UP: {
+                    if (DEBUG_SD_INSTALL) Log.i(TAG, "mcs_giveup too many retries");
+                    HandlerParams params = mPendingInstalls.remove(0);
                     break;
                 }
                 case SEND_PENDING_BROADCAST : {
@@ -2288,10 +2372,10 @@
         synchronized (mPackages) {
             // Look to see if we already know about this package.
             String oldName = mSettings.mRenamedPackages.get(pkg.packageName);
-            if (oldName != null && oldName.equals(pkg.mOriginalPackage)) {
+            if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
                 // This package has been renamed to its original name.  Let's
                 // use that.
-                ps = mSettings.peekPackageLP(pkg.mOriginalPackage);
+                ps = mSettings.peekPackageLP(oldName);
             }
             // If there was no original package, see one for the real package name.
             if (ps == null) {
@@ -2561,7 +2645,7 @@
 
         if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
             // Only system apps can use these features.
-            pkg.mOriginalPackage = null;
+            pkg.mOriginalPackages = null;
             pkg.mRealPackage = null;
             pkg.mAdoptPermissions = null;
         }
@@ -2643,22 +2727,22 @@
             }
 
             if (false) {
-                if (pkg.mOriginalPackage != null) {
+                if (pkg.mOriginalPackages != null) {
                     Log.w(TAG, "WAITING FOR DEBUGGER");
                     Debug.waitForDebugger();
-                    Log.i(TAG, "Package " + pkg.packageName + " from original package"
-                            + pkg.mOriginalPackage);
+                    Log.i(TAG, "Package " + pkg.packageName + " from original packages"
+                            + pkg.mOriginalPackages);
                 }
             }
             
             // Check if we are renaming from an original package name.
             PackageSetting origPackage = null;
             String realName = null;
-            if (pkg.mOriginalPackage != null) {
+            if (pkg.mOriginalPackages != null) {
                 // This package may need to be renamed to a previously
                 // installed name.  Let's check on that...
                 String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage);
-                if (pkg.mOriginalPackage.equals(renamed)) {
+                if (pkg.mOriginalPackages.contains(renamed)) {
                     // This package had originally been installed as the
                     // original name, and we have already taken care of
                     // transitioning to the new one.  Just update the new
@@ -2671,25 +2755,32 @@
                         pkg.setPackageName(renamed);
                     }
                     
-                } else if ((origPackage
-                        = mSettings.peekPackageLP(pkg.mOriginalPackage)) != null) {
-                    // We do have the package already installed under its
-                    // original name...  should we use it?
-                    if (!verifyPackageUpdate(origPackage, pkg)) {
-                        // New package is not compatible with original.
-                        origPackage = null;
-                    } else if (origPackage.sharedUser != null) {
-                        // Make sure uid is compatible between packages.
-                        if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) {
-                            Log.w(TAG, "Unable to migrate data from " + origPackage.name
-                                    + " to " + pkg.packageName + ": old uid "
-                                    + origPackage.sharedUser.name
-                                    + " differs from " + pkg.mSharedUserId);
-                            origPackage = null;
+                } else {
+                    for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) {
+                        if ((origPackage=mSettings.peekPackageLP(
+                                pkg.mOriginalPackages.get(i))) != null) {
+                            // We do have the package already installed under its
+                            // original name...  should we use it?
+                            if (!verifyPackageUpdate(origPackage, pkg)) {
+                                // New package is not compatible with original.
+                                origPackage = null;
+                                continue;
+                            } else if (origPackage.sharedUser != null) {
+                                // Make sure uid is compatible between packages.
+                                if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) {
+                                    Log.w(TAG, "Unable to migrate data from " + origPackage.name
+                                            + " to " + pkg.packageName + ": old uid "
+                                            + origPackage.sharedUser.name
+                                            + " differs from " + pkg.mSharedUserId);
+                                    origPackage = null;
+                                    continue;
+                                }
+                            } else {
+                                if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
+                                        + pkg.packageName + " to old name " + origPackage.name);
+                            }
+                            break;
                         }
-                    } else {
-                        if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
-                                + pkg.packageName + " to old name " + origPackage.name);
                     }
                 }
             }
@@ -4407,12 +4498,41 @@
         });
     }
 
-    interface HandlerParams {
-        void handleStartCopy(IMediaContainerService imcs);
-        void handleServiceError();
+    abstract class HandlerParams {
+        final static int MAX_RETRIES = 4;
+        int retry = 0;
+        final void startCopy() {
+            try {
+                if (DEBUG_SD_INSTALL) Log.i(TAG, "startCopy");
+                retry++;
+                if (retry > MAX_RETRIES) {
+                    Log.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
+                    mHandler.sendEmptyMessage(MCS_GIVE_UP);
+                    handleServiceError();
+                    return;
+                } else {
+                    handleStartCopy();
+                    if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting install MCS_UNBIND");
+                    mHandler.sendEmptyMessage(MCS_UNBIND);
+                }
+            } catch (RemoteException e) {
+                if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting install MCS_RECONNECT");
+                mHandler.sendEmptyMessage(MCS_RECONNECT);
+            }
+            handleReturnCode();
+        }
+
+        final void serviceError() {
+            if (DEBUG_SD_INSTALL) Log.i(TAG, "serviceError");
+            handleServiceError();
+            handleReturnCode();
+        }
+        abstract void handleStartCopy() throws RemoteException;
+        abstract void handleServiceError();
+        abstract void handleReturnCode();
     }
 
-    class InstallParams implements HandlerParams {
+    class InstallParams extends HandlerParams {
         final IPackageInstallObserver observer;
         int flags;
         final Uri packageURI;
@@ -4428,22 +4548,14 @@
             this.installerPackageName = installerPackageName;
         }
 
-        private int getInstallLocation(IMediaContainerService imcs) {
-            try {
-                return imcs.getRecommendedInstallLocation(packageURI);
-            } catch (RemoteException e) {
-            }
-            return  -1;
-        }
-
-        public void handleStartCopy(IMediaContainerService imcs) {
+        public void handleStartCopy() throws RemoteException {
             int ret = PackageManager.INSTALL_SUCCEEDED;
             // Dont need to invoke getInstallLocation for forward locked apps.
             if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
                 flags &= ~PackageManager.INSTALL_EXTERNAL;
-            } else if (imcs != null) {
+            } else {
                 // Remote call to find out default install location
-                int loc = getInstallLocation(imcs);
+                int loc = mContainerService.getRecommendedInstallLocation(packageURI);
                 // Use install location to create InstallArgs and temporary
                 // install location
                 if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE){
@@ -4468,23 +4580,22 @@
             if (ret == PackageManager.INSTALL_SUCCEEDED) {
                 // Create copy only if we are not in an erroneous state.
                 // Remote call to initiate copy using temporary file
-                ret = mArgs.copyApk(imcs, true);
+                ret = mArgs.copyApk(mContainerService, true);
             }
             mRet = ret;
-            mHandler.sendEmptyMessage(MCS_UNBIND);
-            handleReturnCode();
         }
 
+        @Override
         void handleReturnCode() {
             processPendingInstall(mArgs, mRet);
         }
 
-        public void handleServiceError() {
+        @Override
+        void handleServiceError() {
             mArgs = createInstallArgs(this);
             mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
-            handleReturnCode();
         }
-    };
+    }
 
     /*
      * Utility class used in movePackage api.
@@ -4493,7 +4604,7 @@
      * We probably want to return ErrorPrams for both failed installs
      * and moves.
      */
-    class MoveParams implements HandlerParams {
+    class MoveParams extends HandlerParams {
         final IPackageMoveObserver observer;
         final int flags;
         final String packageName;
@@ -4515,25 +4626,36 @@
             }
         }
 
-        public void handleStartCopy(IMediaContainerService imcs) {
+        public void handleStartCopy() throws RemoteException {
             // Create the file args now.
-            mRet = targetArgs.copyApk(imcs, false);
+            mRet = targetArgs.copyApk(mContainerService, false);
             targetArgs.doPreInstall(mRet);
-            mHandler.sendEmptyMessage(MCS_UNBIND);
-            handleReturnCode();
+            if (DEBUG_SD_INSTALL) {
+                StringBuilder builder = new StringBuilder();
+                if (srcArgs != null) {
+                    builder.append("src: ");
+                    builder.append(srcArgs.getCodePath());
+                }
+                if (targetArgs != null) {
+                    builder.append(" target : ");
+                    builder.append(targetArgs.getCodePath());
+                }
+                Log.i(TAG, "Posting move MCS_UNBIND for " + builder.toString());
+            }
         }
 
+        @Override
         void handleReturnCode() {
             targetArgs.doPostInstall(mRet);
             // TODO invoke pending move
             processPendingMove(this, mRet);
         }
 
-        public void handleServiceError() {
+        @Override
+        void handleServiceError() {
             mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
-            handleReturnCode();
         }
-    };
+    }
 
     private InstallArgs createInstallArgs(InstallParams params) {
         if (installOnSd(params.flags)) {
@@ -4577,7 +4699,7 @@
         }
 
         abstract void createCopyFile();
-        abstract int copyApk(IMediaContainerService imcs, boolean temp);
+        abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
         abstract int doPreInstall(int status);
         abstract boolean doRename(int status, String pkgName, String oldCodePath);
         abstract int doPostInstall(int status);
@@ -4628,7 +4750,7 @@
             created = true;
         }
 
-        int copyApk(IMediaContainerService imcs, boolean temp) {
+        int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
             if (temp) {
                 // Generate temp file name
                 createCopyFile();
@@ -4662,7 +4784,6 @@
                 if (imcs.copyResource(packageURI, out)) {
                     ret = PackageManager.INSTALL_SUCCEEDED;
                 }
-            } catch (RemoteException e) {
             } finally {
                 try { if (out != null) out.close(); } catch (IOException e) {}
             }
@@ -4818,16 +4939,13 @@
             cid = getTempContainerId();
         }
 
-        int copyApk(IMediaContainerService imcs, boolean temp) {
+        int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
             if (temp) {
                 createCopyFile();
             }
-            try {
-                cachePath = imcs.copyResourceToContainer(
-                        packageURI, cid,
-                        getEncryptKey(), RES_FILE_NAME);
-            } catch (RemoteException e) {
-            }
+            cachePath = imcs.copyResourceToContainer(
+                    packageURI, cid,
+                    getEncryptKey(), RES_FILE_NAME);
             return (cachePath == null) ? PackageManager.INSTALL_FAILED_CONTAINER_ERROR :
                 PackageManager.INSTALL_SUCCEEDED;
         }
@@ -4862,67 +4980,34 @@
                 String oldCodePath) {
             String newCacheId = getNextCodePath(oldCodePath, pkgName, "/" + RES_FILE_NAME);
             String newCachePath = null;
-            boolean enableRename = false;
-            if (enableRename) {
-                if (PackageHelper.isContainerMounted(cid)) {
-                    // Unmount the container
-                    if (!PackageHelper.unMountSdDir(cid)) {
-                        Log.i(TAG, "Failed to unmount " + cid + " before renaming");
-                        return false;
-                    }
-                }
-                if (!PackageHelper.renameSdDir(cid, newCacheId)) {
-                    Log.e(TAG, "Failed to rename " + cid + " to " + newCacheId);
+            if (PackageHelper.isContainerMounted(cid)) {
+                // Unmount the container
+                if (!PackageHelper.unMountSdDir(cid)) {
+                    Log.i(TAG, "Failed to unmount " + cid + " before renaming");
                     return false;
                 }
-                if (!PackageHelper.isContainerMounted(newCacheId)) {
-                    Log.w(TAG, "Mounting container " + newCacheId);
-                    newCachePath = PackageHelper.mountSdDir(newCacheId,
-                            getEncryptKey(), Process.SYSTEM_UID);
-                } else {
-                    newCachePath = PackageHelper.getSdDir(newCacheId);
-                }
-                if (newCachePath == null) {
-                    Log.w(TAG, "Failed to get cache path for  " + newCacheId);
-                    return false;
-                }
-                // Mount old container?
-                Log.i(TAG, "Succesfully renamed " + cid +
-                        " at path: " + cachePath + " to " + newCacheId +
-                        " at new path: " + newCachePath);
-                cid = newCacheId;
-                cachePath = newCachePath;
-                return true;
-            } else {
-                // STOPSHIP work around for rename
-                Log.i(TAG, "Copying instead of renaming");
-                File srcFile = new File(getCodePath());
-                // Create new container
-                newCachePath = PackageHelper.createSdDir(srcFile, newCacheId,
-                        getEncryptKey(), Process.SYSTEM_UID);
-                Log.i(TAG, "Created rename container " + newCacheId);
-                File destFile = new File(newCachePath + "/" + RES_FILE_NAME);
-                if (!FileUtils.copyFile(srcFile, destFile)) {
-                    Log.e(TAG, "Failed to copy " + srcFile + " to " + destFile);
-                    return false;
-                }
-                Log.i(TAG, "Successfully copied resource to " + newCachePath);
-                if (!PackageHelper.finalizeSdDir(newCacheId)) {
-                    Log.e(TAG, "Failed to finalize " + newCacheId);
-                    PackageHelper.destroySdDir(newCacheId);
-                    return false;
-                }
-                Log.i(TAG, "Finalized " + newCacheId);
-                Runtime.getRuntime().gc();
-                // Unmount first
-                PackageHelper.unMountSdDir(cid);
-                // Delete old container
-                PackageHelper.destroySdDir(cid);
-                // Dont have to mount. Already mounted.
-                cid = newCacheId;
-                cachePath = newCachePath;
-                return true;
             }
+            if (!PackageHelper.renameSdDir(cid, newCacheId)) {
+                Log.e(TAG, "Failed to rename " + cid + " to " + newCacheId);
+                return false;
+            }
+            if (!PackageHelper.isContainerMounted(newCacheId)) {
+                Log.w(TAG, "Mounting container " + newCacheId);
+                newCachePath = PackageHelper.mountSdDir(newCacheId,
+                        getEncryptKey(), Process.SYSTEM_UID);
+            } else {
+                newCachePath = PackageHelper.getSdDir(newCacheId);
+            }
+            if (newCachePath == null) {
+                Log.w(TAG, "Failed to get cache path for  " + newCacheId);
+                return false;
+            }
+            Log.i(TAG, "Succesfully renamed " + cid +
+                    " at path: " + cachePath + " to " + newCacheId +
+                    " at new path: " + newCachePath);
+            cid = newCacheId;
+            cachePath = newCachePath;
+            return true;
         }
 
         int doPostInstall(int status) {
@@ -5401,13 +5486,14 @@
             // Check if installing already existing package
             if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                 String oldName = mSettings.mRenamedPackages.get(pkgName);
-                if (oldName != null && oldName.equals(pkg.mOriginalPackage)
+                if (pkg.mOriginalPackages != null
+                        && pkg.mOriginalPackages.contains(oldName)
                         && mPackages.containsKey(oldName)) {
                     // This package is derived from an original package,
                     // and this device has been updating from that original
                     // name.  We must continue using the original name, so
                     // rename the new package here.
-                    pkg.setPackageName(pkg.mOriginalPackage);
+                    pkg.setPackageName(oldName);
                     pkgName = pkg.packageName;
                     replace = true;
                 } else if (mPackages.containsKey(pkgName)) {
@@ -8885,6 +8971,7 @@
     * Return true if PackageManager does have packages to be updated.
     */
    public boolean updateExternalMediaStatus(final boolean mediaStatus) {
+       final boolean ret;
        synchronized (mPackages) {
            if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
                    mediaStatus+", mMediaMounted=" + mMediaMounted);
@@ -8892,72 +8979,77 @@
                return false;
            }
            mMediaMounted = mediaStatus;
-           boolean ret = false;
-           synchronized (mPackages) {
-               Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_ON_SDCARD);
-               ret = appList != null && appList.size() > 0;
-           }
-           if (!ret) {
-               // No packages will be effected by the sdcard update. Just return.
-               return false;
-           }
-            // Queue up an async operation since the package installation may take a little while.
-           mHandler.post(new Runnable() {
-               public void run() {
-                   mHandler.removeCallbacks(this);
-                   // If we are up here that means there are packages to be
-                   // enabled or disabled.
-                   final HashMap<SdInstallArgs, String> processCids =
-                       new HashMap<SdInstallArgs, String>();
-                   final int[] uidArr = getExternalMediaPackages(mediaStatus, processCids);
-                   if (mediaStatus) {
-                       if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
-                       loadMediaPackages(processCids, uidArr);
-                       startCleaningPackages();
-                   } else {
-                       if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
-                       unloadMediaPackages(processCids, uidArr);
-                   }
-               }
-           });
-           return true;
+           Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_ON_SDCARD);
+           ret = appList != null && appList.size() > 0;
        }
+       // Queue up an async operation since the package installation may take a little while.
+       mHandler.post(new Runnable() {
+           public void run() {
+               mHandler.removeCallbacks(this);
+               updateExternalMediaStatusInner(mediaStatus, ret);
+           }
+       });
+       return ret;
    }
 
-    private int[] getExternalMediaPackages(boolean mediaStatus,
-            Map<SdInstallArgs, String> processCids) {
+   private void updateExternalMediaStatusInner(boolean mediaStatus,
+           boolean sendUpdateBroadcast) {
+       // If we are up here that means there are packages to be
+       // enabled or disabled.
        final String list[] = PackageHelper.getSecureContainerList();
        if (list == null || list.length == 0) {
-           return null;
+           return;
        }
 
        int uidList[] = new int[list.length];
        int num = 0;
+       HashSet<String> removeCids = new HashSet<String>();
+       HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>();
+       /*HashMap<String, String> cidPathMap = new HashMap<String, String>();
+       // Don't hold any locks when getting cache paths
+       for (String cid : list) {
+           String cpath = PackageHelper.getSdDir(cid);
+           if (cpath == null) {
+               removeCids.add(cid);
+           } else {
+               cidPathMap.put(cid, cpath);
+           }
+       }*/
        synchronized (mPackages) {
-           Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_ON_SDCARD);
            for (String cid : list) {
                SdInstallArgs args = new SdInstallArgs(cid);
-               String removeEntry = null;
-               for (String app : appList) {
-                   if (args.matchContainer(app)) {
-                       removeEntry = app;
-                       break;
+               if (DEBUG_SD_INSTALL) Log.i(TAG, "Processing container " + cid);
+               boolean failed = true;
+               try {
+                   String pkgName = args.getPackageName();
+                   if (pkgName == null) {
+                       continue;
                    }
-               }
-               if (removeEntry == null) {
-                   // No matching app on device. Skip entry or may be cleanup?
-                   // Ignore default package
-                   continue;
-               }
-               appList.remove(removeEntry);
-               PackageSetting ps = mSettings.mPackages.get(removeEntry);
-               processCids.put(args, ps.codePathString);
-               int uid = ps.userId;
-               if (uid != -1) {
-                   uidList[num++] = uid;
+                   if (DEBUG_SD_INSTALL) Log.i(TAG, "Looking for pkg : " + pkgName);
+                   PackageSetting ps = mSettings.mPackages.get(pkgName);
+                   if (ps != null && ps.codePathString != null &&
+                           (ps.pkgFlags & ApplicationInfo.FLAG_ON_SDCARD) != 0) {
+                       if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid +
+                               " corresponds to pkg : " + pkgName +
+                               " at code path: " + ps.codePathString);
+                       // We do have a valid package installed on sdcard
+                       processCids.put(args, ps.codePathString);
+                       failed = false;
+                       int uid = ps.userId;
+                       if (uid != -1) {
+                           uidList[num++] = uid;
+                       }
+                   }
+               } finally {
+                   if (failed) {
+                       // Stale container on sdcard. Just delete
+                       if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + " stale");
+                       removeCids.add(cid);
+                   }
                }
            }
        }
+       // Organize uids
        int uidArr[] = null;
        if (num > 0) {
            // Sort uid list
@@ -8972,7 +9064,15 @@
                }
            }
        }
-       return uidArr;
+       // Process packages with valid entries.
+       if (mediaStatus) {
+           if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
+           loadMediaPackages(processCids, uidArr, sendUpdateBroadcast, removeCids);
+           startCleaningPackages();
+       } else {
+           if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
+           unloadMediaPackages(processCids, uidArr, sendUpdateBroadcast);
+       }
    }
 
    private void sendResourcesChangedBroadcast(boolean mediaStatus,
@@ -8992,57 +9092,100 @@
        }
    }
 
-   private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
+   /*
+    * Look at potentially valid container ids from processCids
+    * If package information doesn't match the one on record
+    * or package scanning fails, the cid is added to list of
+    * removeCids and cleaned up. Since cleaning up containers
+    * involves destroying them, we do not want any parse
+    * references to such stale containers. So force gc's
+    * to avoid unnecessary crashes.
+    */
+   private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids,
+           int uidArr[], boolean sendUpdateBroadcast,
+           HashSet<String> removeCids) {
        ArrayList<String> pkgList = new ArrayList<String>();
        Set<SdInstallArgs> keys = processCids.keySet();
+       boolean doGc = false;
        for (SdInstallArgs args : keys) {
            String codePath = processCids.get(args);
-           if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install pkg : "
-                   + args.cid + " from " + args.cachePath);
-           if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) != PackageManager.INSTALL_SUCCEEDED) {
-               Log.e(TAG, "Failed to install package: " + codePath + " from sdcard");
-               continue;
-           }
-           // Parse package
-           int parseFlags = PackageParser.PARSE_CHATTY |
-           PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
-           PackageParser pp = new PackageParser(codePath);
-           pp.setSeparateProcesses(mSeparateProcesses);
-           final PackageParser.Package pkg = pp.parsePackage(new File(codePath),
-                   codePath, mMetrics, parseFlags);
-           if (pkg == null) {
-               Log.e(TAG, "Trying to install pkg : "
-                       + args.cid + " from " + args.cachePath);
-               continue;
-           }
-           setApplicationInfoPaths(pkg, codePath, codePath);
+           if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading container : "
+                   + args.cid);
            int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
-           synchronized (mInstallLock) {
-               // Scan the package
-               if (scanPackageLI(pkg, parseFlags, SCAN_MONITOR) != null) {
-                   synchronized (mPackages) {
-                       // Grant permissions
-                       grantPermissionsLP(pkg, false);
-                       // Persist settings
-                       mSettings.writeLP();
-                       retCode = PackageManager.INSTALL_SUCCEEDED;
-                       pkgList.add(pkg.packageName);
+           try {
+               // Make sure there are no container errors first.
+               if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED)
+                       != PackageManager.INSTALL_SUCCEEDED) {
+                   Log.e(TAG, "Failed to mount cid : " + args.cid +
+                   " when installing from sdcard");
+                   continue;
+               }
+               // Check code path here.
+               if (codePath == null || !codePath.equals(args.getCodePath())) {
+                   Log.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath()+
+                           " does not match one in settings " + codePath);
+                   continue;
+               }
+               // Parse package
+               int parseFlags = PackageParser.PARSE_CHATTY |
+               PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
+               PackageParser pp = new PackageParser(codePath);
+               pp.setSeparateProcesses(mSeparateProcesses);
+               final PackageParser.Package pkg = pp.parsePackage(new File(codePath),
+                       codePath, mMetrics, parseFlags);
+               pp = null;
+               doGc = true;
+               // Check for parse errors
+               if (pkg == null) {
+                   Log.e(TAG, "Parse error when installing install pkg : "
+                           + args.cid + " from " + args.cachePath);
+                   continue;
+               }
+               setApplicationInfoPaths(pkg, codePath, codePath);
+               synchronized (mInstallLock) {
+                   // Scan the package
+                   if (scanPackageLI(pkg, parseFlags, SCAN_MONITOR) != null) {
+                       synchronized (mPackages) {
+                           // Grant permissions
+                           grantPermissionsLP(pkg, false);
+                           // Persist settings
+                           mSettings.writeLP();
+                           retCode = PackageManager.INSTALL_SUCCEEDED;
+                           pkgList.add(pkg.packageName);
+                           // Post process args
+                           args.doPostInstall(PackageManager.INSTALL_SUCCEEDED);
+                       }
+                   } else {
+                       Log.i(TAG, "Failed to install pkg: " +
+                               pkg.packageName + " from sdcard");
                    }
-               } else {
-                   Log.i(TAG, "Failed to install package: " + pkg.packageName + " from sdcard");
+               }
+
+           } finally {
+               if (retCode != PackageManager.INSTALL_SUCCEEDED) {
+                   // Don't destroy container here. Wait till gc clears things up.
+                   removeCids.add(args.cid);
                }
            }
-           args.doPostInstall(retCode);
        }
        // Send a broadcast to let everyone know we are done processing
-       sendResourcesChangedBroadcast(true, pkgList, uidArr);
-       if (pkgList.size() > 0) {
+       if (sendUpdateBroadcast) {
+           sendResourcesChangedBroadcast(true, pkgList, uidArr);
+       }
+       if (doGc) {
            Runtime.getRuntime().gc();
-           // If something failed do we clean up here or next install?
+       }
+       // Delete any stale containers if needed.
+       if (removeCids != null) {
+           for (String cid : removeCids) {
+               Log.i(TAG, "Destroying stale container : " + cid);
+               PackageHelper.destroySdDir(cid);
+           }
        }
    }
 
-   private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
+   private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids,
+           int uidArr[], boolean sendUpdateBroadcast) {
        if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages");
        ArrayList<String> pkgList = new ArrayList<String>();
        ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>();
@@ -9064,13 +9207,14 @@
                }
            }
        }
-       sendResourcesChangedBroadcast(false, pkgList, uidArr);
        // Send broadcasts
-       if (pkgList.size() > 0) {
-           Runtime.getRuntime().gc();
+       if (sendUpdateBroadcast) {
+           sendResourcesChangedBroadcast(false, pkgList, uidArr);
        }
-       // Do clean up. Just unmount
-       for (SdInstallArgs args : failedList) {
+       // Force gc
+       Runtime.getRuntime().gc();
+       // Just unmount all valid containers.
+       for (SdInstallArgs args : keys) {
            synchronized (mInstallLock) {
                args.doPostDeleteLI(false);
            }
@@ -9189,21 +9333,21 @@
                            }
                        }
                    }
-                   if (moveSucceeded) {
-                       // Delete older code
-                       synchronized (mInstallLock) {
-                           mp.srcArgs.cleanUpResourcesLI();
-                       }
-                       // Send resources available broadcast
-                       sendResourcesChangedBroadcast(true, pkgList, uidArr);
-                       Runtime.getRuntime().gc();
-                   }
+                   // Send resources available broadcast
+                   sendResourcesChangedBroadcast(true, pkgList, uidArr);
                }
                if (!moveSucceeded){
                    // Clean up failed installation
                    if (mp.targetArgs != null) {
                        mp.targetArgs.doPostInstall(
-                               PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
+                               returnCode);
+                   }
+               } else {
+                   // Force a gc to clear things up.
+                   Runtime.getRuntime().gc();
+                   // Delete older code
+                   synchronized (mInstallLock) {
+                       mp.srcArgs.doPostDeleteLI(true);
                    }
                }
                IPackageMoveObserver observer = mp.observer;
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 5f37a42..ee54f73 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -40,6 +40,7 @@
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.util.Log;
+import android.widget.Toast;
 
 import com.android.internal.telephony.Phone;
 import com.android.internal.util.HierarchicalState;
@@ -236,7 +237,7 @@
         }
     }
 
-    public boolean tether(String iface) {
+    public int tether(String iface) {
         Log.d(TAG, "Tethering " + iface);
         TetherInterfaceSM sm = null;
         synchronized (mIfaces) {
@@ -244,21 +245,17 @@
         }
         if (sm == null) {
             Log.e(TAG, "Tried to Tether an unknown iface :" + iface + ", ignoring");
-            return false;
+            return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
         }
-        if (sm.isErrored()) {
-            Log.e(TAG, "Tried to Tether to an errored iface :" + iface + ", ignoring");
-            return false;
-        }
-        if (!sm.isAvailable()) {
+        if (!sm.isAvailable() && !sm.isErrored()) {
             Log.e(TAG, "Tried to Tether an unavailable iface :" + iface + ", ignoring");
-            return false;
+            return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
         }
         sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_REQUESTED));
-        return true;
+        return ConnectivityManager.TETHER_ERROR_NO_ERROR;
     }
 
-    public boolean untether(String iface) {
+    public int untether(String iface) {
         Log.d(TAG, "Untethering " + iface);
         TetherInterfaceSM sm = null;
         synchronized (mIfaces) {
@@ -266,14 +263,26 @@
         }
         if (sm == null) {
             Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring");
-            return false;
+            return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
         }
         if (sm.isErrored()) {
             Log.e(TAG, "Tried to Untethered an errored iface :" + iface + ", ignoring");
-            return false;
+            return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
         }
         sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_UNREQUESTED));
-        return true;
+        return ConnectivityManager.TETHER_ERROR_NO_ERROR;
+    }
+
+    public int getLastTetherError(String iface) {
+        TetherInterfaceSM sm = null;
+        synchronized (mIfaces) {
+            sm = mIfaces.get(iface);
+        }
+        if (sm == null) {
+            Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface + ", ignoring");
+            return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+        }
+        return sm.getLastError();
     }
 
     private void sendTetherStateChangedBroadcast() {
@@ -430,6 +439,28 @@
         }
     }
 
+    private void showErrorToast(int error) {
+        int num;
+        switch(error) {
+        case ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR:
+        case ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR:
+        case ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR:
+        case ConnectivityManager.TETHER_ERROR_MASTER_ERROR:
+            num = com.android.internal.R.string.tether_error_message;
+            break;
+        case ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR:
+        case ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR:
+            num = com.android.internal.R.string.tether_stop_error_message;
+            break;
+        default:
+            // do nothing
+            return;
+        }
+        String text = mContext.getResources().getString(num) + " - EC" + error;
+        Log.e(TAG, text);
+        Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
+    }
+
     private class StateReceiver extends BroadcastReceiver {
         public void onReceive(Context content, Intent intent) {
             String action = intent.getAction();
@@ -542,10 +573,9 @@
                                 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
                             } else {
                                 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down");
-                                // TODO - clean this up - maybe a better regex?
-                                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
-                                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("  "," ");
                             }
+                            ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
+                            ifcg.interfaceFlags = ifcg.interfaceFlags.replace("  "," ");
                             service.setInterfaceConfig(iface, ifcg);
                         }
                     } catch (Exception e) {
@@ -611,6 +641,24 @@
         return retVal;
     }
 
+    public String[] getErroredIfaces() {
+        ArrayList<String> list = new ArrayList<String>();
+        synchronized (mIfaces) {
+            Set keys = mIfaces.keySet();
+            for (Object key : keys) {
+                TetherInterfaceSM sm = mIfaces.get(key);
+                if (sm.isErrored()) {
+                    list.add((String)key);
+                }
+            }
+        }
+        String[] retVal = new String[list.size()];
+        for (int i= 0; i< list.size(); i++) {
+            retVal[i] = list.get(i);
+        }
+        return retVal;
+    }
+
 
     class TetherInterfaceSM extends HierarchicalStateMachine {
         // notification from the master SM that it's in tether mode
@@ -637,8 +685,8 @@
         static final int CMD_STOP_TETHERING_ERROR        = 14;
         // notification from the master SM that it had trouble setting the DNS forwarders
         static final int CMD_SET_DNS_FORWARDERS_ERROR    = 15;
-        // a mechanism to transition self to error state from an enter function
-        static final int CMD_TRANSITION_TO_ERROR         = 16;
+        // a mechanism to transition self to another state from an enter function
+        static final int CMD_TRANSITION_TO_STATE         = 16;
 
         private HierarchicalState mDefaultState;
 
@@ -646,18 +694,11 @@
         private HierarchicalState mStartingState;
         private HierarchicalState mTetheredState;
 
-        private HierarchicalState mMasterTetherErrorState;
-        private HierarchicalState mTetherInterfaceErrorState;
-        private HierarchicalState mUntetherInterfaceErrorState;
-        private HierarchicalState mEnableNatErrorState;
-        private HierarchicalState mDisableNatErrorState;
-        private HierarchicalState mUsbConfigurationErrorState;
-
         private HierarchicalState mUnavailableState;
 
         private boolean mAvailable;
-        private boolean mErrored;
         private boolean mTethered;
+        int mLastError;
 
         String mIfaceName;
         boolean mUsb;
@@ -666,6 +707,7 @@
             super(name);
             mIfaceName = name;
             mUsb = usb;
+            setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
 
             mInitialState = new InitialState();
             addState(mInitialState);
@@ -673,18 +715,6 @@
             addState(mStartingState);
             mTetheredState = new TetheredState();
             addState(mTetheredState);
-            mMasterTetherErrorState = new MasterTetherErrorState();
-            addState(mMasterTetherErrorState);
-            mTetherInterfaceErrorState = new TetherInterfaceErrorState();
-            addState(mTetherInterfaceErrorState);
-            mUntetherInterfaceErrorState = new UntetherInterfaceErrorState();
-            addState(mUntetherInterfaceErrorState);
-            mEnableNatErrorState = new EnableNatErrorState();
-            addState(mEnableNatErrorState);
-            mDisableNatErrorState = new DisableNatErrorState();
-            addState(mDisableNatErrorState);
-            mUsbConfigurationErrorState = new UsbConfigurationErrorState();
-            addState(mUsbConfigurationErrorState);
             mUnavailableState = new UnavailableState();
             addState(mUnavailableState);
 
@@ -698,19 +728,30 @@
             if (current == mInitialState) res += "InitialState";
             if (current == mStartingState) res += "StartingState";
             if (current == mTetheredState) res += "TetheredState";
-            if (current == mMasterTetherErrorState) res += "MasterTetherErrorState";
-            if (current == mTetherInterfaceErrorState) res += "TetherInterfaceErrorState";
-            if (current == mUntetherInterfaceErrorState) res += "UntetherInterfaceErrorState";
-            if (current == mEnableNatErrorState) res += "EnableNatErrorState";
-            if (current == mDisableNatErrorState) res += "DisableNatErrorState";
-            if (current == mUsbConfigurationErrorState) res += "UsbConfigurationErrorState";
             if (current == mUnavailableState) res += "UnavailableState";
             if (mAvailable) res += " - Available";
             if (mTethered) res += " - Tethered";
-            if (mErrored) res += " - ERRORED";
+            res += " - lastError =" + mLastError;
             return res;
         }
 
+        public synchronized int getLastError() {
+            return mLastError;
+        }
+
+        private synchronized void setLastError(int error) {
+            mLastError = error;
+
+            if (isErrored()) {
+                if (mUsb) {
+                    // note everything's been unwound by this point so nothing to do on
+                    // further error..
+                    Tethering.this.configureUsbIface(false);
+                }
+                Tethering.this.showErrorToast(error);
+            }
+        }
+
         // synchronized between this getter and the following setter
         public synchronized boolean isAvailable() {
             return mAvailable;
@@ -731,18 +772,7 @@
 
         // synchronized between this getter and the following setter
         public synchronized boolean isErrored() {
-            return mErrored;
-        }
-
-        private void setErrored(boolean errored) {
-            synchronized (this) {
-                mErrored = errored;
-            }
-            if (errored && mUsb) {
-                // note everything's been unwound by this point so nothing to do on
-                // further error..
-                Tethering.this.configureUsbIface(false);
-            }
+            return (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR);
         }
 
         class InitialState extends HierarchicalState {
@@ -750,7 +780,6 @@
             public void enter() {
                 setAvailable(true);
                 setTethered(false);
-                setErrored(false);
                 sendTetherStateChangedBroadcast();
             }
 
@@ -760,6 +789,7 @@
                 boolean retValue = true;
                 switch (message.what) {
                     case CMD_TETHER_REQUESTED:
+                        setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
                         Message m = mTetherMasterSM.obtainMessage(
                                 TetherMasterSM.CMD_TETHER_MODE_REQUESTED);
                         m.obj = TetherInterfaceSM.this;
@@ -788,8 +818,10 @@
                         m.obj = TetherInterfaceSM.this;
                         mTetherMasterSM.sendMessage(m);
 
-                        m = obtainMessage(CMD_TRANSITION_TO_ERROR);
-                        m.obj = mUsbConfigurationErrorState;
+                        setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
+
+                        m = obtainMessage(CMD_TRANSITION_TO_STATE);
+                        m.obj = mInitialState;
                         sendMessageAtFrontOfQueue(m);
                         return;
                     }
@@ -809,7 +841,8 @@
                         mTetherMasterSM.sendMessage(m);
                         if (mUsb) {
                             if (!Tethering.this.configureUsbIface(false)) {
-                                transitionTo(mUsbConfigurationErrorState);
+                                setLastErrorAndTransitionToInitialState(
+                                    ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
                                 break;
                             }
                         }
@@ -824,7 +857,8 @@
                     case CMD_START_TETHERING_ERROR:
                     case CMD_STOP_TETHERING_ERROR:
                     case CMD_SET_DNS_FORWARDERS_ERROR:
-                        transitionTo(mMasterTetherErrorState);
+                        setLastErrorAndTransitionToInitialState(
+                                ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
                         break;
                     case CMD_INTERFACE_DOWN:
                         m = mTetherMasterSM.obtainMessage(
@@ -833,7 +867,7 @@
                         mTetherMasterSM.sendMessage(m);
                         transitionTo(mUnavailableState);
                         break;
-                   case CMD_TRANSITION_TO_ERROR:
+                   case CMD_TRANSITION_TO_STATE:
                        HierarchicalState s = (HierarchicalState)(message.obj);
                        transitionTo(s);
                        break;
@@ -853,16 +887,24 @@
                 try {
                     service.tetherInterface(mIfaceName);
                 } catch (Exception e) {
-                    Message m = obtainMessage(CMD_TRANSITION_TO_ERROR);
-                    m.obj = mTetherInterfaceErrorState;
+                    setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);
+
+                    Message m = obtainMessage(CMD_TRANSITION_TO_STATE);
+                    m.obj = mInitialState;
                     sendMessageAtFrontOfQueue(m);
                     return;
                 }
                 try {
                     service.enableNat(mIfaceName, mUpstreamIfaceName);
                 } catch (Exception e) {
-                    Message m = obtainMessage(CMD_TRANSITION_TO_ERROR);
-                    m.obj = mEnableNatErrorState;
+                    try {
+                        service.untetherInterface(mIfaceName);
+                    } catch (Exception ee) {}
+
+                    setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR);
+
+                    Message m = obtainMessage(CMD_TRANSITION_TO_STATE);
+                    m.obj = mInitialState;
                     sendMessageAtFrontOfQueue(m);
                     return;
                 }
@@ -890,13 +932,19 @@
                         try {
                             service.disableNat(mIfaceName, mUpstreamIfaceName);
                         } catch (Exception e) {
-                            transitionTo(mDisableNatErrorState);
+                            try {
+                                service.untetherInterface(mIfaceName);
+                            } catch (Exception ee) {}
+
+                            setLastErrorAndTransitionToInitialState(
+                                    ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR);
                             break;
                         }
                         try {
                             service.untetherInterface(mIfaceName);
                         } catch (Exception e) {
-                            transitionTo(mUntetherInterfaceErrorState);
+                            setLastErrorAndTransitionToInitialState(
+                                    ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
                             break;
                         }
                         Message m = mTetherMasterSM.obtainMessage(
@@ -906,13 +954,11 @@
                         if (message.what == CMD_TETHER_UNREQUESTED) {
                             if (mUsb) {
                                 if (!Tethering.this.configureUsbIface(false)) {
-                                    transitionTo(mUsbConfigurationErrorState);
-                                } else {
-                                    transitionTo(mInitialState);
+                                    setLastError(
+                                            ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
                                 }
-                            } else {
-                                transitionTo(mInitialState);
                             }
+                            transitionTo(mInitialState);
                         } else if (message.what == CMD_INTERFACE_DOWN) {
                             transitionTo(mUnavailableState);
                         }
@@ -932,30 +978,36 @@
                         try {
                             service.disableNat(mIfaceName, mUpstreamIfaceName);
                         } catch (Exception e) {
-                            transitionTo(mDisableNatErrorState);
+                            try {
+                                service.untetherInterface(mIfaceName);
+                            } catch (Exception ee) {}
+
+                            setLastErrorAndTransitionToInitialState(
+                                    ConnectivityManager.TETHER_ERROR_DISABLE_NAT_ERROR);
                             break;
                         }
                         try {
                             service.untetherInterface(mIfaceName);
                         } catch (Exception e) {
-                            transitionTo(mUntetherInterfaceErrorState);
+                            setLastErrorAndTransitionToInitialState(
+                                    ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
                             break;
                         }
                         if (error) {
-                            transitionTo(mMasterTetherErrorState);
+                            setLastErrorAndTransitionToInitialState(
+                                    ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
                             break;
                         }
                         Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
                         sendTetherStateChangedBroadcast();
                         if (mUsb) {
                             if (!Tethering.this.configureUsbIface(false)) {
-                                transitionTo(mUsbConfigurationErrorState);
-                                break;
+                                setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
                             }
                         }
                         transitionTo(mInitialState);
                         break;
-                    case CMD_TRANSITION_TO_ERROR:
+                    case CMD_TRANSITION_TO_STATE:
                         HierarchicalState s = (HierarchicalState)(message.obj);
                         transitionTo(s);
                         break;
@@ -971,7 +1023,7 @@
             @Override
             public void enter() {
                 setAvailable(false);
-                setErrored(false);
+                setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
                 setTethered(false);
                 sendTetherStateChangedBroadcast();
             }
@@ -990,95 +1042,11 @@
             }
         }
 
-
-        class ErrorState extends HierarchicalState {
-            int mErrorNotification;
-            @Override
-            public boolean processMessage(Message message) {
-                boolean retValue = true;
-                switch (message.what) {
-                    case CMD_TETHER_REQUESTED:
-                        sendTetherStateChangedBroadcast();
-                        break;
-                    default:
-                        retValue = false;
-                        break;
-                }
-                return retValue;
-            }
+        void setLastErrorAndTransitionToInitialState(int error) {
+            setLastError(error);
+            transitionTo(mInitialState);
         }
 
-        class MasterTetherErrorState extends ErrorState {
-            @Override
-            public void enter() {
-                Log.e(TAG, "Error in Master Tether state " + mIfaceName);
-                setAvailable(false);
-                setErrored(true);
-                sendTetherStateChangedBroadcast();
-            }
-        }
-
-        class TetherInterfaceErrorState extends ErrorState {
-            @Override
-            public void enter() {
-                Log.e(TAG, "Error trying to tether " + mIfaceName);
-                setAvailable(false);
-                setErrored(true);
-                sendTetherStateChangedBroadcast();
-            }
-        }
-
-        class UntetherInterfaceErrorState extends ErrorState {
-            @Override
-            public void enter() {
-                Log.e(TAG, "Error trying to untether " + mIfaceName);
-                setAvailable(false);
-                setErrored(true);
-                sendTetherStateChangedBroadcast();
-            }
-        }
-
-        class EnableNatErrorState extends ErrorState {
-            @Override
-            public void enter() {
-                Log.e(TAG, "Error trying to enable NAT " + mIfaceName);
-                setAvailable(false);
-                setErrored(true);
-
-                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-                try {
-                    service.untetherInterface(mIfaceName);
-                } catch (Exception e) {}
-                sendTetherStateChangedBroadcast();
-            }
-        }
-
-
-        class DisableNatErrorState extends ErrorState {
-            @Override
-            public void enter() {
-                Log.e(TAG, "Error trying to disable NAT " + mIfaceName);
-                setAvailable(false);
-                setErrored(true);
-
-                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-                try {
-                    service.untetherInterface(mIfaceName);
-                } catch (Exception e) {}
-                sendTetherStateChangedBroadcast();
-            }
-        }
-
-        class UsbConfigurationErrorState extends ErrorState {
-            @Override
-            public void enter() {
-                Log.e(TAG, "Error trying to configure USB " + mIfaceName);
-                setAvailable(false);
-                setErrored(true);
-            }
-        }
     }
 
     class TetherMasterSM extends HierarchicalStateMachine {
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 562a9f8..521072e 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -51,7 +51,7 @@
  *
  * DataConnection {
  *   + mDefaultState {
- *        EVENT_RESET { clearSettings, >mInactiveState }.
+ *        EVENT_RESET { clearSettings, notifiyDisconnectCompleted, >mInactiveState }.
  *        EVENT_CONNECT {  notifyConnectCompleted(FailCause.UNKNOWN) }.
  *        EVENT_DISCONNECT { notifyDisconnectCompleted }.
  *
@@ -60,8 +60,10 @@
  *        EVENT_GET_LAST_FAIL_DONE,
  *        EVENT_DEACTIVATE_DONE.
  *     }
- *   ++ # mInactiveState {
- *            EVENT_RESET.
+ *   ++ # mInactiveState 
+ *        e(doNotifications)
+ *        x(clearNotifications) {
+ *            EVENT_RESET { notifiyDisconnectCompleted }.
  *            EVENT_CONNECT {startConnecting, >mActivatingState }.
  *        }
  *   ++   mActivatingState {
@@ -338,6 +340,8 @@
         if (DBG) log("NotifyDisconnectCompleted");
 
         Message msg = dp.onCompletedMsg;
+        log(String.format("msg.what=%d msg.obj=%s",
+                msg.what, ((msg.obj instanceof String) ? (String) msg.obj : "<no-reason>")));
         AsyncResult.forMessage(msg);
         msg.sendToTarget();
 
@@ -437,6 +441,9 @@
                 case EVENT_RESET:
                     if (DBG) log("DcDefaultState: msg.what=EVENT_RESET");
                     clearSettings();
+                    if (msg.obj != null) {
+                        notifyDisconnectCompleted((DisconnectParams) msg.obj);
+                    }
                     transitionTo(mInactiveState);
                     break;
 
@@ -467,9 +474,48 @@
      * The state machine is inactive and expects a EVENT_CONNECT.
      */
     private class DcInactiveState extends HierarchicalState {
+        private ConnectionParams mConnectionParams = null;
+        private FailCause mFailCause = null;
+        private DisconnectParams mDisconnectParams = null;
+
+        public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
+            log("DcInactiveState: setEnterNoticationParams cp,cause");
+            mConnectionParams = cp;
+            mFailCause = cause;
+        }
+
+        public void setEnterNotificationParams(DisconnectParams dp) {
+          log("DcInactiveState: setEnterNoticationParams dp");
+            mDisconnectParams = dp;
+        }
+
         @Override protected void enter() {
             mTag += 1;
+
+            /**
+             * Now that we've transitioned to Inactive state we
+             * can send notifications. Previously we sent the
+             * notifications in the processMessage handler but
+             * that caused a race condition because the synchronous
+             * call to isInactive.
+             */
+            if ((mConnectionParams != null) && (mFailCause != null)) {
+                log("DcInactiveState: enter notifyConnectCompleted");
+                notifyConnectCompleted(mConnectionParams, mFailCause);
+            }
+            if (mDisconnectParams != null) {
+              log("DcInactiveState: enter notifyDisconnectCompleted");
+                notifyDisconnectCompleted(mDisconnectParams);
+            }
         }
+
+        @Override protected void exit() {
+            // clear notifications
+            mConnectionParams = null;
+            mFailCause = null;
+            mDisconnectParams = null;
+        }
+
         @Override protected boolean processMessage(Message msg) {
             boolean retVal;
 
@@ -478,6 +524,9 @@
                     if (DBG) {
                         log("DcInactiveState: msg.what=EVENT_RESET, ignore we're already reset");
                     }
+                    if (msg.obj != null) {
+                        notifyDisconnectCompleted((DisconnectParams) msg.obj);
+                    }
                     retVal = true;
                     break;
 
@@ -526,12 +575,14 @@
                     switch (result) {
                         case SUCCESS:
                             // All is well
-                            notifyConnectCompleted(cp, FailCause.NONE);
+                            mActiveState.setEnterNotificationParams(cp, FailCause.NONE);
                             transitionTo(mActiveState);
                             break;
                         case ERR_BadCommand:
                             // Vendor ril rejected the command and didn't connect.
-                            notifyConnectCompleted(cp, result.mFailCause);
+                            // Transition to inactive but send notifications after
+                            // we've entered the mInactive state.
+                            mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
                             transitionTo(mInactiveState);
                             break;
                         case ERR_BadDns:
@@ -565,7 +616,9 @@
                             int rilFailCause = ((int[]) (ar.result))[0];
                             cause = getFailCauseFromRequest(rilFailCause);
                         }
-                         notifyConnectCompleted(cp, cause);
+                        // Transition to inactive but send notifications after
+                        // we've entered the mInactive state.
+                         mInactiveState.setEnterNotificationParams(cp, cause);
                          transitionTo(mInactiveState);
                     } else {
                         if (DBG) {
@@ -591,6 +644,35 @@
      * The state machine is connected, expecting an EVENT_DISCONNECT.
      */
     private class DcActiveState extends HierarchicalState {
+        private ConnectionParams mConnectionParams = null;
+        private FailCause mFailCause = null;
+
+        public void setEnterNotificationParams(ConnectionParams cp, FailCause cause) {
+            log("DcInactiveState: setEnterNoticationParams cp,cause");
+            mConnectionParams = cp;
+            mFailCause = cause;
+        }
+
+        @Override public void enter() {
+            /**
+             * Now that we've transitioned to Active state we
+             * can send notifications. Previously we sent the
+             * notifications in the processMessage handler but
+             * that caused a race condition because the synchronous
+             * call to isActive.
+             */
+            if ((mConnectionParams != null) && (mFailCause != null)) {
+                log("DcActiveState: enter notifyConnectCompleted");
+                notifyConnectCompleted(mConnectionParams, mFailCause);
+            }
+        }
+
+        @Override protected void exit() {
+            // clear notifications
+            mConnectionParams = null;
+            mFailCause = null;
+        }
+
         @Override protected boolean processMessage(Message msg) {
             boolean retVal;
 
@@ -627,7 +709,9 @@
                     AsyncResult ar = (AsyncResult) msg.obj;
                     DisconnectParams dp = (DisconnectParams) ar.userObj;
                     if (dp.tag == mTag) {
-                        notifyDisconnectCompleted((DisconnectParams) ar.userObj);
+                        // Transition to inactive but send notifications after
+                        // we've entered the mInactive state.
+                        mInactiveState.setEnterNotificationParams((DisconnectParams) ar.userObj);
                         transitionTo(mInactiveState);
                     } else {
                         if (DBG) log("DcDisconnectState EVENT_DEACTIVATE_DONE stale dp.tag="
@@ -660,7 +744,9 @@
                     ConnectionParams cp = (ConnectionParams) ar.userObj;
                     if (cp.tag == mTag) {
                         if (DBG) log("DcDisconnectingBadDnsState msg.what=EVENT_DEACTIVATE_DONE");
-                        notifyConnectCompleted(cp, FailCause.UNKNOWN);
+                        // Transition to inactive but send notifications after
+                        // we've entered the mInactive state.
+                        mInactiveState.setEnterNotificationParams(cp, FailCause.UNKNOWN);
                         transitionTo(mInactiveState);
                     } else {
                         if (DBG) log("DcDisconnectingBadDnsState EVENT_DEACTIVE_DONE stale dp.tag="
@@ -683,9 +769,12 @@
 
     /**
      * Disconnect from the network.
+     *
+     * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
+     *        With AsyncResult.userObj set to the original msg.obj.
      */
-    public void reset() {
-        sendMessage(obtainMessage(EVENT_RESET));
+    public void reset(Message onCompletedMsg) {
+        sendMessage(obtainMessage(EVENT_RESET, new DisconnectParams(onCompletedMsg)));
     }
 
     /**
@@ -726,6 +815,9 @@
     // ****** The following are used for debugging.
 
     /**
+     * TODO: This should be an asynchronous call and we wouldn't
+     * have to use handle the notification in the DcInactiveState.enter.
+     *
      * @return true if the state machine is in the inactive state.
      */
     public boolean isInactive() {
@@ -734,7 +826,10 @@
     }
 
     /**
-     * @return true if the state machine is in the inactive state.
+     * TODO: This should be an asynchronous call and we wouldn't
+     * have to use handle the notification in the DcActiveState.enter.
+     *
+     * @return true if the state machine is in the active state.
      */
     public boolean isActive() {
         boolean retVal = getCurrentState() == mActiveState;
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index cab7b81..e8e18a1 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -101,6 +101,7 @@
     protected static final int EVENT_CDMA_OTA_PROVISION = 35;
     protected static final int EVENT_RESTART_RADIO = 36;
     protected static final int EVENT_SET_MASTER_DATA_ENABLE = 37;
+    protected static final int EVENT_RESET_DONE = 38;
 
     /***** Constants *****/
 
@@ -265,6 +266,7 @@
     protected abstract void onRadioOffOrNotAvailable();
     protected abstract void onDataSetupComplete(AsyncResult ar);
     protected abstract void onDisconnectDone(AsyncResult ar);
+    protected abstract void onResetDone(AsyncResult ar);
     protected abstract void onVoiceCallStarted();
     protected abstract void onVoiceCallEnded();
     protected abstract void onCleanUpConnection(boolean tearDown, String reason);
@@ -331,6 +333,10 @@
                 onSetDataEnabled(enabled);
                 break;
 
+            case EVENT_RESET_DONE:
+                onResetDone((AsyncResult) msg.obj);
+                break;
+
             default:
                 Log.e("DATA", "Unidentified event = " + msg.what);
                 break;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index b5461bf..9218715 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -368,7 +368,7 @@
      * @param reason reason for the clean up.
      */
     private void cleanUpConnection(boolean tearDown, String reason) {
-        if (DBG) log("Clean up connection due to " + reason);
+        if (DBG) log("cleanUpConnection: reason: " + reason);
 
         // Clear the reconnect alarm, if set.
         if (mReconnectIntent != null) {
@@ -380,25 +380,25 @@
 
         setState(State.DISCONNECTING);
 
-        for (DataConnection connBase : dataConnectionList) {
-            CdmaDataConnection conn = (CdmaDataConnection) connBase;
-
+        boolean notificationDeferred = false;
+        for (DataConnection conn : dataConnectionList) {
             if(conn != null) {
                 if (tearDown) {
-                    Message msg = obtainMessage(EVENT_DISCONNECT_DONE, reason);
-                    conn.disconnect(msg);
+                    if (DBG) log("cleanUpConnection: teardown, call conn.disconnect");
+                    conn.disconnect(obtainMessage(EVENT_DISCONNECT_DONE, reason));
                 } else {
-                    conn.reset();
+                    if (DBG) log("cleanUpConnection: !tearDown, call conn.reset");
+                    conn.reset(obtainMessage(EVENT_RESET_DONE, reason));
                 }
+                notificationDeferred = true;
             }
         }
 
         stopNetStatPoll();
 
-        if (!tearDown) {
-            setState(State.IDLE);
-            phone.notifyDataConnection(reason);
-            mIsApnActive = false;
+        if (!notificationDeferred) {
+            if (DBG) log("cleanupConnection: !tearDown && !resettingConn");
+            gotoIdleAndNotifyDataConnection(reason);
         }
     }
 
@@ -622,6 +622,13 @@
         setState(State.FAILED);
     }
 
+    private void gotoIdleAndNotifyDataConnection(String reason) {
+        if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
+        setState(State.IDLE);
+        phone.notifyDataConnection(reason);
+        mIsApnActive = false;
+    }
+
     protected void onRecordsLoaded() {
         if (state == State.FAILED) {
             cleanUpConnection(false, null);
@@ -731,7 +738,7 @@
     }
 
     /**
-     * @override com.android.internal.telephony.DataConnectionTracker
+     * Called when EVENT_DISCONNECT_DONE is received.
      */
     protected void onDisconnectDone(AsyncResult ar) {
         if(DBG) log("EVENT_DISCONNECT_DONE");
@@ -762,6 +769,20 @@
     }
 
     /**
+     * Called when EVENT_RESET_DONE is received so goto
+     * IDLE state and send notifications to those interested.
+     */
+    @Override
+    protected void onResetDone(AsyncResult ar) {
+      if (DBG) log("EVENT_RESET_DONE");
+      String reason = null;
+      if (ar.userObj instanceof String) {
+          reason = (String) ar.userObj;
+      }
+      gotoIdleAndNotifyDataConnection(reason);
+    }
+
+    /**
      * @override com.android.internal.telephony.DataConnectionTracker
      */
     protected void onVoiceCallStarted() {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 30beaaa..f26e54e 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -502,20 +502,22 @@
 
         setState(State.DISCONNECTING);
 
+        boolean notificationDeferred = false;
         for (DataConnection conn : pdpList) {
             if (tearDown) {
-                Message msg = obtainMessage(EVENT_DISCONNECT_DONE, reason);
-                conn.disconnect(msg);
+                if (DBG) log("cleanUpConnection: teardown, call conn.disconnect");
+                conn.disconnect(obtainMessage(EVENT_DISCONNECT_DONE, reason));
             } else {
-                conn.reset();
+                if (DBG) log("cleanUpConnection: !tearDown, call conn.reset");
+                conn.reset(obtainMessage(EVENT_RESET_DONE, reason));
             }
+            notificationDeferred = true;
         }
         stopNetStatPoll();
 
-        if (!tearDown) {
-            setState(State.IDLE);
-            phone.notifyDataConnection(reason);
-            mActiveApn = null;
+        if (!notificationDeferred) {
+            if (DBG) log("cleanupConnection: !tearDown && !resettingConn");
+            gotoIdleAndNotifyDataConnection(reason);
         }
     }
 
@@ -749,6 +751,13 @@
         mReregisterOnReconnectFailure = false;
     }
 
+    private void gotoIdleAndNotifyDataConnection(String reason) {
+        if (DBG) log("gotoIdleAndNotifyDataConnection: reason=" + reason);
+        setState(State.IDLE);
+        phone.notifyDataConnection(reason);
+        mActiveApn = null;
+    }
+
     /**
      * This is a kludge to deal with the fact that
      * the PDP state change notification doesn't always work
@@ -1172,6 +1181,9 @@
         }
     }
 
+    /**
+     * Called when EVENT_DISCONNECT_DONE is received.
+     */
     protected void onDisconnectDone(AsyncResult ar) {
         String reason = null;
         if(DBG) log("EVENT_DISCONNECT_DONE");
@@ -1186,6 +1198,19 @@
         }
     }
 
+    /**
+     * Called when EVENT_RESET_DONE is received.
+     */
+    @Override
+    protected void onResetDone(AsyncResult ar) {
+        if (DBG) log("EVENT_RESET_DONE");
+        String reason = null;
+        if (ar.userObj instanceof String) {
+            reason = (String) ar.userObj;
+        }
+        gotoIdleAndNotifyDataConnection(reason);
+    }
+
     protected void onPollPdp() {
         if (state == State.CONNECTED) {
             // only poll when connected
@@ -1487,5 +1512,4 @@
     protected void log(String s) {
         Log.d(LOG_TAG, "[GsmDataConnectionTracker] " + s);
     }
-
 }
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index 5e3895a..50eca02 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -263,6 +263,9 @@
         if (expInstallLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
             return true;
         }
+        if (expInstallLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
+            return false;
+        }
         // TODO Out of memory checks here.
         boolean checkSd = false;
         int setLoc = 0;
@@ -403,7 +406,7 @@
             return ip;
         } finally {
             if (cleanUp) {
-                //cleanUpInstall(ip);
+                cleanUpInstall(ip);
             }
         }
     }
@@ -931,9 +934,9 @@
 
     public void testManifestInstallLocationFwdLockedSdcard() {
         installFromRawResource("install.apk", R.raw.install_loc_sdcard,
-                PackageManager.INSTALL_FORWARD_LOCK, true, true,
-                PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION,
-                PackageInfo.INSTALL_LOCATION_AUTO);
+                PackageManager.INSTALL_FORWARD_LOCK, true, false,
+                -1,
+                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
     }
 
     public void xxxtestClearAllSecureContainers() {
@@ -1050,6 +1053,21 @@
         }
     }
 
+    private int getInstallLoc() {
+        boolean userSetting = false;
+        int origDefaultLoc = PackageInfo.INSTALL_LOCATION_AUTO;
+        try {
+            userSetting = Settings.System.getInt(mContext.getContentResolver(), Settings.System.SET_INSTALL_LOCATION) != 0;
+            origDefaultLoc = Settings.System.getInt(mContext.getContentResolver(), Settings.System.DEFAULT_INSTALL_LOCATION);
+        } catch (SettingNotFoundException e1) {
+        }
+        return origDefaultLoc;
+    }
+
+    private void setInstallLoc(int loc) {
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.DEFAULT_INSTALL_LOCATION, loc);
+    }
     /*
      * Utility function that reads a apk bundled as a raw resource
      * copies it into own data directory and invokes
@@ -1058,6 +1076,8 @@
      */
     public void moveFromRawResource(int installFlags, int moveFlags,
             int expRetCode) {
+        int origDefaultLoc = getInstallLoc();
+        setInstallLoc(PackageInfo.INSTALL_LOCATION_AUTO);
         // Install first
         InstallParams ip = sampleInstallFromRawResource(installFlags, false);
         ApplicationInfo oldAppInfo = null;
@@ -1091,6 +1111,8 @@
             failStr("Failed with exception : " + e);
         } finally {
             cleanUpInstall(ip);
+            // Restore default install location
+            setInstallLoc(origDefaultLoc);
         }
     }
 
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 9fcb21c..ea021d8 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -637,6 +637,7 @@
         sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
         if (application != NULL) {
             fullyQualifyClassName(origPackage, application, String16("name"));
+            fullyQualifyClassName(origPackage, application, String16("backupAgent"));
 
             Vector<sp<XMLNode> >& children = const_cast<Vector<sp<XMLNode> >&>(application->getChildren());
             for (size_t i = 0; i < children.size(); i++) {
@@ -1778,6 +1779,40 @@
     rules.editValueAt(index).add(where);
 }
 
+void
+addProguardKeepRule(ProguardKeepSet* keep, const String8& inClassName,
+        const char* pkg, const String8& srcName, int line)
+{
+    String8 className(inClassName);
+    if (pkg != NULL) {
+        // asdf     --> package.asdf
+        // .asdf  .a.b  --> package.asdf package.a.b
+        // asdf.adsf --> asdf.asdf
+        const char* p = className.string();
+        const char* q = strchr(p, '.');
+        if (p == q) {
+            className = pkg;
+            className.append(inClassName);
+        } else if (q == NULL) {
+            className = pkg;
+            className.append(".");
+            className.append(inClassName);
+        }
+    }
+    
+    String8 rule("-keep class ");
+    rule += className;
+    rule += " { <init>(...); }";
+
+    String8 location("view ");
+    location += srcName;
+    char lineno[20];
+    sprintf(lineno, ":%d", line);
+    location += lineno;
+
+    keep->add(rule, location);
+}
+
 status_t
 writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
 {
@@ -1839,6 +1874,13 @@
             if (tag == "application") {
                 inApplication = true;
                 keepTag = true;
+                
+                String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android",
+                        "backupAgent", &error);
+                if (agent.length() > 0) {
+                    addProguardKeepRule(keep, agent, pkg.string(),
+                            assFile->getPrintableSource(), tree.getLineNumber());
+                }
             } else if (tag == "instrumentation") {
                 keepTag = true;
             }
@@ -1856,31 +1898,8 @@
                 return -1;
             }
             if (name.length() > 0) {
-                // asdf     --> package.asdf
-                // .asdf  .a.b  --> package.asdf package.a.b
-                // asdf.adsf --> asdf.asdf
-                String8 rule("-keep class ");
-                const char* p = name.string();
-                const char* q = strchr(p, '.');
-                if (p == q) {
-                    rule += pkg;
-                    rule += name;
-                } else if (q == NULL) {
-                    rule += pkg;
-                    rule += ".";
-                    rule += name;
-                } else {
-                    rule += name;
-                }
-
-                String8 location = tag;
-                location += " ";
-                location += assFile->getSourceFile();
-                char lineno[20];
-                sprintf(lineno, ":%d", tree.getLineNumber());
-                location += lineno;
-
-                keep->add(rule, location);
+                addProguardKeepRule(keep, name, pkg.string(),
+                        assFile->getPrintableSource(), tree.getLineNumber());
             }
         }
     }
@@ -1888,23 +1907,6 @@
     return NO_ERROR;
 }
 
-void
-addProguardKeepRule(ProguardKeepSet* keep, const String8& className,
-        const String8& srcName, int line)
-{
-    String8 rule("-keep class ");
-    rule += className;
-    rule += " { <init>(...); }";
-
-    String8 location("view ");
-    location += srcName;
-    char lineno[20];
-    sprintf(lineno, ":%d", line);
-    location += lineno;
-
-    keep->add(rule, location);
-}
-
 status_t
 writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
         const char* startTag, const char* altTag)
@@ -1946,7 +1948,7 @@
 
         // If there is no '.', we'll assume that it's one of the built in names.
         if (strchr(tag.string(), '.')) {
-            addProguardKeepRule(keep, tag,
+            addProguardKeepRule(keep, tag, NULL,
                     layoutFile->getPrintableSource(), tree.getLineNumber());
         } else if (altTag != NULL && tag == altTag) {
             ssize_t classIndex = tree.indexOfAttribute(NULL, "class");
@@ -1956,7 +1958,7 @@
             } else {
                 size_t len;
                 addProguardKeepRule(keep,
-                        String8(tree.getAttributeStringValue(classIndex, &len)),
+                        String8(tree.getAttributeStringValue(classIndex, &len)), NULL,
                         layoutFile->getPrintableSource(), tree.getLineNumber());
             }
         }