Merge "Update Arabic fonts to remove Rial ligature" into jb-mr1-dev
diff --git a/api/17.txt b/api/17.txt
index ee9a973..84c2a93 100644
--- a/api/17.txt
+++ b/api/17.txt
@@ -28617,7 +28617,6 @@
     method public void setHeight(int);
     method public void setHorizontalOffset(int);
     method public void setInputMethodMode(int);
-    method public void setLayoutDirection(int);
     method public void setListSelector(android.graphics.drawable.Drawable);
     method public void setModal(boolean);
     method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener);
diff --git a/api/current.txt b/api/current.txt
index 3705140..d7f4e11 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7094,7 +7094,7 @@
     method public int getIndexCount();
     method public int getInt(int, int);
     method public int getInteger(int, int);
-    method public int getLayoutDimension(int, java.lang.String);
+    method public deprecated int getLayoutDimension(int, java.lang.String);
     method public int getLayoutDimension(int, int);
     method public java.lang.String getNonResourceString(int);
     method public java.lang.String getPositionDescription();
@@ -16151,7 +16151,7 @@
 
   public class Looper {
     method public void dump(android.util.Printer, java.lang.String);
-    method public static synchronized android.os.Looper getMainLooper();
+    method public static android.os.Looper getMainLooper();
     method public java.lang.Thread getThread();
     method public static void loop();
     method public static android.os.Looper myLooper();
@@ -28619,7 +28619,6 @@
     method public void setHeight(int);
     method public void setHorizontalOffset(int);
     method public void setInputMethodMode(int);
-    method public void setLayoutDirection(int);
     method public void setListSelector(android.graphics.drawable.Drawable);
     method public void setModal(boolean);
     method public void setOnDismissListener(android.widget.PopupWindow.OnDismissListener);
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 2968fbb..7f3b6b9 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -469,13 +469,20 @@
      * {@link android.view.ViewGroup}'s layout_width and layout_height
      * attributes.  This is only here for performance reasons; applications
      * should use {@link #getDimensionPixelSize}.
-     * 
+     *
      * @param index Index of the attribute to retrieve.
      * @param name Textual name of attribute for error reporting.
      * 
      * @return Attribute dimension value multiplied by the appropriate 
      * metric and truncated to integer pixels.
+     *
+     * @throws RuntimeException
+     *             if this TypedArray does not contain an entry for <code>index</code>
+     *
+     * @deprecated Use {@link #getLayoutDimension(int, int)} instead.
+     *
      */
+    @Deprecated
     public int getLayoutDimension(int index, String name) {
         index *= AssetManager.STYLE_NUM_ENTRIES;
         final int[] data = mData;
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 26a5b26..f692e05 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -20,6 +20,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.widget.FrameLayout;
+import com.android.internal.R;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -43,20 +44,20 @@
  *
  * <pre>LayoutInflater inflater = (LayoutInflater)context.getSystemService
  *      (Context.LAYOUT_INFLATER_SERVICE);</pre>
- * 
+ *
  * <p>
  * To create a new LayoutInflater with an additional {@link Factory} for your
  * own views, you can use {@link #cloneInContext} to clone an existing
  * ViewFactory, and then call {@link #setFactory} on it to include your
  * Factory.
- * 
+ *
  * <p>
  * For performance reasons, view inflation relies heavily on pre-processing of
  * XML files that is done at build time. Therefore, it is not currently possible
  * to use LayoutInflater with an XmlPullParser over a plain XML file at runtime;
  * it only works with an XmlPullParser returned from a compiled resource
  * (R.<em>something</em> file.)
- * 
+ *
  * @see Context#getSystemService
  */
 public abstract class LayoutInflater {
@@ -82,7 +83,7 @@
 
     private static final HashMap<String, Constructor<? extends View>> sConstructorMap =
             new HashMap<String, Constructor<? extends View>>();
-    
+
     private HashMap<String, Boolean> mFilterMap;
 
     private static final String TAG_MERGE = "merge";
@@ -93,36 +94,36 @@
     /**
      * Hook to allow clients of the LayoutInflater to restrict the set of Views that are allowed
      * to be inflated.
-     * 
+     *
      */
     public interface Filter {
         /**
          * Hook to allow clients of the LayoutInflater to restrict the set of Views 
          * that are allowed to be inflated.
-         * 
+         *
          * @param clazz The class object for the View that is about to be inflated
-         * 
+         *
          * @return True if this class is allowed to be inflated, or false otherwise
          */
         @SuppressWarnings("unchecked")
         boolean onLoadClass(Class clazz);
     }
-    
+
     public interface Factory {
         /**
          * Hook you can supply that is called when inflating from a LayoutInflater.
          * You can use this to customize the tag names available in your XML
          * layout files.
-         * 
+         *
          * <p>
          * Note that it is good practice to prefix these custom names with your
          * package (i.e., com.coolcompany.apps) to avoid conflicts with system
          * names.
-         * 
+         *
          * @param name Tag name to be inflated.
          * @param context The context the view is being created in.
          * @param attrs Inflation attributes as specified in XML file.
-         * 
+         *
          * @return View Newly created view. Return null for the default
          *         behavior.
          */
@@ -150,14 +151,14 @@
     private static class FactoryMerger implements Factory2 {
         private final Factory mF1, mF2;
         private final Factory2 mF12, mF22;
-        
+
         FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {
             mF1 = f1;
             mF2 = f2;
             mF12 = f12;
             mF22 = f22;
         }
-        
+
         public View onCreateView(String name, Context context, AttributeSet attrs) {
             View v = mF1.onCreateView(name, context, attrs);
             if (v != null) return v;
@@ -172,13 +173,13 @@
                     : mF2.onCreateView(name, context, attrs);
         }
     }
-    
+
     /**
      * Create a new LayoutInflater instance associated with a particular Context.
      * Applications will almost always want to use
      * {@link Context#getSystemService Context.getSystemService()} to retrieve
      * the standard {@link Context#LAYOUT_INFLATER_SERVICE Context.INFLATER_SERVICE}.
-     * 
+     *
      * @param context The Context in which this LayoutInflater will create its
      * Views; most importantly, this supplies the theme from which the default
      * values for their attributes are retrieved.
@@ -191,7 +192,7 @@
      * Create a new LayoutInflater instance that is a copy of an existing
      * LayoutInflater, optionally with its Context changed.  For use in
      * implementing {@link #cloneInContext}.
-     * 
+     *
      * @param original The original LayoutInflater to copy.
      * @param newContext The new Context to use.
      */
@@ -202,7 +203,7 @@
         mPrivateFactory = original.mPrivateFactory;
         mFilter = original.mFilter;
     }
-    
+
     /**
      * Obtains the LayoutInflater from the given context.
      */
@@ -220,15 +221,15 @@
      * pointing to a different Context than the original.  This is used by
      * {@link ContextThemeWrapper} to create a new LayoutInflater to go along
      * with the new Context theme.
-     * 
+     *
      * @param newContext The new Context to associate with the new LayoutInflater.
      * May be the same as the original Context if desired.
-     * 
+     *
      * @return Returns a brand spanking new LayoutInflater object associated with
      * the given Context.
      */
     public abstract LayoutInflater cloneInContext(Context newContext);
-    
+
     /**
      * Return the context we are running in, for access to resources, class
      * loader, etc.
@@ -264,7 +265,7 @@
      * called on each element name as the xml is parsed. If the factory returns
      * a View, that is added to the hierarchy. If it returns null, the next
      * factory default {@link #onCreateView} method is called.
-     * 
+     *
      * <p>If you have an existing
      * LayoutInflater and want to add your own factory to it, use
      * {@link #cloneInContext} to clone the existing instance and then you
@@ -320,13 +321,13 @@
     public Filter getFilter() {
         return mFilter;
     }
-    
+
     /**
      * Sets the {@link Filter} to by this LayoutInflater. If a view is attempted to be inflated
      * which is not allowed by the {@link Filter}, the {@link #inflate(int, ViewGroup)} call will
      * throw an {@link InflateException}. This filter will replace any previous filter set on this
      * LayoutInflater.
-     * 
+     *
      * @param filter The Filter which restricts the set of Views that are allowed to be inflated.
      *        This filter will replace any previous filter set on this LayoutInflater.
      */
@@ -340,7 +341,7 @@
     /**
      * Inflate a new view hierarchy from the specified xml resource. Throws
      * {@link InflateException} if there is an error.
-     * 
+     *
      * @param resource ID for an XML layout resource to load (e.g.,
      *        <code>R.layout.main_page</code>)
      * @param root Optional view to be the parent of the generated hierarchy.
@@ -360,7 +361,7 @@
      * reasons, view inflation relies heavily on pre-processing of XML files
      * that is done at build time. Therefore, it is not currently possible to
      * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
-     * 
+     *
      * @param parser XML dom node containing the description of the view
      *        hierarchy.
      * @param root Optional view to be the parent of the generated hierarchy.
@@ -375,7 +376,7 @@
     /**
      * Inflate a new view hierarchy from the specified xml resource. Throws
      * {@link InflateException} if there is an error.
-     * 
+     *
      * @param resource ID for an XML layout resource to load (e.g.,
      *        <code>R.layout.main_page</code>)
      * @param root Optional view to be the parent of the generated hierarchy (if
@@ -407,7 +408,7 @@
      * reasons, view inflation relies heavily on pre-processing of XML files
      * that is done at build time. Therefore, it is not currently possible to
      * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
-     * 
+     *
      * @param parser XML dom node containing the description of the view
      *        hierarchy.
      * @param root Optional view to be the parent of the generated hierarchy (if
@@ -442,7 +443,7 @@
                 }
 
                 final String name = parser.getName();
-                
+
                 if (DEBUG) {
                     System.out.println("**************************");
                     System.out.println("Creating root view: "
@@ -528,17 +529,17 @@
      * Low-level function for instantiating a view by name. This attempts to
      * instantiate a view class of the given <var>name</var> found in this
      * LayoutInflater's ClassLoader.
-     * 
+     *
      * <p>
      * There are two things that can happen in an error case: either the
      * exception describing the error will be thrown, or a null will be
      * returned. You must deal with both possibilities -- the former will happen
      * the first time createView() is called for a class of a particular name,
      * the latter every time there-after for that class name.
-     * 
+     *
      * @param name The full name of the class to be instantiated.
      * @param attrs The XML attributes supplied for this instance.
-     * 
+     *
      * @return View The newly instantiated view, or null.
      */
     public final View createView(String name, String prefix, AttributeSet attrs)
@@ -551,7 +552,7 @@
                 // Class not found in the cache, see if it's real, and try to add it
                 clazz = mContext.getClassLoader().loadClass(
                         prefix != null ? (prefix + name) : name).asSubclass(View.class);
-                
+
                 if (mFilter != null && clazz != null) {
                     boolean allowed = mFilter.onLoadClass(clazz);
                     if (!allowed) {
@@ -569,7 +570,7 @@
                         // New class -- remember whether it is allowed
                         clazz = mContext.getClassLoader().loadClass(
                                 prefix != null ? (prefix + name) : name).asSubclass(View.class);
-                        
+
                         boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
                         mFilterMap.put(name, allowed);
                         if (!allowed) {
@@ -632,10 +633,10 @@
      * given the xml element name. Override it to handle custom view objects. If
      * you override this in your subclass be sure to call through to
      * super.onCreateView(name) for names you do not recognize.
-     * 
+     *
      * @param name The fully qualified class name of the View to be create.
      * @param attrs An AttributeSet of attributes to apply to the View.
-     * 
+     *
      * @return View The View created.
      */
     protected View onCreateView(String name, AttributeSet attrs)
@@ -679,7 +680,7 @@
             if (view == null && mPrivateFactory != null) {
                 view = mPrivateFactory.onCreateView(parent, name, mContext, attrs);
             }
-            
+
             if (view == null) {
                 if (-1 == name.indexOf('.')) {
                     view = onCreateView(parent, name, attrs);
@@ -726,7 +727,7 @@
             }
 
             final String name = parser.getName();
-            
+
             if (TAG_REQUEST_FOCUS.equals(name)) {
                 parseRequestFocus(parser, parent);
             } else if (TAG_INCLUDE.equals(name)) {
@@ -741,7 +742,7 @@
                 final ViewGroup viewGroup = (ViewGroup) parent;
                 final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
                 rInflate(parser, view, attrs, true);
-                viewGroup.addView(view, params);                
+                viewGroup.addView(view, params);
             } else {
                 final View view = createViewFromTag(parent, name, attrs);
                 final ViewGroup viewGroup = (ViewGroup) parent;
@@ -810,21 +811,14 @@
                         // We try to load the layout params set in the <include /> tag. If
                         // they don't exist, we will rely on the layout params set in the
                         // included XML file.
-                        // During a layoutparams generation, a runtime exception is thrown
-                        // if either layout_width or layout_height is missing. We catch
-                        // this exception and set localParams accordingly: true means we
-                        // successfully loaded layout params from the <include /> tag,
-                        // false means we need to rely on the included layout params.
-                        ViewGroup.LayoutParams params = null;
-                        try {
-                            params = group.generateLayoutParams(attrs);
-                        } catch (RuntimeException e) {
-                            params = group.generateLayoutParams(childAttrs);
-                        } finally {
-                            if (params != null) {
-                                view.setLayoutParams(params);
-                            }
-                        }
+                        TypedArray ta = getContext().obtainStyledAttributes(attrs,
+                                                            R.styleable.ViewGroup_Layout);
+                        boolean definesBothWidthAndHeight =
+                                ta.hasValue(R.styleable.ViewGroup_Layout_layout_width) &&
+                                ta.hasValue(R.styleable.ViewGroup_Layout_layout_height);
+                        AttributeSet attributes = definesBothWidthAndHeight ? attrs : childAttrs;
+                        view.setLayoutParams(group.generateLayoutParams(attributes));
+                        ta.recycle();
 
                         // Inflate all children.
                         rInflate(childParser, view, childAttrs, true);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6c1049b..b95e1bd 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3383,7 +3383,7 @@
         }
 
         if (child.isLayoutDirectionInherited()) {
-            child.resetResolvedLayoutDirection();
+            child.resetRtlProperties();
             child.resolveRtlPropertiesIfNeeded();
         }
 
@@ -5611,15 +5611,19 @@
         }
 
         /**
-         * Extracts the layout parameters from the supplied attributes.
+         * Extracts the <code>width</code> and <code>height</code> layout parameters
+         * from the supplied TypedArray, <code>a</code>, and assigns them
+         * to the appropriate fields. If, <code>a</code>, does not contain an
+         * entry for either attribute, the value, {@link ViewGroup.LayoutParams#WRAP_CONTENT},
+         * is used as a default.
          *
          * @param a the style attributes to extract the parameters from
          * @param widthAttr the identifier of the width attribute
          * @param heightAttr the identifier of the height attribute
          */
         protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
-            width = a.getLayoutDimension(widthAttr, "layout_width");
-            height = a.getLayoutDimension(heightAttr, "layout_height");
+            width = a.getLayoutDimension(widthAttr, WRAP_CONTENT);
+            height = a.getLayoutDimension(heightAttr, WRAP_CONTENT);
         }
 
         /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 91df4b5..b5a9090 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -887,11 +887,12 @@
         // Intersect with the bounds of the window to skip
         // updates that lie outside of the visible region
         final float appScale = mAttachInfo.mApplicationScale;
-        localDirty.intersect(0, 0,
-                (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
-
-        if (!mWillDrawSoon) {
+        if (localDirty.intersect(0, 0,
+                (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)) &&
+                !mWillDrawSoon) {
             scheduleTraversals();
+        } else {
+            localDirty.setEmpty();
         }
 
         return null;
@@ -1184,6 +1185,7 @@
             viewVisibilityChanged = false;
             mLastConfiguration.setTo(host.getResources().getConfiguration());
             mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
+            host.setLayoutDirection(mLastConfiguration.getLayoutDirection());
             host.dispatchAttachedToWindow(attachInfo, 0);
             mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets);
             host.fitSystemWindows(mFitSystemWindowsInsets);
@@ -2676,7 +2678,12 @@
             // the one in them which may be newer.
             config = mView.getResources().getConfiguration();
             if (force || mLastConfiguration.diff(config) != 0) {
+                final int lastLayoutDirection = mLastConfiguration.getLayoutDirection();
+                final int currentLayoutDirection = config.getLayoutDirection();
                 mLastConfiguration.setTo(config);
+                if (lastLayoutDirection != currentLayoutDirection) {
+                    mView.setLayoutDirection(currentLayoutDirection);
+                }
                 mView.dispatchConfigurationChanged(config);
             }
         }
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 75d1471..f0eb94f 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -1093,7 +1093,6 @@
             mPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NEEDED);
             mPopup.setListItemExpandMax(EXPAND_MAX);
         }
-        mPopup.setLayoutDirection(getLayoutDirection());
         mPopup.show();
         mPopup.getListView().setOverScrollMode(View.OVER_SCROLL_ALWAYS);
     }
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index e158776..45f30df 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -608,6 +608,12 @@
          */
         public int gravity = -1;
 
+        @Override
+        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
+            width = a.getLayoutDimension(widthAttr, MATCH_PARENT);
+            height = a.getLayoutDimension(heightAttr, MATCH_PARENT);
+        }
+
         /**
          * {@inheritDoc}
          */
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 1c81d11..3d6b69e 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -1021,8 +1021,6 @@
                 mDropDownList.setOnItemSelectedListener(mItemSelectedListener);
             }
 
-            mDropDownList.setLayoutDirection(mLayoutDirection);
-
             dropDownView = mDropDownList;
 
             View hintView = mPromptView;
@@ -1132,21 +1130,6 @@
     }
 
     /**
-     * Set the layout direction for this popup. Should be a resolved direction as the
-     * popup as no capacity to do the resolution on his own.
-     *
-     * @param layoutDirection One of {@link View#LAYOUT_DIRECTION_LTR},
-     *   {@link View#LAYOUT_DIRECTION_RTL},
-     *
-     */
-    public void setLayoutDirection(int layoutDirection) {
-        mLayoutDirection = layoutDirection;
-        if (mDropDownList != null) {
-            mDropDownList.setLayoutDirection(mLayoutDirection);
-        }
-    }
-
-    /**
      * <p>Wrapper class for a ListView. This wrapper can hijack the focus to
      * make sure the list uses the appropriate drawables and states when
      * displayed on screen within a drop down. The focus is never actually
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 78d05b0..42d63b2 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -297,33 +297,6 @@
         public LayoutParams(MarginLayoutParams source) {
             super(source);
         }
-
-        /**
-         * <p>Fixes the child's width to
-         * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and the child's
-         * height to  {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
-         * when not specified in the XML file.</p>
-         *
-         * @param a the styled attributes set
-         * @param widthAttr the width attribute to fetch
-         * @param heightAttr the height attribute to fetch
-         */
-        @Override
-        protected void setBaseAttributes(TypedArray a,
-                int widthAttr, int heightAttr) {
-
-            if (a.hasValue(widthAttr)) {
-                width = a.getLayoutDimension(widthAttr, "layout_width");
-            } else {
-                width = WRAP_CONTENT;
-            }
-            
-            if (a.hasValue(heightAttr)) {
-                height = a.getLayoutDimension(heightAttr, "layout_height");
-            } else {
-                height = WRAP_CONTENT;
-            }
-        }
     }
 
     /**
diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java
index 399b4fa..113299a 100644
--- a/core/java/android/widget/TableLayout.java
+++ b/core/java/android/widget/TableLayout.java
@@ -741,14 +741,9 @@
          * @param heightAttr the height attribute to fetch
          */
         @Override
-        protected void setBaseAttributes(TypedArray a,
-                int widthAttr, int heightAttr) {
+        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
             this.width = MATCH_PARENT;
-            if (a.hasValue(heightAttr)) {
-                this.height = a.getLayoutDimension(heightAttr, "layout_height");
-            } else {
-                this.height = WRAP_CONTENT;
-            }
+            this.height = a.getLayoutDimension(heightAttr, WRAP_CONTENT);
         }
     }
 
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index 68ffd73..3f8f9dae 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -505,19 +505,8 @@
 
         @Override
         protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
-            // We don't want to force users to specify a layout_width
-            if (a.hasValue(widthAttr)) {
-                width = a.getLayoutDimension(widthAttr, "layout_width");
-            } else {
-                width = MATCH_PARENT;
-            }
-
-            // We don't want to force users to specify a layout_height
-            if (a.hasValue(heightAttr)) {
-                height = a.getLayoutDimension(heightAttr, "layout_height");
-            } else {
-                height = WRAP_CONTENT;
-            }
+            width = a.getLayoutDimension(widthAttr, MATCH_PARENT);
+            height = a.getLayoutDimension(heightAttr, WRAP_CONTENT);
         }
     }
 
diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp
index 8333e00..f386905 100644
--- a/core/jni/android/graphics/YuvToJpegEncoder.cpp
+++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp
@@ -90,8 +90,9 @@
     // process 16 lines of Y and 8 lines of U/V each time.
     while (cinfo->next_scanline < cinfo->image_height) {
         //deitnerleave u and v
-        deinterleave(vuPlanar, uRows, vRows, cinfo->next_scanline, width);
+        deinterleave(vuPlanar, uRows, vRows, cinfo->next_scanline, width, height);
 
+        // Jpeg library ignores the rows whose indices are greater than height.
         for (int i = 0; i < 16; i++) {
             // y row
             y[i] = yPlanar + (cinfo->next_scanline + i) * fStrides[0];
@@ -112,8 +113,10 @@
 }
 
 void Yuv420SpToJpegEncoder::deinterleave(uint8_t* vuPlanar, uint8_t* uRows,
-        uint8_t* vRows, int rowIndex, int width) {
-    for (int row = 0; row < 8; ++row) {
+        uint8_t* vRows, int rowIndex, int width, int height) {
+    int numRows = (height - rowIndex) / 2;
+    if (numRows > 8) numRows = 8;
+    for (int row = 0; row < numRows; ++row) {
         int offset = ((rowIndex >> 1) + row) * fStrides[1];
         uint8_t* vu = vuPlanar + offset;
         for (int i = 0; i < (width >> 1); ++i) {
@@ -164,6 +167,7 @@
     while (cinfo->next_scanline < cinfo->image_height) {
         deinterleave(yuvOffset, yRows, uRows, vRows, cinfo->next_scanline, width, height);
 
+        // Jpeg library ignores the rows whose indices are greater than height.
         for (int i = 0; i < 16; i++) {
             // y row
             y[i] = yRows + i * width;
@@ -185,7 +189,9 @@
 
 void Yuv422IToJpegEncoder::deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows,
         uint8_t* vRows, int rowIndex, int width, int height) {
-    for (int row = 0; row < 16; ++row) {
+    int numRows = height - rowIndex;
+    if (numRows > 16) numRows = 16;
+    for (int row = 0; row < numRows; ++row) {
         uint8_t* yuvSeg = yuv + (rowIndex + row) * fStrides[0];
         for (int i = 0; i < (width >> 1); ++i) {
             int indexY = row * width + (i << 1);
diff --git a/core/jni/android/graphics/YuvToJpegEncoder.h b/core/jni/android/graphics/YuvToJpegEncoder.h
index 97106ce..0d418ed 100644
--- a/core/jni/android/graphics/YuvToJpegEncoder.h
+++ b/core/jni/android/graphics/YuvToJpegEncoder.h
@@ -55,7 +55,7 @@
      void deinterleaveYuv(uint8_t* yuv, int width, int height,
             uint8_t*& yPlanar, uint8_t*& uPlanar, uint8_t*& vPlanar);
      void deinterleave(uint8_t* vuPlanar, uint8_t* uRows, uint8_t* vRows,
-             int rowIndex, int width);
+             int rowIndex, int width, int height);
      void compress(jpeg_compress_struct* cinfo, uint8_t* yuv, int* offsets);
 };
 
diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
index 05f8b39..1bbc7df 100644
--- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml
+++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml
@@ -72,5 +72,6 @@
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
 </manifest>
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 7fb86ee..10d112a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -74,8 +74,6 @@
     INIT_LOGD("    Maximum texture dimension is %d pixels", mMaxTextureSize);
 
     mDebugEnabled = readDebugLevel() & kDebugCaches;
-
-    mHasNPot = false; //Caches::getInstance().extensions.hasNPot();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -219,11 +217,15 @@
         return;
     }
 
+    // We could also enable mipmapping if both bitmap dimensions are powers
+    // of 2 but we'd have to deal with size changes. Let's keep this simple
+    const bool canMipMap = Caches::getInstance().extensions.hasNPot();
+
     // If the texture had mipmap enabled but not anymore,
     // force a glTexImage2D to discard the mipmap levels
     const bool resize = !regenerate || bitmap->width() != int(texture->width) ||
             bitmap->height() != int(texture->height) ||
-            (regenerate && mHasNPot && texture->mipMap && !bitmap->hasHardwareMipMap());
+            (regenerate && canMipMap && texture->mipMap && !bitmap->hasHardwareMipMap());
 
     if (!regenerate) {
         glGenTextures(1, &texture->id);
@@ -267,7 +269,7 @@
         break;
     }
 
-    if (mHasNPot) {
+    if (canMipMap) {
         texture->mipMap = bitmap->hasHardwareMipMap();
         if (texture->mipMap) {
             glGenerateMipmap(GL_TEXTURE_2D);
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 8e19092..31a2e3d 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -138,7 +138,6 @@
 
     float mFlushRate;
 
-    bool mHasNPot;
     bool mDebugEnabled;
 
     Vector<SkBitmap*> mGarbage;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index 9b1098e..3464924 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -114,10 +114,9 @@
             mProcMemWriter = new BufferedWriter(new FileWriter
                     (new File(MEDIA_PROCMEM_OUTPUT), true));
             mProcMemWriter.write(this.getName() + "\n");
-            mMemWriter = new BufferedWriter(new FileWriter
-                    (new File(MEDIA_MEMORY_OUTPUT), true));
         }
-
+        mMemWriter = new BufferedWriter(new FileWriter
+                (new File(MEDIA_MEMORY_OUTPUT), true));
     }
 
     @Override
@@ -126,10 +125,10 @@
             MediaTestUtil.getNativeHeapDump(this.getName() + "_after");
 
         if (MediaFrameworkPerfTestRunner.mGetProcmem) {
-            mMemWriter.write("\n");
             mProcMemWriter.close();
-            mMemWriter.close();
         }
+        mMemWriter.write("\n");
+        mMemWriter.close();
         super.tearDown();
     }
 
diff --git a/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml b/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml
index 2d1bda4..59544f4 100644
--- a/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml
+++ b/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml
@@ -170,7 +170,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:src="@drawable/ic_sysbar_quicksettings"
-                    android:contentDescription="@string/accessibility_settings_button"
+                    android:contentDescription="@string/accessibility_desc_quick_settings"
                     />
 
                 <ImageView
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index c832fb8..a857eba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -21,6 +21,8 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.MotionEvent;
 import android.view.View;
 
 import com.android.systemui.R;
@@ -31,11 +33,18 @@
     Drawable mHandleBar;
     float mHandleBarHeight;
     View mHandleView;
+    int mFingers;
+    PhoneStatusBar mStatusBar;
+    private boolean mFlipped;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
+    public void setStatusBar(PhoneStatusBar bar) {
+        mStatusBar = bar;
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
@@ -79,4 +88,35 @@
         mHandleBar.draw(canvas);
         canvas.translate(0, -off);
     }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if (PhoneStatusBar.SETTINGS_DRAG_SHORTCUT && mStatusBar.mHasFlipSettings) {
+            switch (event.getActionMasked()) {
+                case MotionEvent.ACTION_DOWN:
+                    mFlipped = false;
+                    break;
+                case MotionEvent.ACTION_POINTER_DOWN:
+                    if (!mFlipped) {
+                        float miny = event.getY(0);
+                        float maxy = miny;
+                        for (int i=1; i<event.getPointerCount(); i++) {
+                            final float y = event.getY(i);
+                            if (y < miny) miny = y;
+                            if (y > maxy) maxy = y;
+                        }
+                        if (maxy - miny < mHandleBarHeight) {
+                            if (getMeasuredHeight() < mHandleBarHeight) {
+                                mStatusBar.switchToSettings();
+                            } else {
+                                mStatusBar.flipToSettings();
+                            }
+                            mFlipped = true;
+                        }
+                    }
+                    break;
+            }
+        }
+        return mHandleView.dispatchTouchEvent(event);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index d0fc340..c9a137c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -102,7 +102,7 @@
             startOpeningPanel(panel);
         }
         final boolean result = mTouchingPanel != null
-                ? mTouchingPanel.getHandle().dispatchTouchEvent(event)
+                ? mTouchingPanel.onTouchEvent(event)
                 : true;
         return result;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index b2bca56..6184e30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -250,6 +250,7 @@
                         case MotionEvent.ACTION_DOWN:
                             mTracking = true;
                             mHandleView.setPressed(true);
+                            postInvalidate(); // catch the press state change
                             mInitialTouchY = y;
                             mVelocityTracker = VelocityTracker.obtain();
                             trackMovement(event);
@@ -283,6 +284,7 @@
                             mFinalTouchY = y;
                             mTracking = false;
                             mHandleView.setPressed(false);
+                            postInvalidate(); // catch the press state change
                             mBar.onTrackingStopped(PanelView.this);
                             trackMovement(event);
 
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 24f76bb..27a4db5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -110,6 +110,8 @@
 
     public static final boolean ENABLE_NOTIFICATION_PANEL_CLING = false;
 
+    public static final boolean SETTINGS_DRAG_SHORTCUT = true;
+
     // additional instrumentation for testing purposes; intended to be left on during development
     public static final boolean CHATTY = DEBUG;
 
@@ -180,7 +182,7 @@
     View mMoreIcon;
 
     // expanded notifications
-    PanelView mNotificationPanel; // the sliding/resizing panel within the notification window
+    NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
     ScrollView mScrollView;
     View mExpandedContents;
     int mNotificationPanelGravity;
@@ -363,7 +365,8 @@
         PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
         mStatusBarView.setPanelHolder(holder);
 
-        mNotificationPanel = (PanelView) mStatusBarWindow.findViewById(R.id.notification_panel);
+        mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(R.id.notification_panel);
+        mNotificationPanel.setStatusBar(this);
         mNotificationPanelIsFullScreenWidth =
             (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
 
@@ -1079,7 +1082,13 @@
                     + " any=" + any + " clearable=" + clearable);
         }
 
-        if (mClearButton.isShown()) {
+        if (mHasFlipSettings 
+                && mFlipSettingsView != null 
+                && mFlipSettingsView.getVisibility() == View.VISIBLE
+                && mScrollView.getVisibility() != View.VISIBLE) {
+            // the flip settings panel is unequivocally showing; we should not be shown
+            mClearButton.setVisibility(View.INVISIBLE);
+        } else if (mClearButton.isShown()) {
             if (clearable != (mClearButton.getAlpha() == 1.0f)) {
                 ObjectAnimator clearAnimation = ObjectAnimator.ofFloat(
                         mClearButton, "alpha", clearable ? 1.0f : 0.0f).setDuration(250);
@@ -1280,6 +1289,10 @@
         }
     }
 
+    public Handler getHandler() {
+        return mHandler;
+    }
+
     View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
         public void onFocusChange(View v, boolean hasFocus) {
             // Because 'v' is a ViewGroup, all its children will be (un)selected
@@ -1294,18 +1307,6 @@
             return;
         }
 
-        if (mHasFlipSettings && !mExpandedVisible) {
-            // reset things to their proper state
-            mScrollView.setScaleX(1f);
-            mScrollView.setVisibility(View.VISIBLE);
-            mSettingsButton.setAlpha(1f);
-            mSettingsButton.setVisibility(View.VISIBLE);
-            mNotificationPanel.setVisibility(View.GONE);
-            mFlipSettingsView.setVisibility(View.GONE);
-            mNotificationButton.setVisibility(View.GONE);
-            setAreThereNotifications(); // show the clear button
-        }
-
         mExpandedVisible = true;
         mPile.setLayoutTransitionsEnabled(true);
         if (mNavigationBarView != null)
@@ -1415,51 +1416,53 @@
         }
 
         mNotificationPanel.expand();
-        if (mHasFlipSettings) {
-            if (mScrollView.getVisibility() != View.VISIBLE) {
-                if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
-                if (mScrollViewAnim != null) mScrollViewAnim.cancel();
-                if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
-                if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
-                if (mClearButtonAnim != null) mClearButtonAnim.cancel();
-
-                mScrollView.setVisibility(View.VISIBLE);
-                mScrollViewAnim = start(
-                    startDelay(FLIP_DURATION_OUT,
-                        interpolator(mDecelerateInterpolator,
-                            ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 0f, 1f)
-                                .setDuration(FLIP_DURATION_IN)
-                            )));
-                mFlipSettingsViewAnim = start(
-                    setVisibilityWhenDone(
-                        interpolator(mAccelerateInterpolator,
-                                ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 1f, 0f)
-                                )
-                            .setDuration(FLIP_DURATION_OUT),
-                        mFlipSettingsView, View.INVISIBLE));
-                mNotificationButtonAnim = start(
-                    setVisibilityWhenDone(
-                        ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f)
-                            .setDuration(FLIP_DURATION),
-                        mNotificationButton, View.INVISIBLE));
-                mSettingsButton.setVisibility(View.VISIBLE);
-                mSettingsButtonAnim = start(
-                    ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f)
-                        .setDuration(FLIP_DURATION));
-                mClearButton.setVisibility(View.VISIBLE);
-                mClearButton.setAlpha(0f);
-                setAreThereNotifications(); // this will show/hide the button as necessary
-                mNotificationPanel.postDelayed(new Runnable() {
-                    public void run() {
-                        updateCarrierLabelVisibility(false);
-                    }
-                }, FLIP_DURATION - 150);
-            }
+        if (mHasFlipSettings && mScrollView.getVisibility() != View.VISIBLE) {
+            flipToNotifications();
         }
 
         if (false) postStartTracing();
     }
 
+    public void flipToNotifications() {
+        if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
+        if (mScrollViewAnim != null) mScrollViewAnim.cancel();
+        if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
+        if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
+        if (mClearButtonAnim != null) mClearButtonAnim.cancel();
+
+        mScrollView.setVisibility(View.VISIBLE);
+        mScrollViewAnim = start(
+            startDelay(FLIP_DURATION_OUT,
+                interpolator(mDecelerateInterpolator,
+                    ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 0f, 1f)
+                        .setDuration(FLIP_DURATION_IN)
+                    )));
+        mFlipSettingsViewAnim = start(
+            setVisibilityWhenDone(
+                interpolator(mAccelerateInterpolator,
+                        ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 1f, 0f)
+                        )
+                    .setDuration(FLIP_DURATION_OUT),
+                mFlipSettingsView, View.INVISIBLE));
+        mNotificationButtonAnim = start(
+            setVisibilityWhenDone(
+                ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f)
+                    .setDuration(FLIP_DURATION),
+                mNotificationButton, View.INVISIBLE));
+        mSettingsButton.setVisibility(View.VISIBLE);
+        mSettingsButtonAnim = start(
+            ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f)
+                .setDuration(FLIP_DURATION));
+        mClearButton.setVisibility(View.VISIBLE);
+        mClearButton.setAlpha(0f);
+        setAreThereNotifications(); // this will show/hide the button as necessary
+        mNotificationPanel.postDelayed(new Runnable() {
+            public void run() {
+                updateCarrierLabelVisibility(false);
+            }
+        }, FLIP_DURATION - 150);
+    }
+
     @Override
     public void animateExpandSettingsPanel() {
         if (SPEW) Slog.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
@@ -1470,46 +1473,7 @@
         if (mHasFlipSettings) {
             mNotificationPanel.expand();
             if (mFlipSettingsView.getVisibility() != View.VISIBLE) {
-                if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
-                if (mScrollViewAnim != null) mScrollViewAnim.cancel();
-                if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
-                if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
-                if (mClearButtonAnim != null) mClearButtonAnim.cancel();
-
-                mFlipSettingsView.setVisibility(View.VISIBLE);
-                mFlipSettingsView.setScaleX(0f);
-                mFlipSettingsViewAnim = start(
-                    startDelay(FLIP_DURATION_OUT,
-                        interpolator(mDecelerateInterpolator,
-                            ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 0f, 1f)
-                                .setDuration(FLIP_DURATION_IN)
-                            )));
-                mScrollViewAnim = start(
-                    setVisibilityWhenDone(
-                        interpolator(mAccelerateInterpolator,
-                                ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 1f, 0f)
-                                )
-                            .setDuration(FLIP_DURATION_OUT), 
-                        mScrollView, View.INVISIBLE));
-                mSettingsButtonAnim = start(
-                    setVisibilityWhenDone(
-                        ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f)
-                            .setDuration(FLIP_DURATION),
-                            mScrollView, View.INVISIBLE));
-                mNotificationButton.setVisibility(View.VISIBLE);
-                mNotificationButtonAnim = start(
-                    ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f)
-                        .setDuration(FLIP_DURATION));
-                mClearButtonAnim = start(
-                    setVisibilityWhenDone(
-                        ObjectAnimator.ofFloat(mClearButton, View.ALPHA, 0f)
-                        .setDuration(FLIP_DURATION),
-                        mClearButton, View.INVISIBLE));
-                mNotificationPanel.postDelayed(new Runnable() {
-                    public void run() {
-                        updateCarrierLabelVisibility(false);
-                    }
-                }, FLIP_DURATION - 150);
+                flipToSettings();
             }
         } else if (mSettingsPanel != null) {
             mSettingsPanel.expand();
@@ -1518,10 +1482,77 @@
         if (false) postStartTracing();
     }
 
+    public void switchToSettings() {
+        mFlipSettingsView.setScaleX(1f);
+        mFlipSettingsView.setVisibility(View.VISIBLE);
+        mSettingsButton.setVisibility(View.GONE);
+        mScrollView.setVisibility(View.GONE);
+        mNotificationButton.setVisibility(View.VISIBLE);
+        mNotificationButton.setAlpha(1f);
+        mClearButton.setVisibility(View.GONE);
+    }
+
+    public void flipToSettings() {
+        if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
+        if (mScrollViewAnim != null) mScrollViewAnim.cancel();
+        if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
+        if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
+        if (mClearButtonAnim != null) mClearButtonAnim.cancel();
+
+        mFlipSettingsView.setVisibility(View.VISIBLE);
+        mFlipSettingsView.setScaleX(0f);
+        mFlipSettingsViewAnim = start(
+            startDelay(FLIP_DURATION_OUT,
+                interpolator(mDecelerateInterpolator,
+                    ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 0f, 1f)
+                        .setDuration(FLIP_DURATION_IN)
+                    )));
+        mScrollViewAnim = start(
+            setVisibilityWhenDone(
+                interpolator(mAccelerateInterpolator,
+                        ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 1f, 0f)
+                        )
+                    .setDuration(FLIP_DURATION_OUT), 
+                mScrollView, View.INVISIBLE));
+        mSettingsButtonAnim = start(
+            setVisibilityWhenDone(
+                ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f)
+                    .setDuration(FLIP_DURATION),
+                    mScrollView, View.INVISIBLE));
+        mNotificationButton.setVisibility(View.VISIBLE);
+        mNotificationButtonAnim = start(
+            ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f)
+                .setDuration(FLIP_DURATION));
+        mClearButtonAnim = start(
+            setVisibilityWhenDone(
+                ObjectAnimator.ofFloat(mClearButton, View.ALPHA, 0f)
+                .setDuration(FLIP_DURATION),
+                mClearButton, View.INVISIBLE));
+        mNotificationPanel.postDelayed(new Runnable() {
+            public void run() {
+                updateCarrierLabelVisibility(false);
+            }
+        }, FLIP_DURATION - 150);
+    }
+
+    public void flipPanels() {
+        if (mHasFlipSettings) {
+            if (mFlipSettingsView.getVisibility() != View.VISIBLE) {
+                flipToSettings();
+            } else {
+                flipToNotifications();
+            }
+        }
+    }
+
     public void animateCollapseQuickSettings() {
         mStatusBarView.collapseAllPanels(true);
     }
 
+    void makeExpandedInvisibleSoon() {
+        mHandler.postDelayed(new Runnable() { public void run() { makeExpandedInvisible(); }}, 50);
+    }
+
     void makeExpandedInvisible() {
         if (SPEW) Slog.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
                 + " mExpandedVisible=" + mExpandedVisible);
@@ -1533,6 +1564,18 @@
         // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
         mStatusBarView.collapseAllPanels(/*animate=*/ false);
 
+        if (mHasFlipSettings) {
+            // reset things to their proper state
+            mScrollView.setScaleX(1f);
+            mScrollView.setVisibility(View.VISIBLE);
+            mSettingsButton.setAlpha(1f);
+            mSettingsButton.setVisibility(View.VISIBLE);
+            mNotificationPanel.setVisibility(View.GONE);
+            mFlipSettingsView.setVisibility(View.GONE);
+            mNotificationButton.setVisibility(View.GONE);
+            setAreThereNotifications(); // show the clear button
+        }
+
         mExpandedVisible = false;
         mPile.setLayoutTransitionsEnabled(false);
         if (mNavigationBarView != null)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index a27df94..af6a149 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -154,7 +154,8 @@
     @Override
     public void onAllPanelsCollapsed() {
         super.onAllPanelsCollapsed();
-        mBar.makeExpandedInvisible();
+        // give animations time to settle
+        mBar.makeExpandedInvisibleSoon();
         mFadingPanel = null;
         mLastFullyOpenedPanel = null;
     }
@@ -205,6 +206,20 @@
             }
         }
 
+        // fade out the panel as it gets buried into the status bar to avoid overdrawing the
+        // status bar on the last frame of a close animation
+        final int H = mBar.getStatusBarHeight();
+        final float ph = panel.getExpandedHeight() + panel.getPaddingBottom();
+        float alpha = 1f;
+        if (ph < 2*H) {
+            if (ph < H) alpha = 0f;
+            else alpha = (ph - H) / H;
+            alpha = alpha * alpha; // get there faster
+        }
+        if (panel.getAlpha() != alpha) {
+            panel.setAlpha(alpha);
+        }
+
         mBar.updateCarrierLabelVisibility(false);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index 56b9c3a..58e3a57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -145,6 +145,7 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
         filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
+        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
         filter.addAction(Intent.ACTION_USER_SWITCHED);
         mContext.registerReceiver(mReceiver, filter);
 
@@ -854,6 +855,11 @@
                         DisplayManager.EXTRA_WIFI_DISPLAY_STATUS);
                 mWifiDisplayStatus = status;
                 applyWifiDisplayStatus();
+            } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
+                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+                        BluetoothAdapter.ERROR);
+                mBluetoothState.enabled = (state == BluetoothAdapter.STATE_ON);
+                applyBluetoothStatus();
             } else if (BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
                 int status = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
                         BluetoothAdapter.STATE_DISCONNECTED);
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 9ea47f9..c215f1b 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -290,7 +290,7 @@
                                     }
                                 });
                         AlertDialog dialog = builder.create();
-                        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+                        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
                         dialog.show();
                     }
 
@@ -339,7 +339,7 @@
                         return mAdapter.getItem(position).onLongPress();
                     }
         });
-        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
 
         dialog.setOnDismissListener(this);
 
@@ -390,11 +390,7 @@
         refreshSilentMode();
         mAirplaneModeOn.updateState(mAirplaneState);
         mAdapter.notifyDataSetChanged();
-        if (mKeyguardShowing) {
-            mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
-        } else {
-            mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
-        }
+        mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
         if (SHOW_SILENT_TOGGLE) {
             IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
             mContext.registerReceiver(mRingerModeReceiver, filter);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 72cb1dd..41d67bc 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2867,8 +2867,6 @@
             mDecor = generateDecor();
             mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
             mDecor.setIsRootNamespace(true);
-            mDecor.setLayoutDirection(
-                    getContext().getResources().getConfiguration().getLayoutDirection());
             if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                 mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
             }
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index 93ab4a4..0b0d7388e 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -178,6 +178,7 @@
 
     private static final String P2P_GO_NEG_SUCCESS_STR = "P2P-GO-NEG-SUCCESS";
 
+    /* P2P-GO-NEG-FAILURE status=x */
     private static final String P2P_GO_NEG_FAILURE_STR = "P2P-GO-NEG-FAILURE";
 
     private static final String P2P_GROUP_FORMATION_SUCCESS_STR =
@@ -566,6 +567,26 @@
                     WifiManager.ERROR, 0));
         }
 
+        /* <event> status=<err> and the special case of <event> reason=FREQ_CONFLICT */
+        private P2pStatus p2pError(String dataString) {
+            P2pStatus err = P2pStatus.UNKNOWN;
+            String[] tokens = dataString.split(" ");
+            if (tokens.length < 2) return err;
+            String[] nameValue = tokens[1].split("=");
+            if (nameValue.length != 2) return err;
+
+            /* Handle the special case of reason=FREQ+CONFLICT */
+            if (nameValue[1].equals("FREQ_CONFLICT")) {
+                return P2pStatus.NO_COMMON_CHANNEL;
+            }
+            try {
+                err = P2pStatus.valueOf(Integer.parseInt(nameValue[1]));
+            } catch (NumberFormatException e) {
+                e.printStackTrace();
+            }
+            return err;
+        }
+
         /**
          * Handle p2p events
          */
@@ -582,11 +603,11 @@
             } else if (dataString.startsWith(P2P_GO_NEG_SUCCESS_STR)) {
                 mStateMachine.sendMessage(P2P_GO_NEGOTIATION_SUCCESS_EVENT);
             } else if (dataString.startsWith(P2P_GO_NEG_FAILURE_STR)) {
-                mStateMachine.sendMessage(P2P_GO_NEGOTIATION_FAILURE_EVENT);
+                mStateMachine.sendMessage(P2P_GO_NEGOTIATION_FAILURE_EVENT, p2pError(dataString));
             } else if (dataString.startsWith(P2P_GROUP_FORMATION_SUCCESS_STR)) {
                 mStateMachine.sendMessage(P2P_GROUP_FORMATION_SUCCESS_EVENT);
             } else if (dataString.startsWith(P2P_GROUP_FORMATION_FAILURE_STR)) {
-                mStateMachine.sendMessage(P2P_GROUP_FORMATION_FAILURE_EVENT);
+                mStateMachine.sendMessage(P2P_GROUP_FORMATION_FAILURE_EVENT, p2pError(dataString));
             } else if (dataString.startsWith(P2P_GROUP_STARTED_STR)) {
                 mStateMachine.sendMessage(P2P_GROUP_STARTED_EVENT, new WifiP2pGroup(dataString));
             } else if (dataString.startsWith(P2P_GROUP_REMOVED_STR)) {
@@ -595,17 +616,7 @@
                 mStateMachine.sendMessage(P2P_INVITATION_RECEIVED_EVENT,
                         new WifiP2pGroup(dataString));
             } else if (dataString.startsWith(P2P_INVITATION_RESULT_STR)) {
-                String[] tokens = dataString.split(" ");
-                if (tokens.length != 2) return;
-                String[] nameValue = tokens[1].split("=");
-                if (nameValue.length != 2) return;
-                P2pStatus err = P2pStatus.UNKNOWN;
-                try {
-                    err = P2pStatus.valueOf(Integer.parseInt(nameValue[1]));
-                } catch (NumberFormatException e) {
-                    e.printStackTrace();
-                }
-                mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, err);
+                mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, p2pError(dataString));
             } else if (dataString.startsWith(P2P_PROV_DISC_PBC_REQ_STR)) {
                 mStateMachine.sendMessage(P2P_PROV_DISC_PBC_REQ_EVENT,
                         new WifiP2pProvDiscEvent(dataString));
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index db539e4..196bf2e 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -114,6 +114,7 @@
 
     private final boolean mP2pSupported;
     private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
+    private boolean mTemporarilyDisconnectWifi = false;
     private final String mPrimaryDeviceType;
 
     /* Scan results handling */
@@ -2017,6 +2018,10 @@
                     NetworkInfo info = (NetworkInfo) message.obj;
                     mP2pConnected.set(info.isConnected());
                     break;
+                case WifiP2pService.DISCONNECT_WIFI_REQUEST:
+                    mTemporarilyDisconnectWifi = (message.arg1 == 1);
+                    replyToMessage(message, WifiP2pService.DISCONNECT_WIFI_RESPONSE);
+                    break;
                 default:
                     loge("Error! unhandled message" + message);
                     break;
@@ -3030,6 +3035,15 @@
                         transitionTo(mDisconnectedState);
                     }
                     break;
+                case WifiP2pService.DISCONNECT_WIFI_REQUEST:
+                    if (message.arg1 == 1) {
+                        mWifiNative.disconnect();
+                        mTemporarilyDisconnectWifi = true;
+                    } else {
+                        mWifiNative.reconnect();
+                        mTemporarilyDisconnectWifi = false;
+                    }
+                    break;
                     /* Do a redundant disconnect without transition */
                 case CMD_DISCONNECT:
                     mWifiNative.disconnect();
@@ -3159,6 +3173,13 @@
                     mWifiNative.disconnect();
                     transitionTo(mDisconnectingState);
                     break;
+                case WifiP2pService.DISCONNECT_WIFI_REQUEST:
+                    if (message.arg1 == 1) {
+                        mWifiNative.disconnect();
+                        mTemporarilyDisconnectWifi = true;
+                        transitionTo(mDisconnectingState);
+                    }
+                    break;
                 case CMD_SET_SCAN_MODE:
                     if (message.arg1 == SCAN_ONLY_MODE) {
                         sendMessage(CMD_DISCONNECT);
@@ -3465,6 +3486,13 @@
             if (DBG) log(getName() + "\n");
             EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
 
+            // We dont scan frequently if this is a temporary disconnect
+            // due to p2p
+            if (mTemporarilyDisconnectWifi) {
+                mWifiP2pChannel.sendMessage(WifiP2pService.DISCONNECT_WIFI_RESPONSE);
+                return;
+            }
+
             mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
                     Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
                     mDefaultFrameworkScanIntervalMs);
@@ -3579,6 +3607,12 @@
                         sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
                                     ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
                     }
+                case CMD_RECONNECT:
+                case CMD_REASSOCIATE:
+                    // Drop a third party reconnect/reassociate if we are
+                    // tempoarily disconnected for p2p
+                    if (mTemporarilyDisconnectWifi) ret = NOT_HANDLED;
+                    break;
                 default:
                     ret = NOT_HANDLED;
             }
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 9c727f9..c8f0712 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -556,8 +556,8 @@
                             mLinkProperties = (LinkProperties) intent.getParcelableExtra(
                                     WifiManager.EXTRA_LINK_PROPERTIES);
                             if (mPoorNetworkDetectionEnabled) {
-                                if (mWifiInfo == null) {
-                                    if (DBG) logd("Ignoring link verification, mWifiInfo is NULL");
+                                if (mWifiInfo == null || mCurrentBssid == null) {
+                                    loge("Ignore, wifiinfo " + mWifiInfo +" bssid " + mCurrentBssid);
                                     sendLinkStatusNotification(true);
                                 } else {
                                     transitionTo(mVerifyingLinkState);
@@ -726,7 +726,7 @@
         }
 
         private void handleRssiChange() {
-            if (mCurrentSignalLevel <= LINK_MONITOR_LEVEL_THRESHOLD) {
+            if (mCurrentSignalLevel <= LINK_MONITOR_LEVEL_THRESHOLD && mCurrentBssid != null) {
                 transitionTo(mLinkMonitoringState);
             } else {
                 // stay here
@@ -920,11 +920,15 @@
         if (DBG) logd("########################################");
         if (isGood) {
             mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
-            mCurrentBssid.mLastTimeGood = SystemClock.elapsedRealtime();
-            logd("Good link notification is sent");
+            if (mCurrentBssid != null) {
+                mCurrentBssid.mLastTimeGood = SystemClock.elapsedRealtime();
+            }
+            if (DBG) logd("Good link notification is sent");
         } else {
             mWsmChannel.sendMessage(POOR_LINK_DETECTED);
-            mCurrentBssid.mLastTimePoor = SystemClock.elapsedRealtime();
+            if (mCurrentBssid != null) {
+                mCurrentBssid.mLastTimePoor = SystemClock.elapsedRealtime();
+            }
             logd("Poor link notification is sent");
         }
     }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 70baf13..4b90901 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -149,9 +149,28 @@
     private static final int PEER_CONNECTION_USER_ACCEPT    =   BASE + 2;
     /* User rejected a peer request */
     private static final int PEER_CONNECTION_USER_REJECT    =   BASE + 3;
+    /* User wants to disconnect wifi in favour of p2p */
+    private static final int DROP_WIFI_USER_ACCEPT          =   BASE + 4;
+    /* User wants to keep his wifi connection and drop p2p */
+    private static final int DROP_WIFI_USER_REJECT          =   BASE + 5;
+
 
     /* Commands to the WifiStateMachine */
-    public static final int P2P_CONNECTION_CHANGED         =   BASE + 11;
+    public static final int P2P_CONNECTION_CHANGED          =   BASE + 11;
+
+    /* These commands are used to tempoarily disconnect wifi when we detect
+     * a frequency conflict which would make it impossible to have with p2p
+     * and wifi active at the same time.
+     *
+     * If the user chooses to disable wifi tempoarily, we keep wifi disconnected
+     * until the p2p connection is done and terminated at which point we will
+     * bring back wifi up
+     *
+     * DISCONNECT_WIFI_REQUEST
+     *      msg.arg1 = 1 enables temporary disconnect and 0 disables it.
+     */
+    public static final int DISCONNECT_WIFI_REQUEST         =   BASE + 12;
+    public static final int DISCONNECT_WIFI_RESPONSE        =   BASE + 13;
 
     private final boolean mP2pSupported;
 
@@ -172,6 +191,8 @@
 
     private NetworkInfo mNetworkInfo;
 
+    private boolean mTempoarilyDisconnectedWifi = false;
+
     /* The transaction Id of service discovery request */
     private byte mServiceTransactionId = 0;
 
@@ -222,7 +243,7 @@
         PREVIOUS_PROTOCOL_ERROR,
 
         /* There is no common channels the both devices can use. */
-        NO_COMMON_CHANNE,
+        NO_COMMON_CHANNEL,
 
         /* Unknown p2p group. For example, Device A tries to invoke the previous persistent group,
          *  but device B has removed the specified credential already. */
@@ -257,7 +278,7 @@
             case 6:
                 return PREVIOUS_PROTOCOL_ERROR;
             case 7:
-                return NO_COMMON_CHANNE;
+                return NO_COMMON_CHANNEL;
             case 8:
                 return UNKNOWN_P2P_GROUP;
             case 9:
@@ -346,6 +367,7 @@
                 = new UserAuthorizingInvitationState();
         private ProvisionDiscoveryState mProvisionDiscoveryState = new ProvisionDiscoveryState();
         private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState();
+        private FrequencyConflictState mFrequencyConflictState =new FrequencyConflictState();
 
         private GroupCreatedState mGroupCreatedState = new GroupCreatedState();
         private UserAuthorizingJoinState mUserAuthorizingJoinState = new UserAuthorizingJoinState();
@@ -400,6 +422,7 @@
                         addState(mUserAuthorizingInvitationState, mGroupCreatingState);
                         addState(mProvisionDiscoveryState, mGroupCreatingState);
                         addState(mGroupNegotiationState, mGroupCreatingState);
+                        addState(mFrequencyConflictState, mGroupCreatingState);
                     addState(mGroupCreatedState, mP2pEnabledState);
                         addState(mUserAuthorizingJoinState, mGroupCreatedState);
                         addState(mOngoingGroupRemovalState, mGroupCreatedState);
@@ -555,6 +578,9 @@
                 case WifiStateMachine.CMD_DISABLE_P2P:
                 case PEER_CONNECTION_USER_ACCEPT:
                 case PEER_CONNECTION_USER_REJECT:
+                case DISCONNECT_WIFI_RESPONSE:
+                case DROP_WIFI_USER_ACCEPT:
+                case DROP_WIFI_USER_REJECT:
                 case GROUP_CREATING_TIMED_OUT:
                 case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
                 case DhcpStateMachine.CMD_POST_DHCP_ACTION:
@@ -1027,20 +1053,7 @@
                        // remain at this state.
                    }
                    break;
-                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
-                    mGroup = (WifiP2pGroup) message.obj;
-                    if (DBG) logd(getName() + " group started");
-
-                    if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
-                        // This is an invocation case.
-                        mAutonomousGroup = false;
-                        deferMessage(message);
-                        transitionTo(mGroupNegotiationState);
-                    } else {
-                        return NOT_HANDLED;
-                    }
-                    break;
-               default:
+              default:
                    return NOT_HANDLED;
             }
             return HANDLED;
@@ -1074,6 +1087,10 @@
                     // mSavedPeerConfig can be empty
                     if (mSavedPeerConfig != null &&
                             !mSavedPeerConfig.deviceAddress.equals(device.deviceAddress)) {
+                        if (DBG) {
+                            logd("mSavedPeerConfig " + mSavedPeerConfig.deviceAddress +
+                                "device " + device.deviceAddress);
+                        }
                         // Do the regular device lost handling
                         ret = NOT_HANDLED;
                         break;
@@ -1269,6 +1286,12 @@
                     transitionTo(mGroupCreatedState);
                     break;
                 case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
+                    P2pStatus status = (P2pStatus) message.obj;
+                    if (status == P2pStatus.NO_COMMON_CHANNEL) {
+                        transitionTo(mFrequencyConflictState);
+                        break;
+                    }
+                    /* continue with group removal handling */
                 case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
                     if (DBG) logd(getName() + " go failure");
                     handleGroupCreationFailure();
@@ -1278,9 +1301,14 @@
                 // a group removed event. Flushing things at group formation
                 // failure causes supplicant issues. Ignore right now.
                 case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
+                    status = (P2pStatus) message.obj;
+                    if (status == P2pStatus.NO_COMMON_CHANNEL) {
+                        transitionTo(mFrequencyConflictState);
+                        break;
+                    }
                     break;
                 case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
-                    P2pStatus status = (P2pStatus)message.obj;
+                    status = (P2pStatus)message.obj;
                     if (status == P2pStatus.SUCCESS) {
                         // invocation was succeeded.
                         // wait P2P_GROUP_STARTED_EVENT.
@@ -1300,6 +1328,8 @@
                             handleGroupCreationFailure();
                             transitionTo(mInactiveState);
                         }
+                    } else if (status == P2pStatus.NO_COMMON_CHANNEL) {
+                        transitionTo(mFrequencyConflictState);
                     } else {
                         handleGroupCreationFailure();
                         transitionTo(mInactiveState);
@@ -1312,7 +1342,90 @@
         }
     }
 
+    class FrequencyConflictState extends State {
+        private AlertDialog mFrequencyConflictDialog;
+        @Override
+        public void enter() {
+            if (DBG) logd(getName());
+            notifyFrequencyConflict();
+        }
 
+        private void notifyFrequencyConflict() {
+            logd("Notify frequency conflict");
+            Resources r = Resources.getSystem();
+
+            AlertDialog dialog = new AlertDialog.Builder(mContext)
+                .setMessage(r.getString(R.string.wifi_p2p_frequency_conflict_message,
+                        getDeviceName(mSavedPeerConfig.deviceAddress)))
+                .setPositiveButton(r.getString(R.string.dlg_ok), new OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            sendMessage(DROP_WIFI_USER_ACCEPT);
+                        }
+                    })
+                .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            sendMessage(DROP_WIFI_USER_REJECT);
+                        }
+                    })
+                .setOnCancelListener(new DialogInterface.OnCancelListener() {
+                        @Override
+                        public void onCancel(DialogInterface arg0) {
+                            sendMessage(DROP_WIFI_USER_REJECT);
+                        }
+                    })
+                .create();
+
+            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+            dialog.show();
+            mFrequencyConflictDialog = dialog;
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) logd(getName() + message.toString());
+            switch (message.what) {
+                case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
+                case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
+                    loge(getName() + "group sucess during freq conflict!");
+                    break;
+                case WifiMonitor.P2P_GROUP_STARTED_EVENT:
+                    loge(getName() + "group started after freq conflict, handle anyway");
+                    deferMessage(message);
+                    transitionTo(mGroupNegotiationState);
+                    break;
+                case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
+                case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
+                case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
+                    // Ignore failures since we retry again
+                    break;
+                case DROP_WIFI_USER_REJECT:
+                    // User rejected dropping wifi in favour of p2p
+                    handleGroupCreationFailure();
+                    transitionTo(mInactiveState);
+                    break;
+                case DROP_WIFI_USER_ACCEPT:
+                    // User accepted dropping wifi in favour of p2p
+                    mWifiChannel.sendMessage(WifiP2pService.DISCONNECT_WIFI_REQUEST, 1);
+                    mTempoarilyDisconnectedWifi = true;
+                    break;
+                case DISCONNECT_WIFI_RESPONSE:
+                    // Got a response from wifistatemachine, retry p2p
+                    if (DBG) logd(getName() + "Wifi disconnected, retry p2p");
+                    transitionTo(mInactiveState);
+                    sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        public void exit() {
+            if (mFrequencyConflictDialog != null) mFrequencyConflictDialog.dismiss();
+        }
+    }
 
     class GroupCreatedState extends State {
         @Override
@@ -2195,6 +2308,11 @@
         mPeersLostDuringConnection.clear();
         mServiceDiscReqId = null;
         if (changed) sendP2pPeersChangedBroadcast();
+
+        if (mTempoarilyDisconnectedWifi) {
+            mWifiChannel.sendMessage(WifiP2pService.DISCONNECT_WIFI_REQUEST, 0);
+            mTempoarilyDisconnectedWifi = false;
+        }
     }
 
     //State machine initiated requests can have replyTo set to null indicating