Merge "Import translations."
diff --git a/api/current.txt b/api/current.txt
index 7edfa53..97be454 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -992,6 +992,7 @@
     field public static final int textColorTertiary = 16843282; // 0x1010212
     field public static final int textColorTertiaryInverse = 16843283; // 0x1010213
     field public static final int textCursorDrawable = 16843618; // 0x1010362
+    field public static final int textDirection = 16843688; // 0x10103a8
     field public static final int textEditNoPasteWindowLayout = 16843541; // 0x1010315
     field public static final int textEditPasteWindowLayout = 16843540; // 0x1010314
     field public static final int textEditSideNoPasteWindowLayout = 16843615; // 0x101035f
@@ -20478,6 +20479,19 @@
     method public int getTopPadding();
   }
 
+  public abstract interface TextDirectionHeuristic {
+  }
+
+  public class TextDirectionHeuristics {
+    ctor public TextDirectionHeuristics();
+    field public static final android.text.TextDirectionHeuristic ANYRTL_LTR;
+    field public static final android.text.TextDirectionHeuristic FIRSTSTRONG_LTR;
+    field public static final android.text.TextDirectionHeuristic FIRSTSTRONG_RTL;
+    field public static final android.text.TextDirectionHeuristic LOCALE;
+    field public static final android.text.TextDirectionHeuristic LTR;
+    field public static final android.text.TextDirectionHeuristic RTL;
+  }
+
   public class TextPaint extends android.graphics.Paint {
     ctor public TextPaint();
     ctor public TextPaint(int);
@@ -23171,6 +23185,7 @@
     method public final android.view.ViewParent getParent();
     method public float getPivotX();
     method public float getPivotY();
+    method public int getResolvedTextDirection();
     method public android.content.res.Resources getResources();
     method public final int getRight();
     method protected float getRightFadingEdgeStrength();
@@ -23190,6 +23205,7 @@
     method public int getSystemUiVisibility();
     method public java.lang.Object getTag();
     method public java.lang.Object getTag(int);
+    method public int getTextDirection();
     method public final int getTop();
     method protected float getTopFadingEdgeStrength();
     method protected int getTopPaddingOffset();
@@ -23321,8 +23337,10 @@
     method public void requestLayout();
     method public boolean requestRectangleOnScreen(android.graphics.Rect);
     method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
+    method protected void resetResolvedTextDirection();
     method public static int resolveSize(int, int);
     method public static int resolveSizeAndState(int, int, int);
+    method protected void resolveTextDirection();
     method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>);
     method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable>);
     method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
@@ -23402,6 +23420,7 @@
     method public void setSystemUiVisibility(int);
     method public void setTag(java.lang.Object);
     method public void setTag(int, java.lang.Object);
+    method public void setTextDirection(int);
     method public final void setTop(int);
     method public void setTouchDelegate(android.view.TouchDelegate);
     method public void setTranslationX(float);
@@ -23424,6 +23443,7 @@
     method public boolean willNotCacheDrawing();
     method public boolean willNotDraw();
     field public static final android.util.Property ALPHA;
+    field protected static int DEFAULT_TEXT_DIRECTION;
     field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
     field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
     field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
@@ -23500,6 +23520,12 @@
     field public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2
     field public static final int SYSTEM_UI_FLAG_LOW_PROFILE = 1; // 0x1
     field public static final int SYSTEM_UI_FLAG_VISIBLE = 0; // 0x0
+    field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
+    field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
+    field public static final int TEXT_DIRECTION_INHERIT = 0; // 0x0
+    field public static final int TEXT_DIRECTION_LOCALE = 5; // 0x5
+    field public static final int TEXT_DIRECTION_LTR = 3; // 0x3
+    field public static final int TEXT_DIRECTION_RTL = 4; // 0x4
     field public static final android.util.Property TRANSLATION_X;
     field public static final android.util.Property TRANSLATION_Y;
     field protected static final java.lang.String VIEW_LOG_TAG = "View";
@@ -23766,7 +23792,6 @@
     method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public void requestTransparentRegion(android.view.View);
     method protected void resetResolvedLayoutDirection();
-    method protected void resetResolvedTextDirection();
     method public void scheduleLayoutAnimation();
     method public void setAddStatesFromChildren(boolean);
     method public void setAlwaysDrawnWithCacheEnabled(boolean);
@@ -27595,7 +27620,6 @@
     method protected void resetResolvedDrawables();
     method protected void resetResolvedLayoutDirection();
     method protected void resolveDrawables();
-    method protected void resolveTextDirection();
     method public void setAllCaps(boolean);
     method public final void setAutoLinkMask(int);
     method public void setCompoundDrawablePadding(int);
diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java
index e5c1e5b..ae41eab 100644
--- a/core/java/android/text/TextDirectionHeuristics.java
+++ b/core/java/android/text/TextDirectionHeuristics.java
@@ -17,13 +17,10 @@
 package android.text;
 
 
-import java.util.Locale;
-
 import android.util.LocaleUtil;
 
 /**
  * Some objects that implement TextDirectionHeuristic.
- * @hide
  */
 public class TextDirectionHeuristics {
 
@@ -74,9 +71,8 @@
      * Computes the text direction based on an algorithm.  Subclasses implement
      * {@link #defaultIsRtl} to handle cases where the algorithm cannot determine the
      * direction from the text alone.
-     * @hide
      */
-    public static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic {
+    private static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristic {
         private final TextDirectionAlgorithm mAlgorithm;
 
         public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) {
@@ -157,13 +153,11 @@
     /**
      * Interface for an algorithm to guess the direction of a paragraph of text.
      *
-     * @hide
      */
-    public static interface TextDirectionAlgorithm {
+    private static interface TextDirectionAlgorithm {
         /**
          * Returns whether the range of text is RTL according to the algorithm.
          *
-         * @hide
          */
         TriState checkRtl(char[] text, int start, int count);
     }
@@ -173,9 +167,8 @@
      * the paragraph direction.  This is the standard Unicode Bidirectional
      * algorithm.
      *
-     * @hide
      */
-    public static class FirstStrong implements TextDirectionAlgorithm {
+    private static class FirstStrong implements TextDirectionAlgorithm {
         @Override
         public TriState checkRtl(char[] text, int start, int count) {
             TriState result = TriState.UNKNOWN;
@@ -196,9 +189,8 @@
      * character (e.g. excludes LRE, LRO, RLE, RLO) to determine the
      * direction of text.
      *
-     * @hide
      */
-    public static class AnyStrong implements TextDirectionAlgorithm {
+    private static class AnyStrong implements TextDirectionAlgorithm {
         private final boolean mLookForRtl;
 
         @Override
@@ -239,7 +231,7 @@
     /**
      * Algorithm that uses the Locale direction to force the direction of a paragraph.
      */
-    public static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl {
+    private static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl {
 
         public TextDirectionHeuristicLocale() {
             super(null);
diff --git a/core/java/android/text/method/KeyListener.java b/core/java/android/text/method/KeyListener.java
index 8594852..318149a 100644
--- a/core/java/android/text/method/KeyListener.java
+++ b/core/java/android/text/method/KeyListener.java
@@ -22,7 +22,7 @@
 
 /**
  * Interface for converting text key events into edit operations on an
- * Editable class.  Note that for must cases this interface has been
+ * Editable class.  Note that for most cases this interface has been
  * superceded by general soft input methods as defined by
  * {@link android.view.inputmethod.InputMethod}; it should only be used
  * for cases where an application has its own on-screen keypad and also wants
diff --git a/core/java/android/text/method/TransformationMethod.java b/core/java/android/text/method/TransformationMethod.java
index 9f51c2a..b542109 100644
--- a/core/java/android/text/method/TransformationMethod.java
+++ b/core/java/android/text/method/TransformationMethod.java
@@ -18,7 +18,6 @@
 
 import android.graphics.Rect;
 import android.view.View;
-import android.widget.TextView;
 
 /**
  * TextView uses TransformationMethods to do things like replacing the
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 056be7f..87104f4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2618,7 +2618,6 @@
 
     /**
      * Text direction is inherited thru {@link ViewGroup}
-     * @hide
      */
     public static final int TEXT_DIRECTION_INHERIT = 0;
 
@@ -2627,7 +2626,6 @@
      * determines the paragraph direction. If there is no strong directional character, the
      * paragraph direction is the view's resolved layout direction.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_FIRST_STRONG = 1;
 
@@ -2636,42 +2634,36 @@
      * any strong RTL character, otherwise it is LTR if it contains any strong LTR characters.
      * If there are neither, the paragraph direction is the view's resolved layout direction.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_ANY_RTL = 2;
 
     /**
      * Text direction is forced to LTR.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_LTR = 3;
 
     /**
      * Text direction is forced to RTL.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_RTL = 4;
 
     /**
      * Text direction is coming from the system Locale.
      *
-     * @hide
      */
     public static final int TEXT_DIRECTION_LOCALE = 5;
 
     /**
      * Default text direction is inherited
      *
-     * @hide
      */
     protected static int DEFAULT_TEXT_DIRECTION = TEXT_DIRECTION_INHERIT;
 
     /**
      * The text direction that has been defined by {@link #setTextDirection(int)}.
      *
-     * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "text", mapping = {
             @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
@@ -2689,7 +2681,6 @@
      * not TEXT_DIRECTION_INHERIT, otherwise resolution proceeds up the parent
      * chain of the view.
      *
-     * {@hide}
      */
     @ViewDebug.ExportedProperty(category = "text", mapping = {
             @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
@@ -3775,14 +3766,6 @@
         }
 
         if ((mPrivateFlags & FOCUSED) != 0) {
-            // If this is the first focusable do not clear focus since the we
-            // try to give it focus every time a view clears its focus. Hence,
-            // the view that would gain focus already has it.
-            View firstFocusable = getFirstFocusable();
-            if (firstFocusable == this) {
-                return;
-            }
-
             mPrivateFlags &= ~FOCUSED;
 
             if (mParent != null) {
@@ -3791,24 +3774,9 @@
 
             onFocusChanged(false, 0, null);
             refreshDrawableState();
-
-            // The view cleared focus and invoked the callbacks, so  now is the
-            // time to give focus to the the first focusable to ensure that the
-            // gain focus is announced after clear focus.
-            if (firstFocusable != null) {
-                firstFocusable.requestFocus(FOCUS_FORWARD);
-            }
         }
     }
 
-    private View getFirstFocusable() {
-        ViewRootImpl viewRoot = getViewRootImpl();
-        if (viewRoot != null) {
-            return viewRoot.focusSearch(null, FOCUS_FORWARD);
-        }
-        return null;
-    }
-
     /**
      * Called to clear the focus of a view that is about to be removed.
      * Doesn't call clearChildFocus, which prevents this view from taking
@@ -14106,7 +14074,6 @@
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE},
      *
-     * @hide
      */
     public int getTextDirection() {
         return mTextDirection;
@@ -14124,7 +14091,6 @@
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE},
      *
-     * @hide
      */
     public void setTextDirection(int textDirection) {
         if (textDirection != mTextDirection) {
@@ -14145,7 +14111,6 @@
      * {@link #TEXT_DIRECTION_RTL},
      * {@link #TEXT_DIRECTION_LOCALE},
      *
-     * @hide
      */
     public int getResolvedTextDirection() {
         if (mResolvedTextDirection == TEXT_DIRECTION_INHERIT) {
@@ -14157,7 +14122,6 @@
     /**
      * Resolve the text direction.
      *
-     * @hide
      */
     protected void resolveTextDirection() {
         if (mTextDirection != TEXT_DIRECTION_INHERIT) {
@@ -14174,7 +14138,6 @@
     /**
      * Reset resolved text direction. Will be resolved during a call to getResolvedTextDirection().
      *
-     * @hide
      */
     protected void resetResolvedTextDirection() {
         mResolvedTextDirection = TEXT_DIRECTION_INHERIT;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a2e85a3..bc147ac 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -675,14 +675,11 @@
      */
     @Override
     public void clearFocus() {
-        if (DBG) {
-            System.out.println(this + " clearFocus()");
-        }
-        if (mFocused == null) {
-            super.clearFocus();
-        } else {
+        super.clearFocus();
+
+        // clear any child focus if it exists
+        if (mFocused != null) {
             mFocused.clearFocus();
-            mFocused = null;
         }
     }
 
@@ -694,12 +691,12 @@
         if (DBG) {
             System.out.println(this + " unFocus()");
         }
-        if (mFocused == null) {
-            super.unFocus();
-        } else {
+
+        super.unFocus();
+        if (mFocused != null) {
             mFocused.unFocus();
-            mFocused = null;
         }
+        mFocused = null;
     }
 
     /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1930a5e..1c3bbfa 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -174,7 +174,6 @@
     View mView;
     View mFocusedView;
     View mRealFocusedView;  // this is not set to null in touch mode
-    View mOldFocusedView;
     int mViewVisibility;
     boolean mAppVisible = true;
     int mOrigWindowType = -1;
@@ -2273,34 +2272,33 @@
 
     public void requestChildFocus(View child, View focused) {
         checkThread();
-
-        if (DEBUG_INPUT_RESIZE) {
-            Log.v(TAG, "Request child focus: focus now " + focused);
+        if (mFocusedView != focused) {
+            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused);
+            scheduleTraversals();
         }
-
-        mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, focused);
-        scheduleTraversals();
-
         mFocusedView = mRealFocusedView = focused;
+        if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now "
+                + mFocusedView);
     }
 
     public void clearChildFocus(View child) {
         checkThread();
 
-        if (DEBUG_INPUT_RESIZE) {
-            Log.v(TAG, "Clearing child focus");
-        }
+        View oldFocus = mFocusedView;
 
-        mOldFocusedView = mFocusedView;
-
-        // Invoke the listener only if there is no view to take focus
-        if (focusSearch(null, View.FOCUS_FORWARD) == null) {
-            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, null);
-        }
-
+        if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Clearing child focus");
         mFocusedView = mRealFocusedView = null;
+        if (mView != null && !mView.hasFocus()) {
+            // If a view gets the focus, the listener will be invoked from requestChildFocus()
+            if (!mView.requestFocus(View.FOCUS_FORWARD)) {
+                mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
+            }
+        } else if (oldFocus != null) {
+            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
+        }
     }
 
+
     public void focusableViewAvailable(View v) {
         checkThread();
 
@@ -2772,7 +2770,6 @@
                         mView.unFocus();
                         mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
                         mFocusedView = null;
-                        mOldFocusedView = null;
                         return true;
                     }
                 }
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 570f0f9..909f383 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -426,7 +426,8 @@
                     }
 
                     // A new word has been created across the interval boundaries with this edit.
-                    // Previous spans (ended on start / started on end) removed, not valid anymore
+                    // The previous spans (that ended on start / started on end) are not valid
+                    // anymore and must be removed.
                     if (wordStart < start && wordEnd > start) {
                         removeSpansAt(editable, start, spellCheckSpans);
                         removeSpansAt(editable, start, suggestionSpans);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 99349b0..7db8a1e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3472,6 +3472,9 @@
         if (mText.length() == 0) {
             invalidate();
         }
+
+        // Invalidate display list if hint will be used
+        if (mText.length() == 0 && mHint != null) mTextDisplayListIsValid = false;
     }
 
     /**
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 2ac3ca8..c5ff16e 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -116,14 +116,9 @@
     return (jboolean)(::wifi_unload_driver() == 0);
 }
 
-static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject)
+static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject, jboolean p2pSupported)
 {
-    return (jboolean)(::wifi_start_supplicant() == 0);
-}
-
-static jboolean android_net_wifi_startP2pSupplicant(JNIEnv* env, jobject)
-{
-    return (jboolean)(::wifi_start_p2p_supplicant() == 0);
+    return (jboolean)(::wifi_start_supplicant(p2pSupported) == 0);
 }
 
 static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject)
@@ -207,8 +202,7 @@
     { "loadDriver", "()Z",  (void *)android_net_wifi_loadDriver },
     { "isDriverLoaded", "()Z",  (void *)android_net_wifi_isDriverLoaded },
     { "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },
-    { "startSupplicant", "()Z",  (void *)android_net_wifi_startSupplicant },
-    { "startP2pSupplicant", "()Z",  (void *)android_net_wifi_startP2pSupplicant },
+    { "startSupplicant", "(Z)Z",  (void *)android_net_wifi_startSupplicant },
     { "killSupplicant", "()Z",  (void *)android_net_wifi_killSupplicant },
     { "connectToSupplicant", "(Ljava/lang/String;)Z",
             (void *)android_net_wifi_connectToSupplicant },
diff --git a/core/res/res/anim/recents_fade_in.xml b/core/res/res/anim/recents_fade_in.xml
new file mode 100644
index 0000000..c516fadb
--- /dev/null
+++ b/core/res/res/anim/recents_fade_in.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/recents_fade_in.xml
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+        android:interpolator="@interpolator/decelerate_quad"
+        android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:duration="150" />
diff --git a/core/res/res/anim/recents_fade_out.xml b/core/res/res/anim/recents_fade_out.xml
new file mode 100644
index 0000000..7468cc19
--- /dev/null
+++ b/core/res/res/anim/recents_fade_out.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/res/anim/recents_fade_out.xml
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@interpolator/accelerate_quad"
+    android:fromAlpha="1.0"
+    android:toAlpha="0.0"
+    android:duration="150" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e84cb5c..4e55b4f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -218,6 +218,7 @@
   <java-symbol type="bool" name="config_allowActionMenuItemTextWithIcon" />
   <java-symbol type="bool" name="config_bluetooth_adapter_quick_switch" />
   <java-symbol type="bool" name="config_bluetooth_sco_off_call" />
+  <java-symbol type="bool" name="config_alwaysUseCdmaRssi" />
   <java-symbol type="bool" name="config_duplicate_port_omadm_wappush" />
   <java-symbol type="bool" name="config_enable_emergency_call_while_sim_locked" />
   <java-symbol type="bool" name="config_enable_puk_unlock_screen" />
@@ -1083,6 +1084,7 @@
   <java-symbol type="style" name="ActiveWallpaperSettings" />
   <java-symbol type="style" name="Animation.InputMethodFancy" />
   <java-symbol type="style" name="Animation.Wallpaper" />
+  <java-symbol type="style" name="Animation.RecentApplications" />
   <java-symbol type="style" name="Animation.ZoomButtons" />
   <java-symbol type="style" name="PreviewWallpaperSettings" />
   <java-symbol type="style" name="TextAppearance.SlidingTabActive" />
@@ -3494,4 +3496,6 @@
      =============================================================== -->
   <public type="attr" name="isolatedProcess" id="0x010103a7" />
 
+  <public type="attr" name="textDirection"/>
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4995d2c..c95dddd 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2708,11 +2708,6 @@
     <!-- Do not translate. Default access point SSID used for tethering -->
     <string name="wifi_tether_configure_ssid_default" translatable="false">AndroidAP</string>
 
-    <!-- Wi-Fi p2p dialog title-->
-    <string name="wifi_p2p_dialog_title">Wi-Fi Direct</string>
-    <string name="wifi_p2p_turnon_message">Start Wi-Fi Direct. This will turn off Wi-Fi client/hotspot.</string>
-    <string name="wifi_p2p_failed_message">Couldn\'t start Wi-Fi Direct.</string>
-
     <string name="accept">Accept</string>
     <string name="decline">Decline</string>
     <string name="wifi_p2p_invitation_sent_title">Invitation sent</string>
@@ -2723,9 +2718,6 @@
     <string name="wifi_p2p_enter_pin_message">Type the required PIN: </string>
     <string name="wifi_p2p_show_pin_message">PIN: </string>
 
-    <string name="wifi_p2p_enabled_notification_title">Wi-Fi Direct is on</string>
-    <string name="wifi_p2p_enabled_notification_message">Touch for settings</string>
-
     <!-- Name of the dialog that lets the user choose an accented character to insert -->
     <string name="select_character">Insert character</string>
 
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 571c4ad..610bad8 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -198,8 +198,10 @@
     <!-- A special animation we can use for recent applications,
          for devices that can support it (do alpha transformations). -->
     <style name="Animation.RecentApplications">
-        <item name="windowEnterAnimation">@anim/fade_in</item>
-        <item name="windowExitAnimation">@anim/fade_out</item>
+        <item name="windowEnterAnimation">@anim/recents_fade_in</item>
+        <item name="windowShowAnimation">@anim/recents_fade_in</item>
+        <item name="windowExitAnimation">@anim/recents_fade_out</item>
+        <item name="windowHideAnimation">@anim/recents_fade_out</item>
     </style>
 
     <!-- A special animation value used internally for popup windows. -->
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocus.java b/core/tests/coretests/src/android/widget/focus/RequestFocus.java
index 21d762a..af9ee17 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocus.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocus.java
@@ -21,7 +21,9 @@
 import android.app.Activity;
 import android.os.Bundle;
 import android.os.Handler;
+import android.widget.LinearLayout;
 import android.widget.Button;
+import android.view.View;
 
 /**
  * Exercises cases where elements of the UI are requestFocus()ed.
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
index baf587e..a78b0c9 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
@@ -16,27 +16,21 @@
 
 package android.widget.focus;
 
-import android.os.Handler;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.UiThreadTest;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.AndroidRuntimeException;
-import android.view.View;
-import android.view.View.OnFocusChangeListener;
-import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
-import android.widget.Button;
-
+import android.widget.focus.RequestFocus;
 import com.android.frameworks.coretests.R;
 
-import java.util.ArrayList;
-import java.util.List;
+import android.os.Handler;
+import android.test.ActivityInstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.widget.Button;
+import android.util.AndroidRuntimeException;
 
 /**
  * {@link RequestFocusTest} is set up to exercise cases where the views that
  * have focus become invisible or GONE.
  */
-public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFocus> {
+public class RequestFocusTest extends ActivityInstrumentationTestCase<RequestFocus> {
 
     private Button mTopLeftButton;
     private Button mBottomLeftButton;
@@ -45,7 +39,7 @@
     private Handler mHandler;
 
     public RequestFocusTest() {
-        super(RequestFocus.class);
+        super("com.android.frameworks.coretests", RequestFocus.class);
     }
 
     @Override
@@ -100,145 +94,4 @@
                          e.getClass().getName());
         }
     }
-
-    /**
-     * This tests checks the case in which the first focusable View clears focus.
-     * In such a case the framework tries to give the focus to another View starting
-     * from the top. Hence, the framework will try to give focus to the view that
-     * wants to clear its focus. From a client perspective, the view does not loose
-     * focus after the call, therefore no callback for focus change should be invoked.
-     *
-     * @throws Exception If an error occurs.
-     */
-    @UiThreadTest
-    public void testOnFocusChangeNotCalledIfFocusDoesNotMove() throws Exception {
-        // Get the first focusable.
-        Button button = mTopLeftButton;
-
-        // Make sure that the button is the first focusable and focus it.
-        button.getRootView().requestFocus(View.FOCUS_DOWN);
-        assertTrue(button.hasFocus());
-
-        // Attach on focus change listener that should not be called.
-        button.setOnFocusChangeListener(new OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                throw new IllegalStateException("Unexpeced call to"
-                        + "OnFocusChangeListener#onFocusChange");
-            }
-        });
-
-        // Attach on global focus change listener that should not be called.
-        button.getViewTreeObserver().addOnGlobalFocusChangeListener(
-                new OnGlobalFocusChangeListener() {
-            @Override
-            public void onGlobalFocusChanged(View oldFocus, View newFocus) {
-                throw new IllegalStateException("Unexpeced call to"
-                        + "OnFocusChangeListener#onFocusChange");
-            }
-        });
-
-        // Try to clear focus.
-        button.clearFocus();
-    }
-
-    /**
-     * This tests check whether the on focus change callbacks are invoked in
-     * the proper order when a View loses focus and the framework gives it to
-     * the fist focusable one.
-     *
-     * @throws Exception
-     */
-    @UiThreadTest
-    public void testOnFocusChangeCallbackOrder() throws Exception {
-        // Get the first focusable.
-        Button clearingFocusButton = mTopRightButton;
-        Button gainingFocusButton = mTopLeftButton;
-
-        // Make sure that the clearing focus is not the first focusable.
-        View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(null,
-                View.FOCUS_FORWARD);
-        assertNotSame("The clearing focus button is not the first focusable.",
-                clearingFocusButton, focusCandidate);
-        assertSame("The gaining focus button is the first focusable.",
-                gainingFocusButton, focusCandidate);
-
-        // Focus the clearing focus button.
-        clearingFocusButton.requestFocus();
-        assertTrue(clearingFocusButton.hasFocus());
-
-        // Register the invocation order checker.
-        CallbackOrderChecker checker = new CallbackOrderChecker(clearingFocusButton,
-                gainingFocusButton);
-        clearingFocusButton.setOnFocusChangeListener(checker);
-        gainingFocusButton.setOnFocusChangeListener(checker);
-        clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(checker);
-
-        // Try to clear focus.
-        clearingFocusButton.clearFocus();
-
-        // Check that no callback was invoked since focus did not move.
-        checker.verify();
-    }
-
-    /**
-     * This class check whether the focus change callback are invoked in order.
-     */
-    private class CallbackOrderChecker implements OnFocusChangeListener,
-            OnGlobalFocusChangeListener {
-
-        private class CallbackInvocation {
-            final String mMethodName;
-            final Object[] mArguments;
-
-            CallbackInvocation(String methodName, Object[] arguments) {
-                mMethodName = methodName;
-                mArguments = arguments;
-            }
-        }
-
-        private final View mClearingFocusView;
-        private final View mGainingFocusView;
-
-        private final List<CallbackInvocation> mInvocations = new ArrayList<CallbackInvocation>();
-
-        public CallbackOrderChecker(View clearingFocusView, View gainingFocusView) {
-            mClearingFocusView = clearingFocusView;
-            mGainingFocusView = gainingFocusView;
-        }
-
-        @Override
-        public void onFocusChange(View view, boolean hasFocus) {
-            CallbackInvocation invocation = new CallbackInvocation(
-                    "OnFocusChangeListener#onFocusChange", new Object[] {view, hasFocus});
-            mInvocations.add(invocation);
-        }
-
-        @Override
-        public void onGlobalFocusChanged(View oldFocus, View newFocus) {
-            CallbackInvocation invocation = new CallbackInvocation(
-                    "OnFocusChangeListener#onFocusChange", new Object[] {oldFocus, newFocus});
-            mInvocations.add(invocation);
-        }
-
-        public void verify() {
-            assertSame("All focus change callback should be invoked.", 3, mInvocations.size());
-            assertInvioked("Callback for View clearing focus explected.", 0,
-                    "OnFocusChangeListener#onFocusChange",
-                    new Object[] {mClearingFocusView, false});
-            assertInvioked("Callback for View global focus change explected.", 1,
-                    "OnFocusChangeListener#onFocusChange", new Object[] {mClearingFocusView,
-                    mGainingFocusView});
-            assertInvioked("Callback for View gaining focus explected.", 2,
-                    "OnFocusChangeListener#onFocusChange", new Object[] {mGainingFocusView, true});
-        }
-
-        private void assertInvioked(String message, int order, String methodName,
-                Object[] arguments) {
-            CallbackInvocation invocation = mInvocations.get(order);
-            assertEquals(message, methodName, invocation.mMethodName);
-            assertEquals(message, arguments[0], invocation.mArguments[0]);
-            assertEquals(message, arguments[1], invocation.mArguments[1]);
-        }
-    }
 }
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 88ee362..44401c8 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008-2012 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -131,7 +131,7 @@
     public static final int USAGE_GRAPHICS_RENDER_TARGET = 0x0010;
 
     /**
-     * USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT The allcation will be
+     * USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT The allocation will be
      * used with a SurfaceTexture object.  This usage will cause the
      * allocation to be created read only.
      *
@@ -140,18 +140,18 @@
     public static final int USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT_OPAQUE = 0x0020;
 
     /**
-     * USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT The allcation will be
+     * USAGE_IO_INPUT The allocation will be
      * used with a SurfaceTexture object.  This usage will cause the
      * allocation to be created read only.
      *
      * @hide
      */
-
     public static final int USAGE_IO_INPUT = 0x0040;
+
     /**
-     * USAGE_GRAPHICS_SURFACE_TEXTURE_INPUT The allcation will be
+     * USAGE_IO_OUTPUT The allocation will be
      * used with a SurfaceTexture object.  This usage will cause the
-     * allocation to be created read only.
+     * allocation to be created write only.
      *
      * @hide
      */
diff --git a/libs/rs/driver/rsdShaderCache.h b/libs/rs/driver/rsdShaderCache.h
index d64780b..0beecae 100644
--- a/libs/rs/driver/rsdShaderCache.h
+++ b/libs/rs/driver/rsdShaderCache.h
@@ -98,7 +98,8 @@
     struct ProgramEntry {
         ProgramEntry(uint32_t numVtxAttr, uint32_t numVtxUnis,
                      uint32_t numFragUnis) : vtx(0), frag(0), program(0), vtxAttrCount(0),
-                                             vtxAttrs(0), vtxUniforms(0), fragUniforms(0) {
+                                             vtxAttrs(0), vtxUniforms(0), fragUniforms(0),
+                                             fragUniformIsSTO(0) {
             vtxAttrCount = numVtxAttr;
             if (numVtxAttr) {
                 vtxAttrs = new AttrData[numVtxAttr];
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
index 2d76455..aa27861 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
@@ -58,6 +58,7 @@
             android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
             android:scaleType="centerInside"
             android:adjustViewBounds="true"
+            android:visibility="invisible"
         />
 
         <TextView android:id="@+id/app_label"
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
index b653fcd..bc389f9 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
@@ -81,6 +81,7 @@
             android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
             android:scaleType="centerInside"
             android:adjustViewBounds="true"
+            android:visibility="invisible"
         />
 
         <TextView android:id="@+id/app_description"
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
index cb26db00..333fcda 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
@@ -65,6 +65,7 @@
         android:maxHeight="@dimen/status_bar_recents_app_icon_max_height"
         android:scaleType="centerInside"
         android:adjustViewBounds="true"
+        android:visibility="invisible"
     />
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 14ce266..cd8bd4e 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -236,6 +236,10 @@
             public void onAnimationEnd(Animator animation) {
                 mCallback.onChildDismissed(view);
                 animView.setLayerType(View.LAYER_TYPE_NONE, null);
+                // Restore the alpha/translation parameters to what they were before swiping
+                // (for when these items are recycled)
+                animView.setAlpha(1f);
+                setTranslation(animView, 0f);
             }
         });
         anim.addUpdateListener(new AnimatorUpdateListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
index ad38a11..f8a4592 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
@@ -134,6 +134,7 @@
 
     void jumpTo(boolean appearing) {
         mContentView.setTranslationY(appearing ? 0 : mPanelHeight);
+        mScrimView.getBackground().setAlpha(appearing ? 255 : 0);
     }
 
     public void setPanelHeight(int h) {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 4145fc4..92f4ca9 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -16,12 +16,6 @@
 
 package com.android.systemui.recent;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -36,15 +30,17 @@
 import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Process;
-import android.os.SystemClock;
-import android.util.DisplayMetrics;
 import android.util.Log;
-import android.util.LruCache;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.tablet.TabletStatusBar;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
 public class RecentTasksLoader {
     static final String TAG = "RecentTasksLoader";
     static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
@@ -55,11 +51,14 @@
     private Context mContext;
     private RecentsPanelView mRecentsPanel;
 
-    private AsyncTask<Void, Integer, Void> mThumbnailLoader;
+    private AsyncTask<Void, ArrayList<TaskDescription>, Void> mTaskLoader;
+    private AsyncTask<Void, TaskDescription, Void> mThumbnailLoader;
     private final Handler mHandler;
 
     private int mIconDpi;
     private Bitmap mDefaultThumbnailBackground;
+    private Bitmap mDefaultIconBackground;
+    private int mNumTasksInFirstScreenful;
 
     public RecentTasksLoader(Context context) {
         mContext = context;
@@ -76,12 +75,20 @@
             mIconDpi = res.getDisplayMetrics().densityDpi;
         }
 
+        // Render default icon (just a blank image)
+        int defaultIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.app_icon_size);
+        int iconSize = (int) (defaultIconSize * mIconDpi / res.getDisplayMetrics().densityDpi);
+        mDefaultIconBackground = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
+
         // Render the default thumbnail background
-        int width = (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
-        int height = (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
+        int thumbnailWidth =
+                (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_width);
+        int thumbnailHeight =
+                (int) res.getDimensionPixelSize(com.android.internal.R.dimen.thumbnail_height);
         int color = res.getColor(R.drawable.status_bar_recents_app_thumbnail_background);
 
-        mDefaultThumbnailBackground = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        mDefaultThumbnailBackground =
+                Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ARGB_8888);
         Canvas c = new Canvas(mDefaultThumbnailBackground);
         c.drawColor(color);
 
@@ -95,12 +102,17 @@
 
     public void setRecentsPanel(RecentsPanelView recentsPanel) {
         mRecentsPanel = recentsPanel;
+        mNumTasksInFirstScreenful = mRecentsPanel.numItemsInOneScreenful();
     }
 
     public Bitmap getDefaultThumbnail() {
         return mDefaultThumbnailBackground;
     }
 
+    public Bitmap getDefaultIcon() {
+        return mDefaultIconBackground;
+    }
+
     // Create an TaskDescription, returning null if the title or icon is null, or if it's the
     // home activity
     TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent,
@@ -114,6 +126,12 @@
             homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
             .resolveActivityInfo(pm, 0);
         }
+        // Don't load the current home activity.
+        if (homeInfo != null
+            && homeInfo.packageName.equals(intent.getComponent().getPackageName())
+            && homeInfo.name.equals(intent.getComponent().getClassName())) {
+            return null;
+        }
 
         intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
                 | Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -121,9 +139,8 @@
         if (resolveInfo != null) {
             final ActivityInfo info = resolveInfo.activityInfo;
             final String title = info.loadLabel(pm).toString();
-            Drawable icon = getFullResIcon(resolveInfo, pm);
 
-            if (title != null && title.length() > 0 && icon != null) {
+            if (title != null && title.length() > 0) {
                 if (DEBUG) Log.v(TAG, "creating activity desc for id="
                         + persistentTaskId + ", label=" + title);
 
@@ -131,14 +148,6 @@
                         persistentTaskId, resolveInfo, baseIntent, info.packageName,
                         description);
                 item.setLabel(title);
-                item.setIcon(icon);
-
-                // Don't load the current home activity.
-                if (homeInfo != null
-                        && homeInfo.packageName.equals(intent.getComponent().getPackageName())
-                        && homeInfo.name.equals(intent.getComponent().getClassName())) {
-                    return null;
-                }
 
                 return item;
             } else {
@@ -148,10 +157,12 @@
         return null;
     }
 
-    void loadThumbnail(TaskDescription td) {
+    void loadThumbnailAndIcon(TaskDescription td) {
         final ActivityManager am = (ActivityManager)
                 mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        final PackageManager pm = mContext.getPackageManager();
         ActivityManager.TaskThumbnails thumbs = am.getTaskThumbnails(td.persistentTaskId);
+        Drawable icon = getFullResIcon(td.resolveInfo, pm);
 
         if (DEBUG) Log.v(TAG, "Loaded bitmap for task "
                 + td + ": " + thumbs.mainThumbnail);
@@ -161,6 +172,10 @@
             } else {
                 td.setThumbnail(mDefaultThumbnailBackground);
             }
+            if (icon != null) {
+                td.setIcon(icon);
+            }
+            td.setLoaded(true);
         }
     }
 
@@ -194,111 +209,149 @@
         return getFullResDefaultActivityIcon();
     }
 
-    public void cancelLoadingThumbnails() {
+    public void cancelLoadingThumbnailsAndIcons() {
+        if (mTaskLoader != null) {
+            mTaskLoader.cancel(false);
+            mTaskLoader = null;
+        }
         if (mThumbnailLoader != null) {
             mThumbnailLoader.cancel(false);
             mThumbnailLoader = null;
         }
     }
 
-    // return a snapshot of the current list of recent apps
-    ArrayList<TaskDescription> getRecentTasks() {
-        cancelLoadingThumbnails();
-
-        ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>();
-        final PackageManager pm = mContext.getPackageManager();
-        final ActivityManager am = (ActivityManager)
+    public void loadTasksInBackground() {
+        // cancel all previous loading of tasks and thumbnails
+        cancelLoadingThumbnailsAndIcons();
+        final LinkedBlockingQueue<TaskDescription> tasksWaitingForThumbnails =
+                new LinkedBlockingQueue<TaskDescription>();
+        final ArrayList<TaskDescription> taskDescriptionsWaitingToLoad =
+                new ArrayList<TaskDescription>();
+        mTaskLoader = new AsyncTask<Void, ArrayList<TaskDescription>, Void>() {
+            @Override
+            protected void onProgressUpdate(ArrayList<TaskDescription>... values) {
+                if (!isCancelled()) {
+                    ArrayList<TaskDescription> newTasks = values[0];
+                    // do a callback to RecentsPanelView to let it know we have more values
+                    // how do we let it know we're all done? just always call back twice
+                    mRecentsPanel.onTasksLoaded(newTasks);
+                }
+            }
+            @Override
+            protected Void doInBackground(Void... params) {
+                // We load in two stages: first, we update progress with just the first screenful
+                // of items. Then, we update with the rest of the items
+                final int origPri = Process.getThreadPriority(Process.myTid());
+                Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE);
+                final PackageManager pm = mContext.getPackageManager();
+                final ActivityManager am = (ActivityManager)
                 mContext.getSystemService(Context.ACTIVITY_SERVICE);
 
-        final List<ActivityManager.RecentTaskInfo> recentTasks =
-                am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+                final List<ActivityManager.RecentTaskInfo> recentTasks =
+                        am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
+                int numTasks = recentTasks.size();
+                ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN)
+                        .addCategory(Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);
 
-        ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
-                    .resolveActivityInfo(pm, 0);
+                boolean firstScreenful = true;
+                ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>();
 
-        HashSet<Integer> recentTasksToKeepInCache = new HashSet<Integer>();
-        int numTasks = recentTasks.size();
-
-        // skip the first task - assume it's either the home screen or the current activity.
-        final int first = 1;
-        recentTasksToKeepInCache.add(recentTasks.get(0).persistentId);
-        for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
-            final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
-
-            TaskDescription item = createTaskDescription(recentInfo.id,
-                    recentInfo.persistentId, recentInfo.baseIntent,
-                    recentInfo.origActivity, recentInfo.description, homeInfo);
-
-            if (item != null) {
-                tasks.add(item);
-                ++index;
-            }
-        }
-
-        // when we're not using the TaskDescription cache, we load the thumbnails in the
-        // background
-        loadThumbnailsInBackground(new ArrayList<TaskDescription>(tasks));
-        return tasks;
-    }
-
-    private void loadThumbnailsInBackground(final ArrayList<TaskDescription> descriptions) {
-        if (descriptions.size() > 0) {
-            if (DEBUG) Log.v(TAG, "Showing " + descriptions.size() + " tasks");
-            loadThumbnail(descriptions.get(0));
-            if (descriptions.size() > 1) {
-                mThumbnailLoader = new AsyncTask<Void, Integer, Void>() {
-                    @Override
-                    protected void onProgressUpdate(Integer... values) {
-                        final TaskDescription td = descriptions.get(values[0]);
-                        if (!isCancelled()) {
-                            mRecentsPanel.onTaskThumbnailLoaded(td);
-                        }
-                        // This is to prevent the loader thread from getting ahead
-                        // of our UI updates.
-                        mHandler.post(new Runnable() {
-                            @Override public void run() {
-                                synchronized (td) {
-                                    td.notifyAll();
-                                }
-                            }
-                        });
+                // skip the first task - assume it's either the home screen or the current activity.
+                final int first = 1;
+                for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
+                    if (isCancelled()) {
+                        break;
                     }
+                    final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
+                    TaskDescription item = createTaskDescription(recentInfo.id,
+                            recentInfo.persistentId, recentInfo.baseIntent,
+                            recentInfo.origActivity, recentInfo.description, homeInfo);
 
-                    @Override
-                    protected Void doInBackground(Void... params) {
-                        final int origPri = Process.getThreadPriority(Process.myTid());
-                        Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE);
-                        long nextTime = SystemClock.uptimeMillis();
-                        for (int i=1; i<descriptions.size(); i++) {
-                            TaskDescription td = descriptions.get(i);
-                            loadThumbnail(td);
-                            long now = SystemClock.uptimeMillis();
-                            nextTime += 0;
-                            if (nextTime > now) {
-                                try {
-                                    Thread.sleep(nextTime-now);
-                                } catch (InterruptedException e) {
-                                }
-                            }
-
-                            if (isCancelled()) {
+                    if (item != null) {
+                        while (true) {
+                            try {
+                                tasksWaitingForThumbnails.put(item);
                                 break;
-                            }
-                            synchronized (td) {
-                                publishProgress(i);
-                                try {
-                                    td.wait(500);
-                                } catch (InterruptedException e) {
-                                }
+                            } catch (InterruptedException e) {
                             }
                         }
-                        Process.setThreadPriority(origPri);
-                        return null;
+                        tasks.add(item);
+                        if (firstScreenful && tasks.size() == mNumTasksInFirstScreenful) {
+                            publishProgress(tasks);
+                            tasks = new ArrayList<TaskDescription>();
+                            firstScreenful = false;
+                            //break;
+                        }
+                        ++index;
                     }
-                };
-                mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+                }
+
+                if (!isCancelled()) {
+                    publishProgress(tasks);
+                    if (firstScreenful) {
+                        // always should publish two updates
+                        publishProgress(new ArrayList<TaskDescription>());
+                    }
+                }
+
+                while (true) {
+                    try {
+                        tasksWaitingForThumbnails.put(new TaskDescription());
+                        break;
+                    } catch (InterruptedException e) {
+                    }
+                }
+
+                Process.setThreadPriority(origPri);
+                return null;
             }
-        }
+        };
+        mTaskLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        loadThumbnailsAndIconsInBackground(tasksWaitingForThumbnails);
     }
 
+    private void loadThumbnailsAndIconsInBackground(
+            final BlockingQueue<TaskDescription> tasksWaitingForThumbnails) {
+        // continually read items from tasksWaitingForThumbnails and load
+        // thumbnails and icons for them. finish thread when cancelled or there
+        // is a null item in tasksWaitingForThumbnails
+        mThumbnailLoader = new AsyncTask<Void, TaskDescription, Void>() {
+            @Override
+            protected void onProgressUpdate(TaskDescription... values) {
+                if (!isCancelled()) {
+                    TaskDescription td = values[0];
+                    mRecentsPanel.onTaskThumbnailLoaded(td);
+                }
+            }
+            @Override
+            protected Void doInBackground(Void... params) {
+                final int origPri = Process.getThreadPriority(Process.myTid());
+                Process.setThreadPriority(Process.THREAD_GROUP_BG_NONINTERACTIVE);
+
+                while (true) {
+                    if (isCancelled()) {
+                        break;
+                    }
+                    TaskDescription td = null;
+                    while (td == null) {
+                        try {
+                            td = tasksWaitingForThumbnails.take();
+                        } catch (InterruptedException e) {
+                        }
+                    }
+                    if (td.isNull()) {
+                        break;
+                    }
+                    loadThumbnailAndIcon(td);
+                    synchronized(td) {
+                        publishProgress(td);
+                    }
+                }
+
+                Process.setThreadPriority(origPri);
+                return null;
+            }
+        };
+        mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index f971d2d..4718a17 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -22,11 +22,18 @@
 import android.database.DataSetObserver;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.FloatMath;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
+import android.view.View.MeasureSpec;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
 import android.view.View.OnTouchListener;
+import android.view.ViewConfiguration;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.widget.HorizontalScrollView;
 import android.widget.LinearLayout;
 
@@ -34,6 +41,8 @@
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.recent.RecentsPanelView.TaskDescriptionAdapter;
 
+import java.util.ArrayList;
+
 public class RecentsHorizontalScrollView extends HorizontalScrollView
     implements SwipeHelper.Callback {
     private static final String TAG = RecentsPanelView.TAG;
@@ -44,6 +53,8 @@
     protected int mLastScrollPosition;
     private SwipeHelper mSwipeHelper;
     private RecentsScrollViewPerformanceHelper mPerformanceHelper;
+    private ArrayList<View> mRecycledViews;
+    private int mNumItemsInOneScreenful;
 
     public RecentsHorizontalScrollView(Context context, AttributeSet attrs) {
         super(context, attrs, 0);
@@ -51,6 +62,7 @@
         float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
         mSwipeHelper = new SwipeHelper(SwipeHelper.Y, this, densityScale, pagingTouchSlop);
         mPerformanceHelper = RecentsScrollViewPerformanceHelper.create(context, attrs, this, false);
+        mRecycledViews = new ArrayList<View>();
     }
 
     private int scrollPositionOfMostRecent() {
@@ -58,9 +70,23 @@
     }
 
     private void update() {
+        for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
+            View v = mLinearLayout.getChildAt(i);
+            mRecycledViews.add(v);
+            mAdapter.recycleView(v);
+        }
+        LayoutTransition transitioner = getLayoutTransition();
+        setLayoutTransition(null);
+
         mLinearLayout.removeAllViews();
         for (int i = 0; i < mAdapter.getCount(); i++) {
-            final View view = mAdapter.getView(i, null, mLinearLayout);
+            View old = null;
+            if (mRecycledViews.size() != 0) {
+                old = mRecycledViews.remove(mRecycledViews.size() - 1);
+                old.setVisibility(VISIBLE);
+            }
+
+            final View view = mAdapter.getView(i, old, mLinearLayout);
 
             if (mPerformanceHelper != null) {
                 mPerformanceHelper.addViewCallback(view);
@@ -87,7 +113,8 @@
                 }
             };
 
-            final View thumbnailView = view.findViewById(R.id.app_thumbnail);
+            RecentsPanelView.ViewHolder holder = (RecentsPanelView.ViewHolder) view.getTag();
+            final View thumbnailView = holder.thumbnailView;
             OnLongClickListener longClickListener = new OnLongClickListener() {
                 public boolean onLongClick(View v) {
                     final View anchorView = view.findViewById(R.id.app_description);
@@ -107,13 +134,21 @@
             appTitle.setOnTouchListener(noOpListener);
             mLinearLayout.addView(view);
         }
+        setLayoutTransition(transitioner);
+
         // Scroll to end after layout.
-        post(new Runnable() {
-            public void run() {
-                mLastScrollPosition = scrollPositionOfMostRecent();
-                scrollTo(mLastScrollPosition, 0);
-            }
-        });
+        final ViewTreeObserver observer = getViewTreeObserver();
+
+        final OnGlobalLayoutListener updateScroll = new OnGlobalLayoutListener() {
+                public void onGlobalLayout() {
+                    mLastScrollPosition = scrollPositionOfMostRecent();
+                    scrollTo(mLastScrollPosition, 0);
+                    if (observer.isAlive()) {
+                        observer.removeOnGlobalLayoutListener(this);
+                    }
+                }
+            };
+        observer.addOnGlobalLayoutListener(updateScroll);
     }
 
     @Override
@@ -142,8 +177,10 @@
     }
 
     public void onChildDismissed(View v) {
+        mRecycledViews.add(v);
         mLinearLayout.removeView(v);
         mCallback.handleSwipe(v);
+        v.setActivated(false);
     }
 
     public void onBeginDrag(View v) {
@@ -315,6 +352,24 @@
                 update();
             }
         });
+        DisplayMetrics dm = getResources().getDisplayMetrics();
+        int childWidthMeasureSpec =
+                MeasureSpec.makeMeasureSpec(dm.widthPixels, MeasureSpec.AT_MOST);
+        int childheightMeasureSpec =
+                MeasureSpec.makeMeasureSpec(dm.heightPixels, MeasureSpec.AT_MOST);
+        View child = mAdapter.createView(mLinearLayout);
+        child.measure(childWidthMeasureSpec, childheightMeasureSpec);
+        mNumItemsInOneScreenful =
+                (int) FloatMath.ceil(dm.widthPixels / (float) child.getMeasuredWidth());
+        mRecycledViews.add(child);
+
+        for (int i = 0; i < mNumItemsInOneScreenful - 1; i++) {
+            mRecycledViews.add(mAdapter.createView(mLinearLayout));
+        }
+    }
+
+    public int numItemsInOneScreenful() {
+        return mNumItemsInOneScreenful;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index a10e363..8706f10 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -27,6 +27,7 @@
 import android.graphics.Matrix;
 import android.graphics.Shader.TileMode;
 import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.provider.Settings;
 import android.util.AttributeSet;
@@ -36,18 +37,18 @@
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
+import android.view.ViewRootImpl;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
 import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
 import android.widget.PopupMenu;
-import android.widget.RelativeLayout;
 import android.widget.ScrollView;
 import android.widget.TextView;
 
@@ -59,7 +60,7 @@
 
 import java.util.ArrayList;
 
-public class RecentsPanelView extends RelativeLayout implements OnItemClickListener, RecentsCallback,
+public class RecentsPanelView extends FrameLayout implements OnItemClickListener, RecentsCallback,
         StatusBarPanel, Animator.AnimatorListener, View.OnTouchListener {
     static final String TAG = "RecentsPanelView";
     static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
@@ -71,6 +72,10 @@
     private StatusBarTouchProxy mStatusBarTouchProxy;
 
     private boolean mShowing;
+    private boolean mWaitingToShow;
+    private boolean mWaitingToShowAnimated;
+    private boolean mReadyToShow;
+    private int mNumItemsWaitingForThumbnailsAndIcons;
     private Choreographer mChoreo;
     OnRecentsPanelVisibilityChangedListener mVisibilityChangedListener;
 
@@ -104,6 +109,7 @@
         TextView labelView;
         TextView descriptionView;
         TaskDescription taskDescription;
+        boolean loadedThumbnailAndIcon;
     }
 
     /* package */ final class TaskDescriptionAdapter extends BaseAdapter {
@@ -125,42 +131,82 @@
             return position; // we just need something unique for this position
         }
 
-        public View getView(int position, View convertView, ViewGroup parent) {
-            ViewHolder holder;
-            if (convertView == null) {
-                convertView = mInflater.inflate(R.layout.status_bar_recent_item, parent, false);
-                holder = new ViewHolder();
-                holder.thumbnailView = convertView.findViewById(R.id.app_thumbnail);
-                holder.thumbnailViewImage = (ImageView) convertView.findViewById(
-                        R.id.app_thumbnail_image);
-                // If we set the default thumbnail now, we avoid an onLayout when we update
-                // the thumbnail later (if they both have the same dimensions)
+        public View createView(ViewGroup parent) {
+            View convertView = mInflater.inflate(R.layout.status_bar_recent_item, parent, false);
+            ViewHolder holder = new ViewHolder();
+            holder.thumbnailView = convertView.findViewById(R.id.app_thumbnail);
+            holder.thumbnailViewImage =
+                    (ImageView) convertView.findViewById(R.id.app_thumbnail_image);
+            // If we set the default thumbnail now, we avoid an onLayout when we update
+            // the thumbnail later (if they both have the same dimensions)
+            if (mRecentTasksLoader != null) {
                 updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false);
-
-                holder.iconView = (ImageView) convertView.findViewById(R.id.app_icon);
-                holder.labelView = (TextView) convertView.findViewById(R.id.app_label);
-                holder.descriptionView = (TextView) convertView.findViewById(R.id.app_description);
-
-                convertView.setTag(holder);
-            } else {
-                holder = (ViewHolder) convertView.getTag();
             }
+            holder.iconView = (ImageView) convertView.findViewById(R.id.app_icon);
+            if (mRecentTasksLoader != null) {
+                holder.iconView.setImageBitmap(mRecentTasksLoader.getDefaultIcon());
+            }
+            holder.labelView = (TextView) convertView.findViewById(R.id.app_label);
+            holder.descriptionView = (TextView) convertView.findViewById(R.id.app_description);
+
+            convertView.setTag(holder);
+            return convertView;
+        }
+
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = createView(parent);
+            }
+            ViewHolder holder = (ViewHolder) convertView.getTag();
 
             // index is reverse since most recent appears at the bottom...
             final int index = mRecentTaskDescriptions.size() - position - 1;
 
             final TaskDescription td = mRecentTaskDescriptions.get(index);
-            holder.iconView.setImageDrawable(td.getIcon());
+
             holder.labelView.setText(td.getLabel());
             holder.thumbnailView.setContentDescription(td.getLabel());
-            updateThumbnail(holder, td.getThumbnail(), true, false);
+            holder.loadedThumbnailAndIcon = td.isLoaded();
+            if (td.isLoaded()) {
+                updateThumbnail(holder, td.getThumbnail(), true, false);
+                updateIcon(holder, td.getIcon(), true, false);
+                mNumItemsWaitingForThumbnailsAndIcons--;
+            }
 
             holder.thumbnailView.setTag(td);
             holder.thumbnailView.setOnLongClickListener(new OnLongClickDelegate(convertView));
             holder.taskDescription = td;
-
             return convertView;
         }
+
+        public void recycleView(View v) {
+            ViewHolder holder = (ViewHolder) v.getTag();
+            updateThumbnail(holder, mRecentTasksLoader.getDefaultThumbnail(), false, false);
+            holder.iconView.setImageBitmap(mRecentTasksLoader.getDefaultIcon());
+            holder.iconView.setVisibility(INVISIBLE);
+            holder.labelView.setText(null);
+            holder.thumbnailView.setContentDescription(null);
+            holder.thumbnailView.setTag(null);
+            holder.thumbnailView.setOnLongClickListener(null);
+            holder.thumbnailView.setVisibility(INVISIBLE);
+            holder.taskDescription = null;
+            holder.loadedThumbnailAndIcon = false;
+        }
+    }
+
+    public int numItemsInOneScreenful() {
+        if (mRecentsContainer instanceof RecentsHorizontalScrollView){
+            RecentsHorizontalScrollView scrollView
+                    = (RecentsHorizontalScrollView) mRecentsContainer;
+            return scrollView.numItemsInOneScreenful();
+        } else if (mRecentsContainer instanceof RecentsVerticalScrollView){
+            RecentsVerticalScrollView scrollView
+                    = (RecentsVerticalScrollView) mRecentsContainer;
+            return scrollView.numItemsInOneScreenful();
+        }
+        else {
+            throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
+        }
     }
 
     @Override
@@ -192,15 +238,31 @@
     }
 
     public void show(boolean show, boolean animate) {
-        show(show, animate, null);
+        if (show) {
+            mWaitingToShow = true;
+            mWaitingToShowAnimated = animate;
+            showIfReady();
+        } else {
+            show(show, animate, null, false);
+        }
+    }
+
+    private void showIfReady() {
+        // mWaitingToShow = there was a touch up on the recents button
+        // mReadyToShow = we've created views for the first screenful of items
+        if (mWaitingToShow && mReadyToShow) { // && mNumItemsWaitingForThumbnailsAndIcons <= 0
+            show(true, mWaitingToShowAnimated, null, false);
+        }
     }
 
     public void show(boolean show, boolean animate,
-            ArrayList<TaskDescription> recentTaskDescriptions) {
+            ArrayList<TaskDescription> recentTaskDescriptions, boolean firstScreenful) {
+        // For now, disable animations. We may want to re-enable in the future
+        animate = false;
         if (show) {
             // Need to update list of recent apps before we set visibility so this view's
             // content description is updated before it gets focus for TalkBack mode
-            refreshRecentTasksList(recentTaskDescriptions);
+            refreshRecentTasksList(recentTaskDescriptions, firstScreenful);
 
             // if there are no apps, either bring up a "No recent apps" message, or just
             // quit early
@@ -209,19 +271,24 @@
                 mRecentsNoApps.setVisibility(noApps ? View.VISIBLE : View.INVISIBLE);
             } else {
                 if (noApps) {
-                    if (DEBUG) Log.v(TAG, "Nothing to show");
+                   if (DEBUG) Log.v(TAG, "Nothing to show");
                     // Need to set recent tasks to dirty so that next time we load, we
                     // refresh the list of tasks
-                    mRecentTasksLoader.cancelLoadingThumbnails();
+                    mRecentTasksLoader.cancelLoadingThumbnailsAndIcons();
                     mRecentTasksDirty = true;
+
+                    mWaitingToShow = false;
+                    mReadyToShow = false;
                     return;
                 }
             }
         } else {
             // Need to set recent tasks to dirty so that next time we load, we
             // refresh the list of tasks
-            mRecentTasksLoader.cancelLoadingThumbnails();
+            mRecentTasksLoader.cancelLoadingThumbnailsAndIcons();
             mRecentTasksDirty = true;
+            mWaitingToShow = false;
+            mReadyToShow = false;
         }
         if (animate) {
             if (mShowing != show) {
@@ -385,7 +452,6 @@
             throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
         }
 
-
         mRecentsScrim = findViewById(R.id.recents_bg_protect);
         mRecentsNoApps = findViewById(R.id.recents_no_apps);
         mChoreo = new Choreographer(this, mRecentsScrim, mRecentsContainer, mRecentsNoApps, this);
@@ -425,6 +491,20 @@
         }
     }
 
+
+    private void updateIcon(ViewHolder h, Drawable icon, boolean show, boolean anim) {
+        if (icon != null) {
+            h.iconView.setImageDrawable(icon);
+            if (show && h.iconView.getVisibility() != View.VISIBLE) {
+                if (anim) {
+                    h.iconView.setAnimation(
+                            AnimationUtils.loadAnimation(mContext, R.anim.recent_appear));
+                }
+                h.iconView.setVisibility(View.VISIBLE);
+            }
+        }
+    }
+
     private void updateThumbnail(ViewHolder h, Bitmap thumbnail, boolean show, boolean anim) {
         if (thumbnail != null) {
             // Should remove the default image in the frame
@@ -458,31 +538,36 @@
         }
     }
 
-    void onTaskThumbnailLoaded(TaskDescription ad) {
-        synchronized (ad) {
+    void onTaskThumbnailLoaded(TaskDescription td) {
+        synchronized (td) {
             if (mRecentsContainer != null) {
                 ViewGroup container = mRecentsContainer;
                 if (container instanceof HorizontalScrollView
                         || container instanceof ScrollView) {
-                    container = (ViewGroup)container.findViewById(
+                    container = (ViewGroup) container.findViewById(
                             R.id.recents_linear_layout);
                 }
                 // Look for a view showing this thumbnail, to update.
-                for (int i=0; i<container.getChildCount(); i++) {
+                for (int i=0; i < container.getChildCount(); i++) {
                     View v = container.getChildAt(i);
                     if (v.getTag() instanceof ViewHolder) {
                         ViewHolder h = (ViewHolder)v.getTag();
-                        if (h.taskDescription == ad) {
+                        if (!h.loadedThumbnailAndIcon && h.taskDescription == td) {
                             // only fade in the thumbnail if recents is already visible-- we
                             // show it immediately otherwise
-                            boolean animateShow = mShowing &&
-                                mRecentsContainer.getAlpha() > ViewConfiguration.ALPHA_THRESHOLD;
-                            updateThumbnail(h, ad.getThumbnail(), true, animateShow);
+                            //boolean animateShow = mShowing &&
+                            //    mRecentsContainer.getAlpha() > ViewConfiguration.ALPHA_THRESHOLD;
+                            boolean animateShow = false;
+                            updateIcon(h, td.getIcon(), true, animateShow);
+                            updateThumbnail(h, td.getThumbnail(), true, animateShow);
+                            h.loadedThumbnailAndIcon = true;
+                            mNumItemsWaitingForThumbnailsAndIcons--;
                         }
                     }
                 }
             }
-        }
+            }
+        showIfReady();
     }
 
     // additional optimization when we have sofware system buttons - start loading the recent
@@ -516,7 +601,7 @@
     public void clearRecentTasksList() {
         // Clear memory used by screenshots
         if (mRecentTaskDescriptions != null) {
-            mRecentTasksLoader.cancelLoadingThumbnails();
+            mRecentTasksLoader.cancelLoadingThumbnailsAndIcons();
             mRecentTaskDescriptions.clear();
             mListAdapter.notifyDataSetInvalidated();
             mRecentTasksDirty = true;
@@ -524,26 +609,50 @@
     }
 
     public void refreshRecentTasksList() {
-        refreshRecentTasksList(null);
+        refreshRecentTasksList(null, false);
     }
 
-    private void refreshRecentTasksList(ArrayList<TaskDescription> recentTasksList) {
+    private void refreshRecentTasksList(
+            ArrayList<TaskDescription> recentTasksList, boolean firstScreenful) {
         if (mRecentTasksDirty) {
             if (recentTasksList != null) {
-                mRecentTaskDescriptions = recentTasksList;
+                mFirstScreenful = true;
+                onTasksLoaded(recentTasksList);
             } else {
-                mRecentTaskDescriptions = mRecentTasksLoader.getRecentTasks();
+                mFirstScreenful = true;
+                mRecentTasksLoader.loadTasksInBackground();
             }
-            mListAdapter.notifyDataSetInvalidated();
-            updateUiElements(getResources().getConfiguration());
             mRecentTasksDirty = false;
         }
     }
 
+    boolean mFirstScreenful;
+    public void onTasksLoaded(ArrayList<TaskDescription> tasks) {
+        if (!mFirstScreenful && tasks.size() == 0) {
+            return;
+        }
+        mNumItemsWaitingForThumbnailsAndIcons =
+                mFirstScreenful ? tasks.size() : mRecentTaskDescriptions.size();
+        if (mRecentTaskDescriptions == null) {
+            mRecentTaskDescriptions = new ArrayList(tasks);
+        } else {
+            mRecentTaskDescriptions.addAll(tasks);
+        }
+        mListAdapter.notifyDataSetInvalidated();
+        updateUiElements(getResources().getConfiguration());
+        mReadyToShow = true;
+        mFirstScreenful = false;
+        showIfReady();
+    }
+
     public ArrayList<TaskDescription> getRecentTasksList() {
         return mRecentTaskDescriptions;
     }
 
+    public boolean getFirstScreenful() {
+        return mFirstScreenful;
+    }
+
     private void updateUiElements(Configuration config) {
         final int items = mRecentTaskDescriptions.size();
 
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index dc13092..0605c4c 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -22,10 +22,18 @@
 import android.database.DataSetObserver;
 import android.graphics.Canvas;
 import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.FloatMath;
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.view.View.OnTouchListener;
 import android.view.ViewConfiguration;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 
@@ -33,6 +41,8 @@
 import com.android.systemui.SwipeHelper;
 import com.android.systemui.recent.RecentsPanelView.TaskDescriptionAdapter;
 
+import java.util.ArrayList;
+
 public class RecentsVerticalScrollView extends ScrollView implements SwipeHelper.Callback {
     private static final String TAG = RecentsPanelView.TAG;
     private static final boolean DEBUG = RecentsPanelView.DEBUG;
@@ -42,6 +52,8 @@
     protected int mLastScrollPosition;
     private SwipeHelper mSwipeHelper;
     private RecentsScrollViewPerformanceHelper mPerformanceHelper;
+    private ArrayList<View> mRecycledViews;
+    private int mNumItemsInOneScreenful;
 
     public RecentsVerticalScrollView(Context context, AttributeSet attrs) {
         super(context, attrs, 0);
@@ -50,6 +62,7 @@
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
 
         mPerformanceHelper = RecentsScrollViewPerformanceHelper.create(context, attrs, this, true);
+        mRecycledViews = new ArrayList<View>();
     }
 
     private int scrollPositionOfMostRecent() {
@@ -57,77 +70,91 @@
     }
 
     private void update() {
+        for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
+            View v = mLinearLayout.getChildAt(i);
+            mRecycledViews.add(v);
+            mAdapter.recycleView(v);
+        }
+        LayoutTransition transitioner = getLayoutTransition();
+        setLayoutTransition(null);
+
         mLinearLayout.removeAllViews();
         // Once we can clear the data associated with individual item views,
         // we can get rid of the removeAllViews() and the code below will
         // recycle them.
         for (int i = 0; i < mAdapter.getCount(); i++) {
             View old = null;
-            if (i < mLinearLayout.getChildCount()) {
-                old = mLinearLayout.getChildAt(i);
-                old.setVisibility(View.VISIBLE);
+            if (mRecycledViews.size() != 0) {
+                old = mRecycledViews.remove(mRecycledViews.size() - 1);
+                old.setVisibility(VISIBLE);
             }
+
             final View view = mAdapter.getView(i, old, mLinearLayout);
 
             if (mPerformanceHelper != null) {
                 mPerformanceHelper.addViewCallback(view);
             }
 
-            if (old == null) {
-                OnTouchListener noOpListener = new OnTouchListener() {
-                    @Override
-                    public boolean onTouch(View v, MotionEvent event) {
-                        return true;
-                    }
-                };
+            OnTouchListener noOpListener = new OnTouchListener() {
+                @Override
+                public boolean onTouch(View v, MotionEvent event) {
+                    return true;
+                }
+            };
 
-                view.setOnClickListener(new OnClickListener() {
-                    public void onClick(View v) {
-                        mCallback.dismiss();
-                    }
-                });
-                // We don't want a click sound when we dimiss recents
-                view.setSoundEffectsEnabled(false);
+            view.setOnClickListener(new OnClickListener() {
+                public void onClick(View v) {
+                    mCallback.dismiss();
+                }
+            });
+            // We don't want a click sound when we dimiss recents
+            view.setSoundEffectsEnabled(false);
 
-                OnClickListener launchAppListener = new OnClickListener() {
-                    public void onClick(View v) {
-                        mCallback.handleOnClick(view);
-                    }
-                };
+            OnClickListener launchAppListener = new OnClickListener() {
+                public void onClick(View v) {
+                    mCallback.handleOnClick(view);
+                }
+            };
 
-                final View thumbnailView = view.findViewById(R.id.app_thumbnail);
-                OnLongClickListener longClickListener = new OnLongClickListener() {
-                    public boolean onLongClick(View v) {
-                        final View anchorView = view.findViewById(R.id.app_description);
-                        mCallback.handleLongPress(view, anchorView, thumbnailView);
-                        return true;
-                    }
-                };
-                thumbnailView.setClickable(true);
-                thumbnailView.setOnClickListener(launchAppListener);
-                thumbnailView.setOnLongClickListener(longClickListener);
+            RecentsPanelView.ViewHolder holder = (RecentsPanelView.ViewHolder) view.getTag();
+            final View thumbnailView = holder.thumbnailView;
+            OnLongClickListener longClickListener = new OnLongClickListener() {
+                public boolean onLongClick(View v) {
+                    final View anchorView = view.findViewById(R.id.app_description);
+                    mCallback.handleLongPress(view, anchorView, thumbnailView);
+                    return true;
+                }
+            };
+            thumbnailView.setClickable(true);
+            thumbnailView.setOnClickListener(launchAppListener);
+            thumbnailView.setOnLongClickListener(longClickListener);
 
-                // We don't want to dismiss recents if a user clicks on the app title
-                // (we also don't want to launch the app either, though, because the
-                // app title is a small target and doesn't have great click feedback)
-                final View appTitle = view.findViewById(R.id.app_label);
-                appTitle.setContentDescription(" ");
-                appTitle.setOnTouchListener(noOpListener);
-                final View calloutLine = view.findViewById(R.id.recents_callout_line);
-                calloutLine.setOnTouchListener(noOpListener);
-                mLinearLayout.addView(view);
-            }
+            // We don't want to dismiss recents if a user clicks on the app title
+            // (we also don't want to launch the app either, though, because the
+            // app title is a small target and doesn't have great click feedback)
+            final View appTitle = view.findViewById(R.id.app_label);
+            appTitle.setContentDescription(" ");
+            appTitle.setOnTouchListener(noOpListener);
+            final View calloutLine = view.findViewById(R.id.recents_callout_line);
+            calloutLine.setOnTouchListener(noOpListener);
+
+            mLinearLayout.addView(view);
         }
-        for (int i = mAdapter.getCount(); i < mLinearLayout.getChildCount(); i++) {
-            mLinearLayout.getChildAt(i).setVisibility(View.GONE);
-        }
+        setLayoutTransition(transitioner);
+
         // Scroll to end after layout.
-        post(new Runnable() {
-            public void run() {
-                mLastScrollPosition = scrollPositionOfMostRecent();
-                scrollTo(0, mLastScrollPosition);
-            }
-        });
+        final ViewTreeObserver observer = getViewTreeObserver();
+
+        final OnGlobalLayoutListener updateScroll = new OnGlobalLayoutListener() {
+                public void onGlobalLayout() {
+                    mLastScrollPosition = scrollPositionOfMostRecent();
+                    scrollTo(0, mLastScrollPosition);
+                    if (observer.isAlive()) {
+                        observer.removeOnGlobalLayoutListener(this);
+                    }
+                }
+            };
+        observer.addOnGlobalLayoutListener(updateScroll);
     }
 
     @Override
@@ -156,8 +183,10 @@
     }
 
     public void onChildDismissed(View v) {
+        mRecycledViews.add(v);
         mLinearLayout.removeView(v);
         mCallback.handleSwipe(v);
+        v.setActivated(false);
     }
 
     public void onBeginDrag(View v) {
@@ -330,6 +359,25 @@
                 update();
             }
         });
+
+        DisplayMetrics dm = getResources().getDisplayMetrics();
+        int childWidthMeasureSpec =
+                MeasureSpec.makeMeasureSpec(dm.widthPixels, MeasureSpec.AT_MOST);
+        int childheightMeasureSpec =
+                MeasureSpec.makeMeasureSpec(dm.heightPixels, MeasureSpec.AT_MOST);
+        View child = mAdapter.createView(mLinearLayout);
+        child.measure(childWidthMeasureSpec, childheightMeasureSpec);
+        mNumItemsInOneScreenful =
+                (int) FloatMath.ceil(dm.heightPixels / (float) child.getMeasuredHeight());
+        mRecycledViews.add(child);
+
+        for (int i = 0; i < mNumItemsInOneScreenful - 1; i++) {
+            mRecycledViews.add(mAdapter.createView(mLinearLayout));
+        }
+    }
+
+    public int numItemsInOneScreenful() {
+        return mNumItemsInOneScreenful;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
index dcfd6d8..7e979b7 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/TaskDescription.java
@@ -32,6 +32,7 @@
     private Bitmap mThumbnail; // generated by Activity.onCreateThumbnail()
     private Drawable mIcon; // application package icon
     private CharSequence mLabel; // application package label
+    private boolean mLoaded;
 
     public TaskDescription(int _taskId, int _persistentTaskId,
             ResolveInfo _resolveInfo, Intent _intent,
@@ -45,6 +46,28 @@
         packageName = _packageName;
     }
 
+    public TaskDescription() {
+        resolveInfo = null;
+        intent = null;
+        taskId = -1;
+        persistentTaskId = -1;
+
+        description = null;
+        packageName = null;
+    }
+
+    public void setLoaded(boolean loaded) {
+        mLoaded = loaded;
+    }
+
+    public boolean isLoaded() {
+        return mLoaded;
+    }
+
+    public boolean isNull() {
+        return resolveInfo == null;
+    }
+
     // mark all these as locked?
     public CharSequence getLabel() {
         return mLabel;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 5a1e3f4..b3cef90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -393,7 +393,7 @@
         }
         lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
         lp.setTitle("RecentsPanel");
-        lp.windowAnimations = R.style.Animation_RecentPanel;
+        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
         lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
         | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
         return lp;
@@ -403,11 +403,13 @@
         // Recents Panel
         boolean visible = false;
         ArrayList<TaskDescription> recentTasksList = null;
+        boolean firstScreenful = false;
         if (mRecentsPanel != null) {
             visible = mRecentsPanel.isShowing();
             WindowManagerImpl.getDefault().removeView(mRecentsPanel);
             if (visible) {
                 recentTasksList = mRecentsPanel.getRecentTasksList();
+                firstScreenful = mRecentsPanel.getFirstScreenful();
             }
         }
 
@@ -425,7 +427,7 @@
         WindowManagerImpl.getDefault().addView(mRecentsPanel, lp);
         mRecentsPanel.setBar(this);
         if (visible) {
-            mRecentsPanel.show(true, false, recentTasksList);
+            mRecentsPanel.show(true, false, recentTasksList, firstScreenful);
         }
 
     }
diff --git a/tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java b/tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java
index 5f53a9b..1a2dcb9 100644
--- a/tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java
+++ b/tests/SmokeTest/tests/src/com/android/smoketest/ProcessErrorsTest.java
@@ -19,12 +19,21 @@
 import com.android.internal.os.RuntimeInit;
 
 import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessErrorStateInfo;
 import android.content.Context;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 /**
  * This smoke test is designed to quickly sniff for any error conditions
@@ -32,53 +41,125 @@
  */
 public class ProcessErrorsTest extends AndroidTestCase {
     
-    private final String TAG = "ProcessErrorsTest";
+    private static final String TAG = "ProcessErrorsTest";
     
     protected ActivityManager mActivityManager;
+    protected PackageManager mPackageManager;
 
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        mActivityManager = (ActivityManager) 
+        mActivityManager = (ActivityManager)
                 getContext().getSystemService(Context.ACTIVITY_SERVICE);
+        mPackageManager = getContext().getPackageManager();
     }
 
     public void testSetUpConditions() throws Exception {
         assertNotNull(mActivityManager);
+        assertNotNull(mPackageManager);
     }
 
     public void testNoProcessErrors() throws Exception {
-        List<ActivityManager.ProcessErrorStateInfo> errList;        
+        final String reportMsg = checkForProcessErrors();
+        if (reportMsg != null) {
+            Log.w(TAG, reportMsg);
+        }
+
+        // report a non-empty list back to the test framework
+        assertNull(reportMsg, reportMsg);
+    }
+
+    private String checkForProcessErrors() throws Exception {
+        List<ProcessErrorStateInfo> errList;
         errList = mActivityManager.getProcessesInErrorState();
         
         // note: this contains information about each process that is currently in an error
         // condition.  if the list is empty (null) then "we're good".  
         
         // if the list is non-empty, then it's useful to report the contents of the list
-        // we'll put a copy in the log, and we'll report it back to the framework via the assert.
         final String reportMsg = reportListContents(errList);
-        if (reportMsg != null) {
-            Log.w(TAG, reportMsg);
-        }
-        
-        // report a non-empty list back to the test framework
-        assertNull(reportMsg, errList);
+        return reportMsg;
     }
-    
+
+    /**
+     * A test that runs all Launcher-launchable activities and verifies that no ANRs or crashes
+     * happened while doing so.
+     * <p />
+     * FIXME: Doesn't detect multiple crashing apps properly, since the crash dialog for the
+     * FIXME: first app doesn't go away.
+     */
+    public void testRunAllActivities() throws Exception {
+        final Intent home = new Intent(Intent.ACTION_MAIN);
+        home.addCategory(Intent.CATEGORY_HOME);
+        home.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        final Intent launchable = new Intent(Intent.ACTION_MAIN);
+        launchable.addCategory(Intent.CATEGORY_LAUNCHER);
+        final List<ResolveInfo> activities = mPackageManager.queryIntentActivities(launchable, 0);
+        final Set<ProcessError> errSet = new HashSet<ProcessError>();
+
+        for (ResolveInfo info : activities) {
+            Log.i(TAG, String.format("Got %s/%s", info.activityInfo.packageName,
+                    info.activityInfo.name));
+
+            // build an Intent to launch the app
+            final ComponentName component = new ComponentName(info.activityInfo.packageName,
+                    info.activityInfo.name);
+            final Intent intent = new Intent(Intent.ACTION_MAIN);
+            intent.setComponent(component);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            // launch app, and wait 7 seconds for it to start/settle
+            getContext().startActivity(intent);
+            try {
+                Thread.sleep(7000);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+
+            // See if there are any errors
+            Collection<ProcessErrorStateInfo> procs = mActivityManager.getProcessesInErrorState();
+            if (procs != null) {
+                errSet.addAll(ProcessError.fromCollection(procs));
+            }
+
+            // Send the "home" intent and wait 2 seconds for us to get there
+            getContext().startActivity(home);
+            try {
+                Thread.sleep(2000);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+        }
+
+        if (!errSet.isEmpty()) {
+            fail(String.format("Got %d errors: %s", errSet.size(),
+                    reportWrappedListContents(errSet)));
+        }
+    }
+
+    private String reportWrappedListContents(Collection<ProcessError> errList) {
+        List<ProcessErrorStateInfo> newList = new ArrayList<ProcessErrorStateInfo>(errList.size());
+        for (ProcessError err : errList) {
+            newList.add(err.info);
+        }
+        return reportListContents(newList);
+    }
+
     /**
      * This helper function will dump the actual error reports.
      * 
      * @param errList The error report containing one or more error records.
      * @return Returns a string containing all of the errors.
      */
-    private String reportListContents(List<ActivityManager.ProcessErrorStateInfo> errList) {
+    private String reportListContents(Collection<ProcessErrorStateInfo> errList) {
         if (errList == null) return null;
 
         StringBuilder builder = new StringBuilder();
 
-        Iterator<ActivityManager.ProcessErrorStateInfo> iter = errList.iterator();
+        Iterator<ProcessErrorStateInfo> iter = errList.iterator();
         while (iter.hasNext()) {
-            ActivityManager.ProcessErrorStateInfo entry = iter.next();
+            ProcessErrorStateInfo entry = iter.next();
 
             String condition;
             switch (entry.condition) {
@@ -96,8 +177,77 @@
             builder.append("Process error ").append(condition).append(" ");
             builder.append(" ").append(entry.shortMsg);
             builder.append(" detected in ").append(entry.processName).append(" ").append(entry.tag);
+            builder.append("\n");
         }
         return builder.toString();
     }
-    
+
+    /**
+     * A {@link ProcessErrorStateInfo} wrapper class that hashes how we want (so that equivalent
+     * crashes are considered equal).
+     */
+    private static class ProcessError {
+        public final ProcessErrorStateInfo info;
+
+        public ProcessError(ProcessErrorStateInfo newInfo) {
+            info = newInfo;
+        }
+
+        public static Collection<ProcessError> fromCollection(Collection<ProcessErrorStateInfo> in)
+                {
+            List<ProcessError> out = new ArrayList<ProcessError>(in.size());
+            for (ProcessErrorStateInfo info : in) {
+                out.add(new ProcessError(info));
+            }
+            return out;
+        }
+
+        private boolean strEquals(String a, String b) {
+            if ((a == null) && (b == null)) {
+                return true;
+            } else if ((a == null) || (b == null)) {
+                return false;
+            } else {
+                return a.equals(b);
+            }
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other == null) return false;
+            if (!(other instanceof ProcessError)) return false;
+            ProcessError peOther = (ProcessError) other;
+
+            return (info.condition == peOther.info.condition)
+                    && strEquals(info.longMsg, peOther.info.longMsg)
+                    && (info.pid == peOther.info.pid)
+                    && strEquals(info.processName, peOther.info.processName)
+                    && strEquals(info.shortMsg, peOther.info.shortMsg)
+                    && strEquals(info.stackTrace, peOther.info.stackTrace)
+                    && strEquals(info.tag, peOther.info.tag)
+                    && (info.uid == peOther.info.uid);
+        }
+
+        private int hash(Object obj) {
+            if (obj == null) {
+                return 13;
+            } else {
+                return obj.hashCode();
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            int code = 17;
+            code += info.condition;
+            code *= hash(info.longMsg);
+            code += info.pid;
+            code *= hash(info.processName);
+            code *= hash(info.shortMsg);
+            code *= hash(info.stackTrace);
+            code *= hash(info.tag);
+            code += info.uid;
+            return code;
+        }
+    }
 }
diff --git a/tests/SmokeTestApps/Android.mk b/tests/SmokeTestApps/Android.mk
new file mode 100644
index 0000000..3f5f011
--- /dev/null
+++ b/tests/SmokeTestApps/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := SmokeTestTriggerApps
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/SmokeTestApps/AndroidManifest.xml b/tests/SmokeTestApps/AndroidManifest.xml
new file mode 100644
index 0000000..0f20107
--- /dev/null
+++ b/tests/SmokeTestApps/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.smoketest.triggers">
+
+    <application android:label="something">
+        <activity android:name=".CrashyApp"
+                  android:label="Test Crashy App">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".CrashyApp2"
+                  android:label="Test Crashy App2">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".UnresponsiveApp"
+                  android:label="Test Unresponsive App">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/SmokeTestApps/README b/tests/SmokeTestApps/README
new file mode 100644
index 0000000..04aa366
--- /dev/null
+++ b/tests/SmokeTestApps/README
@@ -0,0 +1,3 @@
+The apps in this folder are intentionally bad-behaving apps that are intended
+to trigger the smoke tests to fail.  They are otherwise not useful.
+
diff --git a/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java
new file mode 100644
index 0000000..c11b0f3
--- /dev/null
+++ b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.android.smoketest.triggers;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class CrashyApp extends Activity {
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        TextView tv = new TextView(this);
+        tv.setText("Hello, Crashy Android");
+        setContentView(tv);
+    }
+
+    @Override
+    public void onResume() {
+        ((String) null).length();
+    }
+}
diff --git a/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp2.java b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp2.java
new file mode 100644
index 0000000..3ef5b2b
--- /dev/null
+++ b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp2.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.android.smoketest.triggers;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class CrashyApp2 extends Activity {
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        TextView tv = new TextView(this);
+        tv.setText("Hello, Other Crashy Android");
+        setContentView(tv);
+    }
+
+
+    @Override
+    public void onResume() {
+        throw new RuntimeException("Two drums and a cymbal fall off a cliff...");
+    }
+}
diff --git a/tests/SmokeTestApps/src/com/android/smoketest/triggers/UnresponsiveApp.java b/tests/SmokeTestApps/src/com/android/smoketest/triggers/UnresponsiveApp.java
new file mode 100644
index 0000000..1291897
--- /dev/null
+++ b/tests/SmokeTestApps/src/com/android/smoketest/triggers/UnresponsiveApp.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package com.android.smoketest.triggers;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class UnresponsiveApp extends Activity {
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        TextView tv = new TextView(this);
+        tv.setText("Hello, Unresponsive Android");
+        setContentView(tv);
+    }
+
+    @Override
+    public void onResume() {
+        // Attempt to provoke the ire of the ActivityManager
+        while (true) {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                // ignore
+            }
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index bbb74d1..d05e0b8 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -366,17 +366,6 @@
                     handleDriverEvent(eventData);
                 } else if (event == TERMINATING) {
                     /**
-                     * If monitor socket is closed, we have already
-                     * stopped the supplicant, simply exit the monitor thread
-                     */
-                    if (eventData.startsWith(MONITOR_SOCKET_CLOSED_STR)) {
-                        if (false) {
-                            Log.d(TAG, "Monitor socket is closed, exiting thread");
-                        }
-                        break;
-                    }
-
-                    /**
                      * Close the supplicant connection if we see
                      * too many recv errors
                      */
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 48a785c..e3dd3a6 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -39,6 +39,8 @@
  */
 public class WifiNative {
 
+    private static final boolean DBG = false;
+    private final String mTAG;
     private static final int DEFAULT_GROUP_OWNER_INTENT = 7;
 
     static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0;
@@ -53,9 +55,7 @@
 
     public native static boolean unloadDriver();
 
-    public native static boolean startSupplicant();
-
-    public native static boolean startP2pSupplicant();
+    public native static boolean startSupplicant(boolean p2pSupported);
 
     /* Sends a kill signal to supplicant. To be used when we have lost connection
        or when the supplicant is hung */
@@ -79,6 +79,7 @@
 
     public WifiNative(String iface) {
         mInterface = iface;
+        mTAG = "WifiNative-" + iface;
     }
 
     public boolean connectToSupplicant() {
@@ -94,14 +95,17 @@
     }
 
     private boolean doBooleanCommand(String command) {
+        if (DBG) Log.d(mTAG, "doBoolean: " + command);
         return doBooleanCommand(mInterface, command);
     }
 
     private int doIntCommand(String command) {
+        if (DBG) Log.d(mTAG, "doInt: " + command);
         return doIntCommand(mInterface, command);
     }
 
     private String doStringCommand(String command) {
+        if (DBG) Log.d(mTAG, "doString: " + command);
         return doStringCommand(mInterface, command);
     }
 
@@ -437,6 +441,10 @@
         return doBooleanCommand("P2P_FIND " + timeout);
     }
 
+    public boolean p2pStopFind() {
+       return doBooleanCommand("P2P_STOP_FIND");
+    }
+
     public boolean p2pListen() {
         return doBooleanCommand("P2P_LISTEN");
     }
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index fb9286e..0134456 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -45,6 +45,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
 import android.net.DhcpInfoInternal;
@@ -118,6 +119,8 @@
     private INetworkManagementService mNwService;
     private ConnectivityManager mCm;
 
+    private final boolean mP2pSupported;
+
     /* Scan results handling */
     private List<ScanResult> mScanResults;
     private static final Pattern scanResultPattern = Pattern.compile("\t+");
@@ -361,9 +364,9 @@
     /* Reset the WPS state machine */
     static final int CMD_RESET_WPS_STATE                  = BASE + 122;
 
-    /* Interaction with WifiP2pService */
-    public static final int WIFI_ENABLE_PENDING           = BASE + 131;
-    public static final int P2P_ENABLE_PROCEED            = BASE + 132;
+    /* P2p commands */
+    public static final int CMD_ENABLE_P2P                = BASE + 131;
+    public static final int CMD_DISABLE_P2P               = BASE + 132;
 
     private static final int CONNECT_MODE   = 1;
     private static final int SCAN_ONLY_MODE = 2;
@@ -482,9 +485,6 @@
     /* Waiting for untether confirmation to stop soft Ap */
     private State mSoftApStoppingState = new SoftApStoppingState();
 
-    /* Wait till p2p is disabled */
-    private State mWaitForP2pDisableState = new WaitForP2pDisableState();
-
     private class TetherStateChange {
         ArrayList<String> available;
         ArrayList<String> active;
@@ -556,6 +556,9 @@
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
         mNwService = INetworkManagementService.Stub.asInterface(b);
 
+        mP2pSupported = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_WIFI_DIRECT);
+
         mWifiNative = new WifiNative(mInterfaceName);
         mWifiConfigStore = new WifiConfigStore(context, mWifiNative);
         mWifiMonitor = new WifiMonitor(this, mWifiNative);
@@ -639,7 +642,6 @@
                 addState(mTetheringState, mSoftApStartedState);
                 addState(mTetheredState, mSoftApStartedState);
             addState(mSoftApStoppingState, mDefaultState);
-            addState(mWaitForP2pDisableState, mDefaultState);
 
         setInitialState(mInitialState);
 
@@ -1896,11 +1898,6 @@
                     mReplyChannel.replyToMessage(message, WifiManager.CMD_WPS_COMPLETED,
                                 new WpsResult(Status.FAILURE));
                     break;
-                case WifiP2pService.P2P_ENABLE_PENDING:
-                    // turn off wifi and defer to be handled in DriverUnloadedState
-                    setWifiEnabled(false);
-                    deferMessage(message);
-                    break;
                 default:
                     loge("Error! unhandled message" + message);
                     break;
@@ -2060,7 +2057,7 @@
                         loge("Unable to change interface settings: " + ie);
                     }
 
-                    if(mWifiNative.startSupplicant()) {
+                    if(mWifiNative.startSupplicant(mP2pSupported)) {
                         if (DBG) log("Supplicant start successful");
                         mWifiMonitor.startMonitoring();
                         transitionTo(mSupplicantStartingState);
@@ -2172,11 +2169,7 @@
             if (DBG) log(getName() + message.toString() + "\n");
             switch (message.what) {
                 case CMD_LOAD_DRIVER:
-                    mWifiP2pChannel.sendMessage(WIFI_ENABLE_PENDING);
-                    transitionTo(mWaitForP2pDisableState);
-                    break;
-                case WifiP2pService.P2P_ENABLE_PENDING:
-                    mReplyChannel.replyToMessage(message, P2P_ENABLE_PROCEED);
+                    transitionTo(mDriverLoadingState);
                     break;
                 default:
                     return NOT_HANDLED;
@@ -2556,13 +2549,15 @@
                 mWifiNative.status();
                 transitionTo(mDisconnectedState);
             }
+
+            if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
         }
         @Override
         public boolean processMessage(Message message) {
             if (DBG) log(getName() + message.toString() + "\n");
             boolean eventLoggingEnabled = true;
             switch(message.what) {
-                case CMD_SET_SCAN_TYPE:
+               case CMD_SET_SCAN_TYPE:
                     mSetScanActive = (message.arg1 == SCAN_ACTIVE);
                     mWifiNative.setScanMode(mSetScanActive);
                     break;
@@ -2675,6 +2670,8 @@
             mIsRunning = false;
             updateBatteryWorkSource(null);
             mScanResults = null;
+
+            if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P);
         }
     }
 
@@ -3348,7 +3345,6 @@
                 case CMD_START_PACKET_FILTERING:
                 case CMD_STOP_PACKET_FILTERING:
                 case CMD_TETHER_STATE_CHANGE:
-                case WifiP2pService.P2P_ENABLE_PENDING:
                     deferMessage(message);
                     break;
                 case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
@@ -3412,55 +3408,6 @@
                         transitionTo(mTetheringState);
                     }
                     break;
-                case WifiP2pService.P2P_ENABLE_PENDING:
-                    // turn of soft Ap and defer to be handled in DriverUnloadedState
-                    setWifiApEnabled(null, false);
-                    deferMessage(message);
-                    break;
-                default:
-                    return NOT_HANDLED;
-            }
-            EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
-            return HANDLED;
-        }
-    }
-
-    class WaitForP2pDisableState extends State {
-        private int mSavedArg;
-        @Override
-        public void enter() {
-            if (DBG) log(getName() + "\n");
-            EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
-
-            //Preserve the argument arg1 that has information used in DriverLoadingState
-            mSavedArg = getCurrentMessage().arg1;
-        }
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) log(getName() + message.toString() + "\n");
-            switch(message.what) {
-                case WifiP2pService.WIFI_ENABLE_PROCEED:
-                    //restore argument from original message (CMD_LOAD_DRIVER)
-                    message.arg1 = mSavedArg;
-                    transitionTo(mDriverLoadingState);
-                    break;
-                case CMD_LOAD_DRIVER:
-                case CMD_UNLOAD_DRIVER:
-                case CMD_START_SUPPLICANT:
-                case CMD_STOP_SUPPLICANT:
-                case CMD_START_AP:
-                case CMD_STOP_AP:
-                case CMD_START_DRIVER:
-                case CMD_STOP_DRIVER:
-                case CMD_SET_SCAN_MODE:
-                case CMD_SET_SCAN_TYPE:
-                case CMD_SET_HIGH_PERF_MODE:
-                case CMD_SET_COUNTRY_CODE:
-                case CMD_SET_FREQUENCY_BAND:
-                case CMD_START_PACKET_FILTERING:
-                case CMD_STOP_PACKET_FILTERING:
-                    deferMessage(message);
-                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -3510,7 +3457,6 @@
                 case CMD_SET_FREQUENCY_BAND:
                 case CMD_START_PACKET_FILTERING:
                 case CMD_STOP_PACKET_FILTERING:
-                case WifiP2pService.P2P_ENABLE_PENDING:
                     deferMessage(message);
                     break;
                 default:
@@ -3606,7 +3552,6 @@
                 case CMD_SET_FREQUENCY_BAND:
                 case CMD_START_PACKET_FILTERING:
                 case CMD_STOP_PACKET_FILTERING:
-                case WifiP2pService.P2P_ENABLE_PENDING:
                     deferMessage(message);
                     break;
                 default:
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index 7471a2d..b0cde64 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -301,7 +301,8 @@
     private String trimQuotes(String str) {
         str = str.trim();
         if (str.startsWith("'") && str.endsWith("'")) {
-            return str.substring(1, str.length()-1);
+            if (str.length() <= 2) return "";
+            else return str.substring(1, str.length()-1);
         }
         return str;
     }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 9205300..4fd0a57 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -199,68 +199,61 @@
     private static final int BASE = Protocol.BASE_WIFI_P2P_MANAGER;
 
     /** @hide */
-    public static final int ENABLE_P2P                              = BASE + 1;
+    public static final int DISCOVER_PEERS                          = BASE + 1;
     /** @hide */
-    public static final int ENABLE_P2P_FAILED                       = BASE + 2;
+    public static final int DISCOVER_PEERS_FAILED                   = BASE + 2;
     /** @hide */
-    public static final int ENABLE_P2P_SUCCEEDED                    = BASE + 3;
+    public static final int DISCOVER_PEERS_SUCCEEDED                = BASE + 3;
 
     /** @hide */
-    public static final int DISABLE_P2P                             = BASE + 4;
+    public static final int STOP_DISCOVERY                          = BASE + 4;
     /** @hide */
-    public static final int DISABLE_P2P_FAILED                      = BASE + 5;
+    public static final int STOP_DISCOVERY_FAILED                   = BASE + 5;
     /** @hide */
-    public static final int DISABLE_P2P_SUCCEEDED                   = BASE + 6;
+    public static final int STOP_DISCOVERY_SUCCEEDED                = BASE + 6;
 
     /** @hide */
-    public static final int DISCOVER_PEERS                          = BASE + 7;
+    public static final int CONNECT                                 = BASE + 7;
     /** @hide */
-    public static final int DISCOVER_PEERS_FAILED                   = BASE + 8;
+    public static final int CONNECT_FAILED                          = BASE + 8;
     /** @hide */
-    public static final int DISCOVER_PEERS_SUCCEEDED                = BASE + 9;
+    public static final int CONNECT_SUCCEEDED                       = BASE + 9;
 
     /** @hide */
-    public static final int CONNECT                                 = BASE + 10;
+    public static final int CANCEL_CONNECT                          = BASE + 10;
     /** @hide */
-    public static final int CONNECT_FAILED                          = BASE + 11;
+    public static final int CANCEL_CONNECT_FAILED                   = BASE + 11;
     /** @hide */
-    public static final int CONNECT_SUCCEEDED                       = BASE + 12;
+    public static final int CANCEL_CONNECT_SUCCEEDED                = BASE + 12;
 
     /** @hide */
-    public static final int CANCEL_CONNECT                          = BASE + 13;
+    public static final int CREATE_GROUP                            = BASE + 13;
     /** @hide */
-    public static final int CANCEL_CONNECT_FAILED                   = BASE + 14;
+    public static final int CREATE_GROUP_FAILED                     = BASE + 14;
     /** @hide */
-    public static final int CANCEL_CONNECT_SUCCEEDED                = BASE + 15;
+    public static final int CREATE_GROUP_SUCCEEDED                  = BASE + 15;
 
     /** @hide */
-    public static final int CREATE_GROUP                            = BASE + 16;
+    public static final int REMOVE_GROUP                            = BASE + 16;
     /** @hide */
-    public static final int CREATE_GROUP_FAILED                     = BASE + 17;
+    public static final int REMOVE_GROUP_FAILED                     = BASE + 17;
     /** @hide */
-    public static final int CREATE_GROUP_SUCCEEDED                  = BASE + 18;
+    public static final int REMOVE_GROUP_SUCCEEDED                  = BASE + 18;
 
     /** @hide */
-    public static final int REMOVE_GROUP                            = BASE + 19;
+    public static final int REQUEST_PEERS                           = BASE + 19;
     /** @hide */
-    public static final int REMOVE_GROUP_FAILED                     = BASE + 20;
-    /** @hide */
-    public static final int REMOVE_GROUP_SUCCEEDED                  = BASE + 21;
+    public static final int RESPONSE_PEERS                          = BASE + 20;
 
     /** @hide */
-    public static final int REQUEST_PEERS                           = BASE + 22;
+    public static final int REQUEST_CONNECTION_INFO                 = BASE + 21;
     /** @hide */
-    public static final int RESPONSE_PEERS                          = BASE + 23;
+    public static final int RESPONSE_CONNECTION_INFO                = BASE + 22;
 
     /** @hide */
-    public static final int REQUEST_CONNECTION_INFO                 = BASE + 24;
+    public static final int REQUEST_GROUP_INFO                      = BASE + 23;
     /** @hide */
-    public static final int RESPONSE_CONNECTION_INFO                = BASE + 25;
-
-    /** @hide */
-    public static final int REQUEST_GROUP_INFO                      = BASE + 26;
-    /** @hide */
-    public static final int RESPONSE_GROUP_INFO                     = BASE + 27;
+    public static final int RESPONSE_GROUP_INFO                     = BASE + 24;
 
     /**
      * Create a new WifiP2pManager instance. Applications use
@@ -376,6 +369,7 @@
                         break;
                     /* ActionListeners grouped together */
                     case WifiP2pManager.DISCOVER_PEERS_FAILED:
+                    case WifiP2pManager.STOP_DISCOVERY_FAILED:
                     case WifiP2pManager.CONNECT_FAILED:
                     case WifiP2pManager.CANCEL_CONNECT_FAILED:
                     case WifiP2pManager.CREATE_GROUP_FAILED:
@@ -386,6 +380,7 @@
                         break;
                     /* ActionListeners grouped together */
                     case WifiP2pManager.DISCOVER_PEERS_SUCCEEDED:
+                    case WifiP2pManager.STOP_DISCOVERY_SUCCEEDED:
                     case WifiP2pManager.CONNECT_SUCCEEDED:
                     case WifiP2pManager.CANCEL_CONNECT_SUCCEEDED:
                     case WifiP2pManager.CREATE_GROUP_SUCCEEDED:
@@ -459,26 +454,6 @@
     }
 
     /**
-     * Sends in a request to the system to enable p2p. This will pop up a dialog
-     * to the user and upon authorization will enable p2p.
-     * @hide
-     */
-    public void enableP2p(Channel c) {
-        if (c == null) return;
-        c.mAsyncChannel.sendMessage(ENABLE_P2P);
-    }
-
-    /**
-     * Sends in a request to the system to disable p2p. This will pop up a dialog
-     * to the user and upon authorization will enable p2p.
-     * @hide
-     */
-    public void disableP2p(Channel c) {
-        if (c == null) return;
-        c.mAsyncChannel.sendMessage(DISABLE_P2P);
-    }
-
-    /**
      * Initiate peer discovery. A discovery process involves scanning for available Wi-Fi peers
      * for the purpose of establishing a connection.
      *
@@ -503,6 +478,16 @@
     }
 
     /**
+     * TODO: Add more documentation before opening up
+     * Cancel peer discovery
+     * @hide
+     */
+    public void stopPeerDiscovery(Channel c, ActionListener listener) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, c.putListener(listener));
+    }
+
+    /**
      * Start a p2p connection to a device with the specified configuration.
      *
      * <p> The function call immediately returns after sending a connection request
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 69cbb5c..5b0e424 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -49,6 +49,7 @@
 import android.os.HandlerThread;
 import android.os.Message;
 import android.os.Messenger;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.provider.Settings;
@@ -84,7 +85,7 @@
  */
 public class WifiP2pService extends IWifiP2pManager.Stub {
     private static final String TAG = "WifiP2pService";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
     private static final String NETWORKTYPE = "WIFI_P2P";
 
     private Context mContext;
@@ -94,11 +95,6 @@
     INetworkManagementService mNwService;
     private DhcpStateMachine mDhcpStateMachine;
 
-    //Tracked to notify the user about wifi client/hotspot being shut down
-    //during p2p bring up
-    private int mWifiState = WifiManager.WIFI_STATE_DISABLED;
-    private int mWifiApState = WifiManager.WIFI_AP_STATE_DISABLED;
-
     private P2pStateMachine mP2pStateMachine;
     private AsyncChannel mReplyChannel = new AsyncChannel();
     private AsyncChannel mWifiChannel;
@@ -110,6 +106,9 @@
     private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000;
     private static int mGroupCreatingTimeoutIndex = 0;
 
+    /* Set a two minute discover timeout to avoid STA scans from being blocked */
+    private static final int DISCOVER_TIMEOUT_S = 120;
+
     /**
      * Delay between restarts upon failure to setup connection with supplicant
      */
@@ -124,28 +123,13 @@
 
     private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE;
 
-    /* Message sent to WifiStateMachine to indicate p2p enable is pending */
-    public static final int P2P_ENABLE_PENDING              =   BASE + 1;
-    /* Message sent to WifiStateMachine to indicate Wi-Fi client/hotspot operation can proceed */
-    public static final int WIFI_ENABLE_PROCEED             =   BASE + 2;
-
     /* Delayed message to timeout group creation */
-    public static final int GROUP_CREATING_TIMED_OUT        =   BASE + 3;
-
-    /* User accepted to disable Wi-Fi in order to enable p2p */
-    private static final int WIFI_DISABLE_USER_ACCEPT       =   BASE + 4;
-    /* User rejected to disable Wi-Fi in order to enable p2p */
-    private static final int WIFI_DISABLE_USER_REJECT       =   BASE + 5;
+    public static final int GROUP_CREATING_TIMED_OUT        =   BASE + 1;
 
     /* User accepted a peer request */
-    private static final int PEER_CONNECTION_USER_ACCEPT    =   BASE + 6;
+    private static final int PEER_CONNECTION_USER_ACCEPT    =   BASE + 2;
     /* User rejected a peer request */
-    private static final int PEER_CONNECTION_USER_REJECT    =   BASE + 7;
-
-    /* Airplane mode changed */
-    private static final int AIRPLANE_MODE_CHANGED          =   BASE + 8;
-    /* Emergency callback mode */
-    private static final int EMERGENCY_CALLBACK_MODE        =   BASE + 9;
+    private static final int PEER_CONNECTION_USER_REJECT    =   BASE + 3;
 
     private final boolean mP2pSupported;
 
@@ -166,7 +150,7 @@
     public WifiP2pService(Context context) {
         mContext = context;
 
-        //STOPSHIP: fix this
+        //STOPSHIP: get this from native side
         mInterface = "p2p0";
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");
 
@@ -179,15 +163,6 @@
 
         mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported);
         mP2pStateMachine.start();
-
-        // broadcasts
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
-        filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
-        mContext.registerReceiver(new WifiStateReceiver(), filter);
-
     }
 
     public void connectivityServiceReady() {
@@ -195,26 +170,6 @@
         mNwService = INetworkManagementService.Stub.asInterface(b);
     }
 
-    private class WifiStateReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-                mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                        WifiManager.WIFI_STATE_DISABLED);
-            } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
-                mWifiApState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
-                        WifiManager.WIFI_AP_STATE_DISABLED);
-            } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
-                mP2pStateMachine.sendMessage(AIRPLANE_MODE_CHANGED);
-            } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
-                if (intent.getBooleanExtra("phoneinECMState", false) == true) {
-                    mP2pStateMachine.sendMessage(EMERGENCY_CALLBACK_MODE);
-                }
-            }
-        }
-    }
-
     private void enforceAccessPermission() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
                 "WifiP2pService");
@@ -264,8 +219,6 @@
         private P2pNotSupportedState mP2pNotSupportedState = new P2pNotSupportedState();
         private P2pDisablingState mP2pDisablingState = new P2pDisablingState();
         private P2pDisabledState mP2pDisabledState = new P2pDisabledState();
-        private WaitForUserActionState mWaitForUserActionState = new WaitForUserActionState();
-        private WaitForWifiDisableState mWaitForWifiDisableState = new WaitForWifiDisableState();
         private P2pEnablingState mP2pEnablingState = new P2pEnablingState();
         private P2pEnabledState mP2pEnabledState = new P2pEnabledState();
         // Inactive is when p2p is enabled with no connectivity
@@ -299,8 +252,6 @@
                 addState(mP2pNotSupportedState, mDefaultState);
                 addState(mP2pDisablingState, mDefaultState);
                 addState(mP2pDisabledState, mDefaultState);
-                    addState(mWaitForUserActionState, mP2pDisabledState);
-                    addState(mWaitForWifiDisableState, mP2pDisabledState);
                 addState(mP2pEnablingState, mDefaultState);
                 addState(mP2pEnabledState, mDefaultState);
                     addState(mInactiveState, mP2pEnabledState);
@@ -346,23 +297,14 @@
                     AsyncChannel ac = new AsyncChannel();
                     ac.connect(mContext, getHandler(), message.replyTo);
                     break;
-                case WifiStateMachine.WIFI_ENABLE_PENDING:
-                    // Disable p2p operation before we can respond
-                    sendMessage(WifiP2pManager.DISABLE_P2P);
-                    deferMessage(message);
-                    break;
-                case WifiP2pManager.ENABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
-                            WifiP2pManager.BUSY);
-                    break;
-                case WifiP2pManager.DISABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
-                            WifiP2pManager.BUSY);
-                    break;
                 case WifiP2pManager.DISCOVER_PEERS:
                     replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
                             WifiP2pManager.BUSY);
                     break;
+                case WifiP2pManager.STOP_DISCOVERY:
+                    replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+                            WifiP2pManager.BUSY);
+                    break;
                 case WifiP2pManager.CONNECT:
                     replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
                             WifiP2pManager.BUSY);
@@ -388,16 +330,14 @@
                 case WifiP2pManager.REQUEST_GROUP_INFO:
                     replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, mGroup);
                     break;
-                case AIRPLANE_MODE_CHANGED:
-                    if (isAirplaneModeOn()) sendMessage(WifiP2pManager.DISABLE_P2P);
-                    break;
-                case EMERGENCY_CALLBACK_MODE:
-                    sendMessage(WifiP2pManager.DISABLE_P2P);
-                    break;
                     // Ignore
                 case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
-                case WIFI_DISABLE_USER_ACCEPT:
-                case WIFI_DISABLE_USER_REJECT:
+                case WifiMonitor.SCAN_RESULTS_EVENT:
+                case WifiMonitor.SUP_CONNECTION_EVENT:
+                case WifiMonitor.SUP_DISCONNECTION_EVENT:
+                case WifiMonitor.NETWORK_CONNECTION_EVENT:
+                case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
+                case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
                 case PEER_CONNECTION_USER_ACCEPT:
                 case PEER_CONNECTION_USER_REJECT:
                 case GROUP_CREATING_TIMED_OUT:
@@ -414,22 +354,14 @@
         @Override
         public boolean processMessage(Message message) {
             switch (message.what) {
-                // Allow Wi-Fi to proceed
-                case WifiStateMachine.WIFI_ENABLE_PENDING:
-                    replyToMessage(message, WIFI_ENABLE_PROCEED);
-                    break;
-                case WifiP2pManager.ENABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
-                            WifiP2pManager.P2P_UNSUPPORTED);
-                    break;
-                case WifiP2pManager.DISABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
-                            WifiP2pManager.P2P_UNSUPPORTED);
-                    break;
-                case WifiP2pManager.DISCOVER_PEERS:
+               case WifiP2pManager.DISCOVER_PEERS:
                     replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
                     break;
+                case WifiP2pManager.STOP_DISCOVERY:
+                    replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+                            WifiP2pManager.P2P_UNSUPPORTED);
+                    break;
                 case WifiP2pManager.CONNECT:
                     replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
@@ -438,7 +370,7 @@
                     replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
                     break;
-                case WifiP2pManager.CREATE_GROUP:
+               case WifiP2pManager.CREATE_GROUP:
                     replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
                     break;
@@ -455,26 +387,15 @@
 
     class P2pDisablingState extends State {
         @Override
-        public void enter() {
-            if (DBG) logd(getName());
-            logd("stopping supplicant");
-            if (!mWifiNative.stopSupplicant()) {
-                loge("Failed to stop supplicant, issue kill");
-                mWifiNative.killSupplicant();
-            }
-        }
-
-        @Override
         public boolean processMessage(Message message) {
             if (DBG) logd(getName() + message.toString());
             switch (message.what) {
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
-                    logd("Supplicant connection lost");
-                    mWifiNative.closeSupplicantConnection();
+                    if (DBG) logd("p2p socket connection lost");
                     transitionTo(mP2pDisabledState);
                     break;
-                case WifiP2pManager.ENABLE_P2P:
-                case WifiP2pManager.DISABLE_P2P:
+                case WifiStateMachine.CMD_ENABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P:
                     deferMessage(message);
                     break;
                 default:
@@ -484,7 +405,6 @@
         }
     }
 
-
     class P2pDisabledState extends State {
        @Override
         public void enter() {
@@ -495,118 +415,19 @@
         public boolean processMessage(Message message) {
             if (DBG) logd(getName() + message.toString());
             switch (message.what) {
-                case WifiP2pManager.ENABLE_P2P:
-                    OnClickListener listener = new OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int which) {
-                            if (which == DialogInterface.BUTTON_POSITIVE) {
-                                sendMessage(WIFI_DISABLE_USER_ACCEPT);
-                            } else {
-                                sendMessage(WIFI_DISABLE_USER_REJECT);
-                            }
-                        }
-                    };
-
-                    // Show a user request dialog if we know Wi-Fi client/hotspot is in operation
-                    if (mWifiState != WifiManager.WIFI_STATE_DISABLED ||
-                            mWifiApState != WifiManager.WIFI_AP_STATE_DISABLED) {
-                        Resources r = Resources.getSystem();
-                        AlertDialog dialog = new AlertDialog.Builder(mContext)
-                            .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
-                            .setMessage(r.getString(R.string.wifi_p2p_turnon_message))
-                            .setPositiveButton(r.getString(R.string.ok), listener)
-                            .setNegativeButton(r.getString(R.string.cancel), listener)
-                            .create();
-                        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
-                        dialog.show();
-                        transitionTo(mWaitForUserActionState);
-                    } else {
-                        mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
-                        transitionTo(mWaitForWifiDisableState);
-                    }
-                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
-                    break;
-                case WifiP2pManager.DISABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
-                    break;
-                case WifiStateMachine.WIFI_ENABLE_PENDING:
-                    replyToMessage(message, WIFI_ENABLE_PROCEED);
-                    break;
-                default:
-                    return NOT_HANDLED;
-            }
-            return HANDLED;
-        }
-    }
-
-    class WaitForUserActionState extends State {
-        @Override
-        public void enter() {
-            if (DBG) logd(getName());
-        }
-
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) logd(getName() + message.toString());
-            switch (message.what) {
-                case WIFI_DISABLE_USER_ACCEPT:
-                    mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
-                    transitionTo(mWaitForWifiDisableState);
-                    break;
-                case WIFI_DISABLE_USER_REJECT:
-                    logd("User rejected enabling p2p");
-                    sendP2pStateChangedBroadcast(false);
-                    transitionTo(mP2pDisabledState);
-                    break;
-                case WifiP2pManager.ENABLE_P2P:
-                case WifiP2pManager.DISABLE_P2P:
-                    deferMessage(message);
-                    break;
-                default:
-                    return NOT_HANDLED;
-            }
-            return HANDLED;
-        }
-    }
-
-    class WaitForWifiDisableState extends State {
-        @Override
-        public void enter() {
-            if (DBG) logd(getName());
-        }
-
-        @Override
-        public boolean processMessage(Message message) {
-            if (DBG) logd(getName() + message.toString());
-            switch (message.what) {
-                case WifiStateMachine.P2P_ENABLE_PROCEED:
+                case WifiStateMachine.CMD_ENABLE_P2P:
                     try {
-                        mNwService.wifiFirmwareReload(mInterface, "P2P");
-                    } catch (Exception e) {
-                        loge("Failed to reload p2p firmware " + e);
-                        // continue
+                        mNwService.setInterfaceUp(mInterface);
+                    } catch (RemoteException re) {
+                        loge("Unable to change interface settings: " + re);
+                    } catch (IllegalStateException ie) {
+                        loge("Unable to change interface settings: " + ie);
                     }
-
-                    //A runtime crash can leave the interface up and
-                    //this affects p2p when supplicant starts up.
-                    //Ensure interface is down before a supplicant start.
-                    try {
-                        mNwService.setInterfaceDown(mInterface);
-                    } catch (Exception e) {
-                        if (DBG) Slog.w(TAG, "Unable to bring down wlan interface: " + e);
-                    }
-
-                    if (mWifiNative.startP2pSupplicant()) {
-                        mWifiMonitor.startMonitoring();
-                        transitionTo(mP2pEnablingState);
-                    } else {
-                        notifyP2pEnableFailure();
-                        transitionTo(mP2pDisabledState);
-                    }
+                    mWifiMonitor.startMonitoring();
+                    transitionTo(mP2pEnablingState);
                     break;
-                case WifiP2pManager.ENABLE_P2P:
-                case WifiP2pManager.DISABLE_P2P:
-                    deferMessage(message);
+                case WifiStateMachine.CMD_DISABLE_P2P:
+                    //Nothing to do
                     break;
                 default:
                     return NOT_HANDLED;
@@ -626,22 +447,15 @@
             if (DBG) logd(getName() + message.toString());
             switch (message.what) {
                 case WifiMonitor.SUP_CONNECTION_EVENT:
-                    logd("P2p start successful");
+                    if (DBG) logd("P2p socket connection successful");
                     transitionTo(mInactiveState);
                     break;
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
-                    if (++mP2pRestartCount <= P2P_RESTART_TRIES) {
-                        loge("Failed to start p2p, retry");
-                        mWifiNative.killSupplicant();
-                        sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
-                    } else {
-                        loge("Failed " + mP2pRestartCount + " times to start p2p, quit ");
-                        mP2pRestartCount = 0;
-                    }
+                    loge("P2p socket connection failed");
                     transitionTo(mP2pDisabledState);
                     break;
-                case WifiP2pManager.ENABLE_P2P:
-                case WifiP2pManager.DISABLE_P2P:
+                case WifiStateMachine.CMD_ENABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P:
                     deferMessage(message);
                     break;
                 default:
@@ -658,30 +472,36 @@
             sendP2pStateChangedBroadcast(true);
             mNetworkInfo.setIsAvailable(true);
             initializeP2pSettings();
-            showNotification();
         }
 
         @Override
         public boolean processMessage(Message message) {
             if (DBG) logd(getName() + message.toString());
             switch (message.what) {
-                case WifiP2pManager.ENABLE_P2P:
-                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
+                case WifiStateMachine.CMD_ENABLE_P2P:
+                    //Nothing to do
                     break;
-                case WifiP2pManager.DISABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P:
                     if (mPeers.clear()) sendP2pPeersChangedBroadcast();
-                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
+                    mWifiNative.closeSupplicantConnection();
                     transitionTo(mP2pDisablingState);
                     break;
                 case WifiP2pManager.DISCOVER_PEERS:
-                    int timeout = message.arg1;
-                    if (mWifiNative.p2pFind(timeout)) {
+                    if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
                         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
                     } else {
                         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
                                 WifiP2pManager.ERROR);
                     }
                     break;
+                case WifiP2pManager.STOP_DISCOVERY:
+                    if (mWifiNative.p2pStopFind()) {
+                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED);
+                    } else {
+                        replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED,
+                                WifiP2pManager.ERROR);
+                    }
+                    break;
                 case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
                     WifiP2pDevice device = (WifiP2pDevice) message.obj;
                     if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
@@ -692,15 +512,7 @@
                     device = (WifiP2pDevice) message.obj;
                     if (mPeers.remove(device)) sendP2pPeersChangedBroadcast();
                     break;
-               case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant died */
-                    loge("Connection lost, restart p2p");
-                    mWifiNative.killSupplicant();
-                    mWifiNative.closeSupplicantConnection();
-                    if (mPeers.clear()) sendP2pPeersChangedBroadcast();
-                    transitionTo(mP2pDisabledState);
-                    sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
-                    break;
-                default:
+               default:
                     return NOT_HANDLED;
             }
             return HANDLED;
@@ -710,7 +522,6 @@
         public void exit() {
             sendP2pStateChangedBroadcast(false);
             mNetworkInfo.setIsAvailable(false);
-            clearNotification();
         }
     }
 
@@ -719,7 +530,8 @@
         public void enter() {
             if (DBG) logd(getName());
             //Start listening every time we get inactive
-            mWifiNative.p2pListen();
+            //TODO: Fix listen after driver behavior is fixed
+            //mWifiNative.p2pListen();
         }
 
         @Override
@@ -737,6 +549,8 @@
                         //TODO: if failure, remove config and do a regular p2pConnect()
                         mWifiNative.p2pReinvoke(netId, mSavedPeerConfig.deviceAddress);
                     } else {
+                        //Stop discovery before issuing connect
+                        mWifiNative.p2pStopFind();
                         //If peer is a GO, we do not need to send provisional discovery,
                         //the supplicant takes care of it.
                         if (isGroupOwner(mSavedPeerConfig.deviceAddress)) {
@@ -1114,7 +928,7 @@
                     }
                     // Do the regular device lost handling
                     return NOT_HANDLED;
-                case WifiP2pManager.DISABLE_P2P:
+                case WifiStateMachine.CMD_DISABLE_P2P:
                     sendMessage(WifiP2pManager.REMOVE_GROUP);
                     deferMessage(message);
                     break;
@@ -1494,54 +1308,5 @@
         Slog.e(TAG, s);
     }
 
-    private void showNotification() {
-        NotificationManager notificationManager =
-            (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        if (notificationManager == null || mNotification != null) {
-            return;
-        }
-
-        Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
-
-        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
-
-        Resources r = Resources.getSystem();
-        CharSequence title = r.getText(R.string.wifi_p2p_enabled_notification_title);
-        CharSequence message = r.getText(R.string.wifi_p2p_enabled_notification_message);
-
-        mNotification = new Notification();
-        mNotification.when = 0;
-        //TODO: might change to be a seperate icon
-        mNotification.icon = R.drawable.stat_sys_tether_wifi;
-        mNotification.defaults &= ~Notification.DEFAULT_SOUND;
-        mNotification.flags = Notification.FLAG_ONGOING_EVENT;
-        mNotification.tickerText = title;
-        mNotification.setLatestEventInfo(mContext, title, message, pi);
-
-        notificationManager.notify(mNotification.icon, mNotification);
-    }
-
-    private void clearNotification() {
-        NotificationManager notificationManager =
-            (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-        if (notificationManager != null && mNotification != null) {
-            notificationManager.cancel(mNotification.icon);
-            mNotification = null;
-        }
-    }
-
-    private boolean isAirplaneSensitive() {
-        String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
-                Settings.System.AIRPLANE_MODE_RADIOS);
-        return airplaneModeRadios == null
-            || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
-    }
-
-    private boolean isAirplaneModeOn() {
-        return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
-                Settings.System.AIRPLANE_MODE_ON, 0) == 1;
-    }
-
     }
 }