Merge "Move the padding to the text field in SearchView."
diff --git a/api/current.txt b/api/current.txt
index 1e3d40e..3ea70b7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16037,6 +16037,12 @@
     field public static final java.lang.String GROUP_SOURCE_ID = "group_sourceid";
   }
 
+  public static final class ContactsContract.CommonDataKinds.Identity implements android.provider.ContactsContract.DataColumnsWithJoins {
+    field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/identity";
+    field public static final java.lang.String IDENTITY = "data1";
+    field public static final java.lang.String NAMESPACE = "data2";
+  }
+
   public static final class ContactsContract.CommonDataKinds.Im implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
     method public static final java.lang.CharSequence getProtocolLabel(android.content.res.Resources, int, java.lang.CharSequence);
     method public static final int getProtocolLabelResource(int);
@@ -22481,12 +22487,14 @@
     method public android.graphics.Bitmap getBitmap(android.graphics.Bitmap);
     method public android.graphics.SurfaceTexture getSurfaceTexture();
     method public android.view.TextureView.SurfaceTextureListener getSurfaceTextureListener();
+    method public android.graphics.Matrix getTransform(android.graphics.Matrix);
     method public boolean isAvailable();
     method public android.graphics.Canvas lockCanvas();
     method public android.graphics.Canvas lockCanvas(android.graphics.Rect);
     method protected final void onDraw(android.graphics.Canvas);
     method public void setOpaque(boolean);
     method public void setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener);
+    method public void setTransform(android.graphics.Matrix);
     method public void unlockCanvasAndPost(android.graphics.Canvas);
   }
 
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index eb9cd21..53fd50d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -260,11 +260,18 @@
      */
     public static final int TYPE_MOBILE_CBS  = 12;
 
-    /** {@hide} */
-    public static final int MAX_RADIO_TYPE   = TYPE_MOBILE_CBS;
+    /**
+     * A Wi-Fi p2p connection. Only requesting processes will have access to
+     * the peers connected.
+     * {@hide}
+     */
+    public static final int TYPE_WIFI_P2P    = 13;
 
     /** {@hide} */
-    public static final int MAX_NETWORK_TYPE = TYPE_MOBILE_CBS;
+    public static final int MAX_RADIO_TYPE   = TYPE_WIFI_P2P;
+
+    /** {@hide} */
+    public static final int MAX_NETWORK_TYPE = TYPE_WIFI_P2P;
 
     public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI;
 
@@ -303,6 +310,8 @@
                 return "MOBILE_IMS";
             case TYPE_MOBILE_CBS:
                 return "MOBILE_CBS";
+            case TYPE_WIFI_P2P:
+                return "WIFI_P2P";
             default:
                 return Integer.toString(type);
         }
diff --git a/core/java/android/net/DhcpInfoInternal.java b/core/java/android/net/DhcpInfoInternal.java
index 860da0a..9b0a2d7 100644
--- a/core/java/android/net/DhcpInfoInternal.java
+++ b/core/java/android/net/DhcpInfoInternal.java
@@ -100,7 +100,8 @@
         if (TextUtils.isEmpty(dns1) == false) {
             p.addDns(NetworkUtils.numericToInetAddress(dns1));
         } else {
-            Log.d(TAG, "makeLinkProperties with empty dns1!");
+            p.addDns(NetworkUtils.numericToInetAddress(serverAddress));
+            Log.d(TAG, "empty dns1, use dhcp server as dns1!");
         }
         if (TextUtils.isEmpty(dns2) == false) {
             p.addDns(NetworkUtils.numericToInetAddress(dns2));
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index d867e35..a66fa81 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -6455,6 +6455,37 @@
                 }
             }
         }
+
+        /**
+         * A data kind representing an Identity related to the contact.
+         * <p>
+         * This can be used as a signal by the aggregator to combine raw contacts into
+         * contacts, e.g. if two contacts have Identity rows with
+         * the same NAMESPACE and IDENTITY values the aggregator can know that they refer
+         * to the same person.
+         * </p>
+         */
+        public static final class Identity implements DataColumnsWithJoins {
+            /**
+             * This utility class cannot be instantiated
+             */
+            private Identity() {}
+
+            /** MIME type used when storing this in data table. */
+            public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/identity";
+
+            /**
+             * The identity string.
+             * <P>Type: TEXT</P>
+             */
+            public static final String IDENTITY = DataColumns.DATA1;
+
+            /**
+             * The namespace of the identity string, e.g. "com.google"
+             * <P>Type: TEXT</P>
+             */
+            public static final String NAMESPACE = DataColumns.DATA2;
+        }
     }
 
     /**
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index e586370..cfbb47c 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -151,6 +151,7 @@
     static native void nResizeLayer(int layerId, int width, int height, int[] layerInfo);
     static native void nUpdateTextureLayer(int layerId, int width, int height, boolean opaque,
             SurfaceTexture surface);
+    static native void nSetTextureLayerTransform(int layerId, int matrix);
     static native void nDestroyLayer(int layerId);
     static native void nDestroyLayerDeferred(int layerId);
     static native boolean nCopyLayer(int layerId, int bitmap);
diff --git a/core/java/android/view/GLES20RenderLayer.java b/core/java/android/view/GLES20RenderLayer.java
index 41f16e2..23a7166 100644
--- a/core/java/android/view/GLES20RenderLayer.java
+++ b/core/java/android/view/GLES20RenderLayer.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 
 /**
  * An OpenGL ES 2.0 implementation of {@link HardwareLayer}. This
@@ -87,4 +88,11 @@
         }
         return getCanvas();
     }
+
+    /**
+     * Ignored
+     */
+    @Override
+    void setTransform(Matrix matrix) {
+    }
 }
diff --git a/core/java/android/view/GLES20TextureLayer.java b/core/java/android/view/GLES20TextureLayer.java
index 391d9f4..6c41023 100644
--- a/core/java/android/view/GLES20TextureLayer.java
+++ b/core/java/android/view/GLES20TextureLayer.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.SurfaceTexture;
 
 /**
@@ -75,4 +76,9 @@
         super.update(width, height, isOpaque);
         GLES20Canvas.nUpdateTextureLayer(mLayer, width, height, isOpaque, mSurface);
     }
+
+    @Override
+    void setTransform(Matrix matrix) {
+        GLES20Canvas.nSetTextureLayerTransform(mLayer, matrix.native_instance);
+    }
 }
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index dfb39ae..28389ab 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 
 /**
  * A hardware layer can be used to render graphics operations into a hardware
@@ -150,4 +151,11 @@
         mHeight = height;
         mOpaque = isOpaque;
     }
+
+    /**
+     * Sets an optional transform on this layer.
+     * 
+     * @param matrix The transform to apply to the layer.
+     */
+    abstract void setTransform(Matrix matrix);
 }
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index f2c3131..a05637d 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -718,7 +718,9 @@
                 if (!createSurface(holder)) {
                     return;
                 }
-                setEnabled(true);                
+                if (mCanvas != null) {
+                    setEnabled(true);
+                }
             }
         }
 
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 53a6bcb..b72222e 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
@@ -104,6 +105,9 @@
 
     private boolean mOpaque = true;
 
+    private final Matrix mMatrix = new Matrix();
+    private boolean mMatrixChanged;
+
     private final Object[] mLock = new Object[0];
     private boolean mUpdateLayer;
 
@@ -312,6 +316,11 @@
 
         applyUpdate();
 
+        if (mMatrixChanged) {
+            mLayer.setTransform(mMatrix);
+            mMatrixChanged = false;
+        }
+
         return mLayer;
     }
 
@@ -358,6 +367,50 @@
     }
 
     /**
+     * <p>Sets the transform to associate with this texture view.
+     * The specified transform applies to the underlying surface
+     * texture and does not affect the size or position of the view
+     * itself, only of its content.</p>
+     * 
+     * <p>Some transforms might prevent the content from drawing
+     * all the pixels contained within this view's bounds. In such
+     * situations, make sure this texture view is not marked opaque.</p>
+     * 
+     * @param transform The transform to apply to the content of
+     *        this view.
+     * 
+     * @see #getTransform(android.graphics.Matrix) 
+     * @see #isOpaque() 
+     * @see #setOpaque(boolean) 
+     */
+    public void setTransform(Matrix transform) {
+        mMatrix.set(transform);
+        mMatrixChanged = true;
+        invalidate();
+    }
+
+    /**
+     * Returns the transform associated with this texture view.
+     * 
+     * @param transform The {@link Matrix} in which to copy the current
+     *        transform. Can be null.
+     * 
+     * @return The specified matrix if not null or a new {@link Matrix}
+     *         instance otherwise.
+     *         
+     * @see #setTransform(android.graphics.Matrix) 
+     */
+    public Matrix getTransform(Matrix transform) {
+        if (transform == null) {
+            transform = new Matrix();
+        }
+
+        transform.set(mMatrix);
+
+        return transform;
+    }
+
+    /**
      * <p>Returns a {@link android.graphics.Bitmap} representation of the content
      * of the associated surface texture. If the surface texture is not available,
      * this method returns null.</p>
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 70e48ad..2bcb020 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -709,10 +709,14 @@
 
         final WebSettings settings = mWebView.getSettings();
         final PackageManager pm = context.getPackageManager();
-        mSupportMultiTouch = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
+        mSupportMultiTouch = 
+                (pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
+                 || pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT))
                 && settings.supportZoom() && settings.getBuiltInZoomControls();
-        mAllowPanAndScale = pm.hasSystemFeature(
-                PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
+        mAllowPanAndScale =
+                pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)
+                || pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT);
+
         if (mSupportMultiTouch && (mScaleDetector == null)) {
             mScaleDetector = new ScaleGestureDetector(context, new ScaleDetectorListener());
         } else if (!mSupportMultiTouch && (mScaleDetector != null)) {
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index 267221b..d613921 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -109,6 +109,13 @@
         // Divide the view into cells.
         final int cellCount = widthSize / mMinCellSize;
         final int cellSizeRemaining = widthSize % mMinCellSize;
+
+        if (cellCount == 0) {
+            // Give up, nothing fits.
+            setMeasuredDimension(widthSize, 0);
+            return;
+        }
+
         final int cellSize = mMinCellSize + cellSizeRemaining / cellCount;
 
         int cellsRemaining = cellCount;
@@ -213,7 +220,8 @@
                 }
             }
 
-            final int extraPixels = (int) (cellsRemaining * cellSize / expandCount);
+            final int extraPixels = expandCount > 0 ?
+                    (int) (cellsRemaining * cellSize / expandCount) : 0;
 
             for (int i = 0; i < childCount; i++) {
                 if ((smallestItemsAt & (1 << i)) == 0) continue;
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 039c5ba..80c79fd 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -683,6 +683,12 @@
     LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, renderTarget, transform);
 }
 
+static void android_view_GLES20Canvas_setTextureLayerTransform(JNIEnv* env, jobject clazz,
+        Layer* layer, SkMatrix* matrix) {
+
+    layer->getTransform().load(*matrix);
+}
+
 static void android_view_GLES20Canvas_destroyLayer(JNIEnv* env, jobject clazz, Layer* layer) {
     LayerRenderer::destroyLayer(layer);
 }
@@ -827,6 +833,7 @@
     { "nCreateTextureLayer",     "(Z[I)I",     (void*) android_view_GLES20Canvas_createTextureLayer },
     { "nUpdateTextureLayer",     "(IIIZLandroid/graphics/SurfaceTexture;)V",
                                                (void*) android_view_GLES20Canvas_updateTextureLayer },
+    { "nSetTextureLayerTransform", "(II)V",    (void*) android_view_GLES20Canvas_setTextureLayerTransform },
     { "nDestroyLayer",           "(I)V",       (void*) android_view_GLES20Canvas_destroyLayer },
     { "nDestroyLayerDeferred",   "(I)V",       (void*) android_view_GLES20Canvas_destroyLayerDeferred },
     { "nDrawLayer",              "(IIFFI)V",   (void*) android_view_GLES20Canvas_drawLayer },
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3f67d1ba..74989e6 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -116,6 +116,7 @@
         <item>"mobile_fota,10,0,2,60000,true"</item>
         <item>"mobile_ims,11,0,2,60000,true"</item>
         <item>"mobile_cbs,12,0,2,60000,true"</item>
+        <item>"wifi_p2p,13,1,0,-1,true"</item>
     </string-array>
 
     <!-- Array of ConnectivityManager.TYPE_xxxx constants for networks that may only
diff --git a/data/fonts/Lohit_Hindi.ttf b/data/fonts/Lohit_Hindi.ttf
new file mode 100644
index 0000000..73caae3
--- /dev/null
+++ b/data/fonts/Lohit_Hindi.ttf
Binary files differ
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 6ac615d..1bee47a 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -46,6 +46,11 @@
     </family>
     <family>
         <fileset>
+            <file>Lohit_Hindi.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
             <file>DroidSansFallback.ttf</file>
         </fileset>
     </family>
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 73fb111..d8c1fa2 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -31,6 +31,7 @@
     frameworks/base/data/fonts/DroidSerif-Italic.ttf:system/fonts/DroidSerif-Italic.ttf \
     frameworks/base/data/fonts/DroidSerif-BoldItalic.ttf:system/fonts/DroidSerif-BoldItalic.ttf \
     frameworks/base/data/fonts/DroidSansMono.ttf:system/fonts/DroidSansMono.ttf \
+    frameworks/base/data/fonts/Lohit_Hindi.ttf:system/fonts/Lohit_Hindi.ttf \
     frameworks/base/data/fonts/Clockopia.ttf:system/fonts/Clockopia.ttf \
     frameworks/base/data/fonts/DroidSansFallback.ttf:system/fonts/DroidSansFallback.ttf \
     frameworks/base/data/fonts/AndroidClock.ttf:system/fonts/AndroidClock.ttf \
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index f13e9bb..e965f14 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -36,6 +36,7 @@
         kWhatShutdownCompleted   = 'scom',
         kWhatFlushCompleted      = 'fcom',
         kWhatOutputFormatChanged = 'outC',
+        kWhatError               = 'erro',
     };
 
     ACodec();
@@ -58,7 +59,6 @@
     struct OutputPortSettingsChangedState;
     struct ExecutingToIdleState;
     struct IdleToLoadedState;
-    struct ErrorState;
     struct FlushingState;
 
     enum {
@@ -102,7 +102,6 @@
     sp<OutputPortSettingsChangedState> mOutputPortSettingsChangedState;
     sp<ExecutingToIdleState> mExecutingToIdleState;
     sp<IdleToLoadedState> mIdleToLoadedState;
-    sp<ErrorState> mErrorState;
     sp<FlushingState> mFlushingState;
 
     AString mComponentName;
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index dd75497..a8ae5c6 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -203,6 +203,10 @@
         return texTransform;
     }
 
+    inline mat4& getTransform() {
+        return transform;
+    }
+
     /**
      * Bounds of the layer.
      */
@@ -282,6 +286,11 @@
      */
     mat4 texTransform;
 
+    /**
+     * Optional transform.
+     */
+    mat4 transform;
+
 }; // struct Layer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 9fc5131..769c99c 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -51,6 +51,7 @@
     data[kTranslateZ]   = 0.0f;
     data[kPerspective2] = 1.0f;
 
+    mIsIdentity = true;
     mSimpleMatrix = true;
 }
 
@@ -71,14 +72,21 @@
     return mSimpleMatrix;
 }
 
+bool Matrix4::isIdentity() {
+    return mIsIdentity;
+}
+
 void Matrix4::load(const float* v) {
     memcpy(data, v, sizeof(data));
+    // TODO: Do something smarter here
     mSimpleMatrix = false;
+    mIsIdentity = false;
 }
 
 void Matrix4::load(const Matrix4& v) {
     memcpy(data, v.data, sizeof(data));
     mSimpleMatrix = v.mSimpleMatrix;
+    mIsIdentity = v.mIsIdentity;
 }
 
 void Matrix4::load(const SkMatrix& v) {
@@ -99,6 +107,7 @@
     data[kScaleZ] = 1.0f;
 
     mSimpleMatrix = (v.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask));
+    mIsIdentity = v.isIdentity();
 }
 
 void Matrix4::copyTo(SkMatrix& v) const {
@@ -148,6 +157,7 @@
             v.data[kSkewX] * v.data[kSkewY]) * scale;
 
     mSimpleMatrix = v.mSimpleMatrix;
+    mIsIdentity = v.mIsIdentity;
 }
 
 void Matrix4::copyTo(float* v) const {
@@ -166,20 +176,27 @@
     for (int i = 0; i < 16; i++) {
         data[i] *= v;
     }
+    mIsIdentity = false;
 }
 
 void Matrix4::loadTranslate(float x, float y, float z) {
     loadIdentity();
+
     data[kTranslateX] = x;
     data[kTranslateY] = y;
     data[kTranslateZ] = z;
+
+    mIsIdentity = false;
 }
 
 void Matrix4::loadScale(float sx, float sy, float sz) {
     loadIdentity();
+
     data[kScaleX] = sx;
     data[kScaleY] = sy;
     data[kScaleZ] = sz;
+
+    mIsIdentity = false;
 }
 
 void Matrix4::loadSkew(float sx, float sy) {
@@ -198,6 +215,7 @@
     data[kPerspective2] = 1.0f;
 
     mSimpleMatrix = false;
+    mIsIdentity = false;
 }
 
 void Matrix4::loadRotate(float angle, float x, float y, float z) {
@@ -238,6 +256,7 @@
     data[kScaleZ] = z * z * nc +  c;
 
     mSimpleMatrix = false;
+    mIsIdentity = false;
 }
 
 void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) {
@@ -262,16 +281,20 @@
     }
 
     mSimpleMatrix = u.mSimpleMatrix && v.mSimpleMatrix;
+    mIsIdentity = false;
 }
 
 void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) {
     loadIdentity();
+
     data[kScaleX] = 2.0f / (right - left);
     data[kScaleY] = 2.0f / (top - bottom);
     data[kScaleZ] = -2.0f / (far - near);
     data[kTranslateX] = -(right + left) / (right - left);
     data[kTranslateY] = -(top + bottom) / (top - bottom);
     data[kTranslateZ] = -(far + near) / (far - near);
+
+    mIsIdentity = false;
 }
 
 #define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c)
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 2fa6ab7..56fd37d 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -112,6 +112,7 @@
 
     bool isPureTranslate();
     bool isSimple();
+    bool isIdentity();
 
     bool changesBounds();
 
@@ -128,6 +129,7 @@
 
 private:
     bool mSimpleMatrix;
+    bool mIsIdentity;
 
     inline float get(int i, int j) const {
         return data[i * 4 + j];
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 4864cff..a0f806a 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -627,6 +627,12 @@
 void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
     float alpha = layer->getAlpha() / 255.0f;
 
+    mat4& transform = layer->getTransform();
+    if (!transform.isIdentity()) {
+        save(0);
+        mSnapshot->transform->multiply(transform);
+    }
+
     setupDraw();
     if (layer->getRenderTarget() == GL_TEXTURE_2D) {
         setupDrawWithTexture();
@@ -663,6 +669,10 @@
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
 
     finishDrawTexture();
+
+    if (!transform.isIdentity()) {
+        restore();
+    }
 }
 
 void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index b06f20d..7fb141a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -340,6 +340,11 @@
                 }
 
                 finishFlushIfPossible();
+            } else if (what == ACodec::kWhatError) {
+                LOGE("Received error from %s decoder, aborting playback.",
+                     audio ? "audio" : "video");
+
+                mRenderer->queueEOS(audio, UNKNOWN_ERROR);
             } else {
                 CHECK_EQ((int)what, (int)ACodec::kWhatDrainThisBuffer);
 
@@ -358,13 +363,24 @@
                 int32_t audio;
                 CHECK(msg->findInt32("audio", &audio));
 
+                int32_t finalResult;
+                CHECK(msg->findInt32("finalResult", &finalResult));
+
                 if (audio) {
                     mAudioEOS = true;
                 } else {
                     mVideoEOS = true;
                 }
 
-                LOGV("reached %s EOS", audio ? "audio" : "video");
+                if (finalResult == ERROR_END_OF_STREAM) {
+                    LOGV("reached %s EOS", audio ? "audio" : "video");
+                } else {
+                    LOGE("%s track encountered an error (0x%08x)",
+                         audio ? "audio" : "video", finalResult);
+
+                    notifyListener(
+                            MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, finalResult);
+                }
 
                 if ((mAudioEOS || mAudioDecoder == NULL)
                         && (mVideoEOS || mVideoDecoder == NULL)) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 828e008..35ed43f 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -200,6 +200,22 @@
 void NuPlayer::Renderer::onDrainAudioQueue() {
 
     for (;;) {
+        if (mAudioQueue.empty()) {
+            break;
+        }
+
+        QueueEntry *entry = &*mAudioQueue.begin();
+
+        if (entry->mBuffer == NULL) {
+            // EOS
+
+            notifyEOS(true /* audio */, entry->mFinalResult);
+
+            mAudioQueue.erase(mAudioQueue.begin());
+            entry = NULL;
+            return;
+        }
+
         uint32_t numFramesPlayed;
         CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
 
@@ -213,22 +229,6 @@
             break;
         }
 
-        if (mAudioQueue.empty()) {
-            break;
-        }
-
-        QueueEntry *entry = &*mAudioQueue.begin();
-
-        if (entry->mBuffer == NULL) {
-            // EOS
-
-            notifyEOS(true /* audio */);
-
-            mAudioQueue.erase(mAudioQueue.begin());
-            entry = NULL;
-            return;
-        }
-
         if (entry->mOffset == 0) {
             int64_t mediaTimeUs;
             CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
@@ -330,7 +330,7 @@
     if (entry->mBuffer == NULL) {
         // EOS
 
-        notifyEOS(false /* audio */);
+        notifyEOS(false /* audio */, entry->mFinalResult);
 
         mVideoQueue.erase(mVideoQueue.begin());
         entry = NULL;
@@ -352,10 +352,11 @@
     notifyPosition();
 }
 
-void NuPlayer::Renderer::notifyEOS(bool audio) {
+void NuPlayer::Renderer::notifyEOS(bool audio, status_t finalResult) {
     sp<AMessage> notify = mNotify->dup();
     notify->setInt32("what", kWhatEOS);
     notify->setInt32("audio", static_cast<int32_t>(audio));
+    notify->setInt32("finalResult", finalResult);
     notify->post();
 }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 703e971..2713031 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -111,7 +111,7 @@
     void onPause();
     void onResume();
 
-    void notifyEOS(bool audio);
+    void notifyEOS(bool audio, status_t finalResult);
     void notifyFlushComplete(bool audio);
     void notifyPosition();
 
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 174ec92..5d91f6a 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -285,21 +285,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-struct ACodec::ErrorState : public ACodec::BaseState {
-    ErrorState(ACodec *codec);
-
-protected:
-    virtual bool onMessageReceived(const sp<AMessage> &msg);
-    virtual void stateEntered();
-
-    virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
-
-private:
-    DISALLOW_EVIL_CONSTRUCTORS(ErrorState);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-
 struct ACodec::FlushingState : public ACodec::BaseState {
     FlushingState(ACodec *codec);
 
@@ -335,7 +320,6 @@
 
     mExecutingToIdleState = new ExecutingToIdleState(this);
     mIdleToLoadedState = new IdleToLoadedState(this);
-    mErrorState = new ErrorState(this);
     mFlushingState = new FlushingState(this);
 
     mPortEOS[kPortIndexInput] = mPortEOS[kPortIndexOutput] = false;
@@ -594,7 +578,10 @@
 
 ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
     ANativeWindowBuffer *buf;
-    CHECK_EQ(mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf), 0);
+    if (mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf) != 0) {
+        LOGE("dequeueBuffer failed.");
+        return NULL;
+    }
 
     for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) {
         BufferInfo *info =
@@ -1263,10 +1250,12 @@
         return false;
     }
 
-    LOGE("[%s] ERROR(0x%08lx, 0x%08lx)",
-         mCodec->mComponentName.c_str(), data1, data2);
+    LOGE("[%s] ERROR(0x%08lx)", mCodec->mComponentName.c_str(), data1);
 
-    mCodec->changeState(mCodec->mErrorState);
+    sp<AMessage> notify = mCodec->mNotify->dup();
+    notify->setInt32("what", ACodec::kWhatError);
+    notify->setInt32("omx-error", data1);
+    notify->post();
 
     return true;
 }
@@ -1595,13 +1584,15 @@
                     info = mCodec->dequeueBufferFromNativeWindow();
                 }
 
-                LOGV("[%s] calling fillBuffer %p",
-                     mCodec->mComponentName.c_str(), info->mBufferID);
+                if (info != NULL) {
+                    LOGV("[%s] calling fillBuffer %p",
+                         mCodec->mComponentName.c_str(), info->mBufferID);
 
-                CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
-                         (status_t)OK);
+                    CHECK_EQ(mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID),
+                             (status_t)OK);
 
-                info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+                    info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+                }
             }
             break;
         }
@@ -1642,6 +1633,7 @@
             notify->post();
 
             handled = true;
+            break;
         }
 
         case ACodec::kWhatFlush:
@@ -1651,6 +1643,7 @@
             notify->post();
 
             handled = true;
+            break;
         }
 
         default:
@@ -1696,7 +1689,16 @@
         node = NULL;
     }
 
-    CHECK(node != NULL);
+    if (node == NULL) {
+        LOGE("Unable to instantiate a decoder for type '%s'.", mime.c_str());
+
+        sp<AMessage> notify = mCodec->mNotify->dup();
+        notify->setInt32("what", ACodec::kWhatError);
+        notify->setInt32("omx-error", OMX_ErrorComponentNotFound);
+        notify->post();
+
+        return;
+    }
 
     sp<AMessage> notify = new AMessage(kWhatOMXMessage, mCodec->id());
     observer->setNotificationMessage(notify);
@@ -2236,26 +2238,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-ACodec::ErrorState::ErrorState(ACodec *codec)
-    : BaseState(codec) {
-}
-
-bool ACodec::ErrorState::onMessageReceived(const sp<AMessage> &msg) {
-    return BaseState::onMessageReceived(msg);
-}
-
-void ACodec::ErrorState::stateEntered() {
-    LOGV("[%s] Now in ErrorState", mCodec->mComponentName.c_str());
-}
-
-bool ACodec::ErrorState::onOMXEvent(
-        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
-    LOGV("EVENT(%d, 0x%08lx, 0x%08lx)", event, data1, data2);
-    return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
 ACodec::FlushingState::FlushingState(ACodec *codec)
     : BaseState(codec) {
 }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index f88b311..b60a038 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -2850,7 +2850,7 @@
             // or orientation sensor disabled
             //or case.unspecified
             if (mHdmiPlugged) {
-                return Surface.ROTATION_0;
+                return mLandscapeRotation;
             } else if (mLidOpen == LID_OPEN) {
                 return mLidOpenRotation;
             } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR && mCarDockRotation >= 0) {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index a506636..22ce484 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -468,14 +468,10 @@
         for (int netType : mPriorityList) {
             switch (mNetConfigs[netType].radio) {
             case ConnectivityManager.TYPE_WIFI:
-                if (DBG) log("Starting Wifi Service.");
-                WifiStateTracker wst = new WifiStateTracker();
-                WifiService wifiService = new WifiService(context);
-                ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
-                wifiService.checkAndStartWifi();
-                mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
-                wst.startMonitoring(context, mHandler);
-                break;
+                mNetTrackers[netType] = new WifiStateTracker(netType,
+                        mNetConfigs[netType].name);
+                mNetTrackers[netType].startMonitoring(context, mHandler);
+               break;
             case ConnectivityManager.TYPE_MOBILE:
                 mNetTrackers[netType] = new MobileDataStateTracker(netType,
                         mNetConfigs[netType].name);
@@ -882,15 +878,8 @@
 
         FeatureUser f = new FeatureUser(networkType, feature, binder);
 
-        // TODO - move this into the MobileDataStateTracker
-        int usedNetworkType = networkType;
-        if(networkType == ConnectivityManager.TYPE_MOBILE) {
-            usedNetworkType = convertFeatureToNetworkType(feature);
-            if (usedNetworkType < 0) {
-                loge("Can't match any netTracker!");
-                usedNetworkType = networkType;
-            }
-        }
+        // TODO - move this into individual networktrackers
+        int usedNetworkType = convertFeatureToNetworkType(networkType, feature);
 
         if (mProtectedNetworks.contains(usedNetworkType)) {
             enforceConnectivityInternalPermission();
@@ -900,7 +889,6 @@
         if (network != null) {
             Integer currentPid = new Integer(getCallingPid());
             if (usedNetworkType != networkType) {
-                NetworkStateTracker radio = mNetTrackers[networkType];
                 NetworkInfo ni = network.getNetworkInfo();
 
                 if (ni.isAvailable() == false) {
@@ -1046,14 +1034,9 @@
                 }
             }
 
-            // TODO - move to MobileDataStateTracker
-            int usedNetworkType = networkType;
-            if (networkType == ConnectivityManager.TYPE_MOBILE) {
-                usedNetworkType = convertFeatureToNetworkType(feature);
-                if (usedNetworkType < 0) {
-                    usedNetworkType = networkType;
-                }
-            }
+            // TODO - move to individual network trackers
+            int usedNetworkType = convertFeatureToNetworkType(networkType, feature);
+
             tracker =  mNetTrackers[usedNetworkType];
             if (tracker == null) {
                 if (DBG) log("ignoring - no known tracker for net type " + usedNetworkType);
@@ -1780,14 +1763,20 @@
         mCurrentLinkProperties[netType] = newLp;
         boolean resetDns = updateRoutes(newLp, curLp, mNetConfigs[netType].isDefault());
 
-        if (doReset || resetMask != 0 || resetDns) {
+        if (resetMask != 0 || resetDns) {
             LinkProperties linkProperties = mNetTrackers[netType].getLinkProperties();
             if (linkProperties != null) {
                 String iface = linkProperties.getInterfaceName();
                 if (TextUtils.isEmpty(iface) == false) {
-                    if (doReset || resetMask != 0) {
+                    if (resetMask != 0) {
                         if (DBG) log("resetConnections(" + iface + ", " + resetMask + ")");
                         NetworkUtils.resetConnections(iface, resetMask);
+
+                        // Tell VPN the interface is down. It is a temporary
+                        // but effective fix to make VPN aware of the change.
+                        if ((resetMask & NetworkUtils.RESET_IPV4_ADDRESSES) != 0) {
+                            mVpn.interfaceStatusChanged(iface, false);
+                        }
                     }
                     if (resetDns) {
                         if (DBG) log("resetting DNS cache for " + iface);
@@ -2666,25 +2655,38 @@
         Slog.e(TAG, s);
     }
 
-    int convertFeatureToNetworkType(String feature){
-        int networkType = -1;
-        if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
-            networkType = ConnectivityManager.TYPE_MOBILE_MMS;
-        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
-            networkType = ConnectivityManager.TYPE_MOBILE_SUPL;
-        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN) ||
-                TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN_ALWAYS)) {
-            networkType = ConnectivityManager.TYPE_MOBILE_DUN;
-        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
-            networkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
-        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_FOTA)) {
-            networkType = ConnectivityManager.TYPE_MOBILE_FOTA;
-        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_IMS)) {
-            networkType = ConnectivityManager.TYPE_MOBILE_IMS;
-        } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_CBS)) {
-            networkType = ConnectivityManager.TYPE_MOBILE_CBS;
+    int convertFeatureToNetworkType(int networkType, String feature) {
+        int usedNetworkType = networkType;
+
+        if(networkType == ConnectivityManager.TYPE_MOBILE) {
+            if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
+                usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
+            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
+                usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
+            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN) ||
+                    TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN_ALWAYS)) {
+                usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
+            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
+                usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
+            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_FOTA)) {
+                usedNetworkType = ConnectivityManager.TYPE_MOBILE_FOTA;
+            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_IMS)) {
+                usedNetworkType = ConnectivityManager.TYPE_MOBILE_IMS;
+            } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_CBS)) {
+                usedNetworkType = ConnectivityManager.TYPE_MOBILE_CBS;
+            } else {
+                Slog.e(TAG, "Can't match any mobile netTracker!");
+            }
+        } else if (networkType == ConnectivityManager.TYPE_WIFI) {
+            if (TextUtils.equals(feature, "p2p")) {
+                usedNetworkType = ConnectivityManager.TYPE_WIFI_P2P;
+            } else {
+                Slog.e(TAG, "Can't match any wifi netTracker!");
+            }
+        } else {
+            Slog.e(TAG, "Unexpected network type");
         }
-        return networkType;
+        return usedNetworkType;
     }
 
     private static <T> T checkNotNull(T value, String message) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 77d0457..5dd3c5b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -110,6 +110,7 @@
         NetworkPolicyManagerService networkPolicy = null;
         ConnectivityService connectivity = null;
         WifiP2pService wifiP2p = null;
+        WifiService wifi = null;
         IPackageManager pm = null;
         Context context = null;
         WindowManagerService wm = null;
@@ -309,6 +310,15 @@
                 Slog.e(TAG, "Failure starting Wi-Fi P2pService", e);
             }
 
+           try {
+                Slog.i(TAG, "Wi-Fi Service");
+                wifi = new WifiService(context);
+                ServiceManager.addService(Context.WIFI_SERVICE, wifi);
+                wifi.checkAndStartWifi();
+            } catch (Throwable e) {
+                Slog.e(TAG, "Failure starting Wi-Fi Service", e);
+            }
+
             try {
                 Slog.i(TAG, "Connectivity Service");
                 connectivity = new ConnectivityService(context, networkManagement, networkPolicy);
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index d9da92c..6b65e07 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -265,7 +265,6 @@
     // INetworkManagementEventObserver.Stub
     @Override
     public void interfaceLinkStateChanged(String interfaze, boolean up) {
-        interfaceStatusChanged(interfaze, up);
     }
 
     // INetworkManagementEventObserver.Stub
@@ -280,6 +279,9 @@
             if (mConnection != null) {
                 mContext.unbindService(mConnection);
                 mConnection = null;
+            } else if (mLegacyVpnRunner != null) {
+                mLegacyVpnRunner.exit();
+                mLegacyVpnRunner = null;
             }
         }
     }
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index 91c5e33..a01c975 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -253,13 +253,6 @@
             }
         };
 
-        private static final int NOTIFICATION_NONE = 0;
-        private static final int NOTIFICATION_MTP = 1;
-        private static final int NOTIFICATION_PTP = 2;
-        private static final int NOTIFICATION_INSTALLER = 3;
-        private static final int NOTIFICATION_ACCESSORY = 4;
-        private static final int NOTIFICATION_ADB = 5;
-
         public UsbHandler(Looper looper) {
             super(looper);
             try {
@@ -536,27 +529,18 @@
 
         private void updateUsbNotification() {
             if (mNotificationManager == null || !mUseUsbNotification) return;
-            int id = NOTIFICATION_NONE;
+            int id = 0;
             Resources r = mContext.getResources();
-            CharSequence title = null;
             if (mConnected) {
                 if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)) {
-                    title = r.getText(
-                        com.android.internal.R.string.usb_mtp_notification_title);
-                    id = NOTIFICATION_MTP;
+                    id = com.android.internal.R.string.usb_mtp_notification_title;
                 } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP)) {
-                    title = r.getText(
-                        com.android.internal.R.string.usb_ptp_notification_title);
-                    id = NOTIFICATION_PTP;
+                    id = com.android.internal.R.string.usb_ptp_notification_title;
                 } else if (containsFunction(mCurrentFunctions,
                         UsbManager.USB_FUNCTION_MASS_STORAGE)) {
-                    title = r.getText(
-                        com.android.internal.R.string.usb_cd_installer_notification_title);
-                    id = NOTIFICATION_INSTALLER;
+                    id = com.android.internal.R.string.usb_cd_installer_notification_title;
                 } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) {
-                    title = r.getText(
-                        com.android.internal.R.string.usb_accessory_notification_title);
-                    id = NOTIFICATION_ACCESSORY;
+                    id = com.android.internal.R.string.usb_accessory_notification_title;
                 } else {
                     // There is a different notification for USB tethering so we don't need one here
                     if (!containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_RNDIS)) {
@@ -566,13 +550,14 @@
             }
             if (id != mUsbNotificationId) {
                 // clear notification if title needs changing
-                if (mUsbNotificationId != NOTIFICATION_NONE) {
+                if (mUsbNotificationId != 0) {
                     mNotificationManager.cancel(mUsbNotificationId);
-                    mUsbNotificationId = NOTIFICATION_NONE;
+                    mUsbNotificationId = 0;
                 }
-                if (id != NOTIFICATION_NONE) {
+                if (id != 0) {
                     CharSequence message = r.getText(
                             com.android.internal.R.string.usb_notification_message);
+                    CharSequence title = r.getText(id);
 
                     Notification notification = new Notification();
                     notification.icon = com.android.internal.R.drawable.stat_sys_data_usb;
@@ -600,13 +585,13 @@
 
         private void updateAdbNotification() {
             if (mNotificationManager == null) return;
+            final int id = com.android.internal.R.string.adb_active_notification_title;
             if (mAdbEnabled && mConnected) {
                 if ("0".equals(SystemProperties.get("persist.adb.notify"))) return;
 
                 if (!mAdbNotificationShown) {
                     Resources r = mContext.getResources();
-                    CharSequence title = r.getText(
-                            com.android.internal.R.string.adb_active_notification_title);
+                    CharSequence title = r.getText(id);
                     CharSequence message = r.getText(
                             com.android.internal.R.string.adb_active_notification_message);
 
@@ -629,11 +614,11 @@
                             intent, 0);
                     notification.setLatestEventInfo(mContext, title, message, pi);
                     mAdbNotificationShown = true;
-                    mNotificationManager.notify(NOTIFICATION_ADB, notification);
+                    mNotificationManager.notify(id, notification);
                 }
             } else if (mAdbNotificationShown) {
                 mAdbNotificationShown = false;
-                mNotificationManager.cancel(NOTIFICATION_ADB);
+                mNotificationManager.cancel(id);
             }
         }
 
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
index 97e2108..fcb57d9 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
@@ -17,6 +17,7 @@
 package com.android.test.hwui;
 
 import android.app.Activity;
+import android.graphics.Matrix;
 import android.graphics.SurfaceTexture;
 import android.hardware.Camera;
 import android.os.Bundle;
@@ -33,6 +34,7 @@
     private Camera mCamera;
     private TextureView mTextureView;
     private FrameLayout mContent;
+    private Matrix mMatrix = new Matrix();
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -82,8 +84,6 @@
         }
 
         mCamera.startPreview();
-
-        mTextureView.setCameraDistance(5000);
     }
 
     @Override
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 338cb4d..c20c716 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -27,6 +27,7 @@
 import android.net.NetworkInfo;
 import android.net.LinkProperties;
 import android.net.NetworkStateTracker;
+import android.net.wifi.p2p.WifiP2pManager;
 import android.os.Handler;
 import android.os.Message;
 
@@ -58,8 +59,8 @@
     private BroadcastReceiver mWifiStateReceiver;
     private WifiManager mWifiManager;
 
-    public WifiStateTracker() {
-        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, NETWORKTYPE, "");
+    public WifiStateTracker(int netType, String networkName) {
+        mNetworkInfo = new NetworkInfo(netType, 0, networkName, "");
         mLinkProperties = new LinkProperties();
         mLinkCapabilities = new LinkCapabilities();
 
@@ -87,6 +88,7 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
+        filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
 
         mWifiStateReceiver = new WifiStateReceiver();
         mContext.registerReceiver(mWifiStateReceiver, filter);
@@ -104,7 +106,6 @@
 
     /**
      * Re-enable connectivity to a network after a {@link #teardown()}.
-     * TODO: do away with return value after making MobileDataStateTracker async
      */
     public boolean reconnect() {
         mTeardownRequested.set(false);
@@ -115,7 +116,6 @@
     /**
      * Turn the wireless radio off for a network.
      * @param turnOn {@code true} to turn the radio on, {@code false}
-     * TODO: do away with return value after making MobileDataStateTracker async
      */
     public boolean setRadio(boolean turnOn) {
         mWifiManager.setWifiEnabled(turnOn);
@@ -205,7 +205,21 @@
     private class WifiStateReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-           if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+
+            if (intent.getAction().equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {
+                    mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+                            WifiP2pManager.EXTRA_NETWORK_INFO);
+                    mLinkProperties = intent.getParcelableExtra(
+                            WifiP2pManager.EXTRA_LINK_PROPERTIES);
+                    if (mLinkProperties == null) {
+                        mLinkProperties = new LinkProperties();
+                    }
+                    mLinkCapabilities = intent.getParcelableExtra(
+                        WifiP2pManager.EXTRA_LINK_CAPABILITIES);
+                    if (mLinkCapabilities == null) {
+                        mLinkCapabilities = new LinkCapabilities();
+                    }
+             } else if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                 mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
                         WifiManager.EXTRA_NETWORK_INFO);
                 mLinkProperties = intent.getParcelableExtra(
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index ea212ac..cc1f062 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -19,13 +19,17 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Handler;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.WorkSource;
 import android.os.Messenger;
+import android.util.Log;
 
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
@@ -98,6 +102,22 @@
     public static final String EXTRA_NETWORK_INFO = "networkInfo";
 
     /**
+     * The lookup key for a {@link android.net.LinkProperties} object associated with the
+     * network. Retrieve with
+     * {@link android.content.Intent#getParcelableExtra(String)}.
+     * @hide
+     */
+    public static final String EXTRA_LINK_PROPERTIES = "linkProperties";
+
+    /**
+     * The lookup key for a {@link android.net.LinkCapabilities} object associated with the
+     * network. Retrieve with
+     * {@link android.content.Intent#getParcelableExtra(String)}.
+     * @hide
+     */
+    public static final String EXTRA_LINK_CAPABILITIES = "linkCapabilities";
+
+    /**
      * Broadcast intent action indicating that the available peer list has changed
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -115,9 +135,6 @@
 
     IWifiP2pManager mService;
 
-    /* For communication with WifiP2pService */
-    private AsyncChannel mAsyncChannel = new AsyncChannel();
-
     /* AsyncChannel notifications to apps */
     public static final int HANDLER_CONNECTION = AsyncChannel.CMD_CHANNEL_HALF_CONNECTED;
     public static final int HANDLER_DISCONNECTION = AsyncChannel.CMD_CHANNEL_DISCONNECTED;
@@ -194,18 +211,35 @@
     }
 
     /**
-     * Registers the application handler with the Wi-Fi framework.
-     * This function must be the first to be called before any p2p control
-     * or query operations can be performed.
+     * A channel that connects the application handler to the Wifi framework.
+     * All p2p operations are performed on a channel.
+     */
+    public class Channel {
+        Channel(AsyncChannel c) {
+            mAsyncChannel = c;
+        }
+        AsyncChannel mAsyncChannel;
+    }
+
+    /**
+     * Registers the application handler with the Wi-Fi framework. This function
+     * must be the first to be called before any p2p control or query operations can be performed.
      * @param srcContext is the context of the source
      * @param srcHandler is the handler on which the source receives messages
-     * @return {@code true} if the operation succeeded
+     * @return Channel instance that is necessary for performing p2p operations
      */
-    public boolean connectHandler(Context srcContext, Handler srcHandler) {
+    public Channel initialize(Context srcContext, Handler srcHandler) {
         Messenger messenger = getMessenger();
-        if (messenger == null) return false;
-        return mAsyncChannel.connectSync(srcContext, srcHandler, messenger)
-                == AsyncChannel.STATUS_SUCCESSFUL;
+        if (messenger == null) return null;
+
+        AsyncChannel asyncChannel = new AsyncChannel();
+        Channel c = new Channel(asyncChannel);
+        if (asyncChannel.connectSync(srcContext, srcHandler, messenger)
+                == AsyncChannel.STATUS_SUCCESSFUL) {
+            return c;
+        } else {
+            return null;
+        }
     }
 
     public boolean isP2pSupported() {
@@ -220,16 +254,18 @@
      * 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.
      */
-    public void enableP2p() {
-        mAsyncChannel.sendMessage(ENABLE_P2P);
+    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.
      */
-    public void disableP2p() {
-        mAsyncChannel.sendMessage(DISABLE_P2P);
+    public void disableP2p(Channel c) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(DISABLE_P2P);
     }
 
     /**
@@ -238,29 +274,33 @@
      * A dialog to the user is thrown to request his permission since it can
      * have a significant impact on power consumption
      */
-     public void setListenState(int timeout) {
-        mAsyncChannel.sendMessage(START_LISTEN_MODE, timeout);
+     public void setListenState(Channel c, int timeout) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(START_LISTEN_MODE, timeout);
      }
 
     /**
      * Initiates peer discovery
      */
-    public void discoverPeers() {
-        mAsyncChannel.sendMessage(DISCOVER_PEERS);
+    public void discoverPeers(Channel c) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(DISCOVER_PEERS);
     }
 
     /**
      * Initiates peer discovery with a timeout
      */
-    public void discoverPeers(int timeout) {
-        mAsyncChannel.sendMessage(DISCOVER_PEERS, timeout);
+    public void discoverPeers(Channel c, int timeout) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(DISCOVER_PEERS, timeout);
     }
 
     /**
      * Cancel any existing peer discovery operation
      */
-    public void cancelPeerDiscovery() {
-        mAsyncChannel.sendMessage(CANCEL_DISCOVER_PEERS);
+    public void cancelPeerDiscovery(Channel c) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(CANCEL_DISCOVER_PEERS);
     }
 
     /**
@@ -268,47 +308,53 @@
      *
      * @param peer Configuration described in a {@link WifiP2pConfig} object.
      */
-    public void connect(WifiP2pConfig config) {
-        mAsyncChannel.sendMessage(CONNECT, config);
+    public void connect(Channel c, WifiP2pConfig config) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(CONNECT, config);
     }
 
     /**
      * Cancel any ongoing negotiation or disconnect from an existing group
      */
-    public void disconnect() {
-        mAsyncChannel.sendMessage(CANCEL_CONNECT);
+    public void disconnect(Channel c) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(CANCEL_CONNECT);
     }
 
     /**
      * Create a p2p group. This is essentially an access point that can accept
      * client connections.
      */
-    public void createGroup() {
-        mAsyncChannel.sendMessage(CREATE_GROUP);
+    public void createGroup(Channel c) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(CREATE_GROUP);
     }
 
     /**
      * Remove the current group. This also removes the p2p interface created
      * during group formation.
      */
-    public void removeGroup() {
-        mAsyncChannel.sendMessage(REMOVE_GROUP);
+    public void removeGroup(Channel c) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(REMOVE_GROUP);
     }
 
     /**
      * Request current p2p settings. This returns a RESPONSE_SETTINGS on the source
      * handler.
      */
-    public void requestP2pSettings() {
-        mAsyncChannel.sendMessage(REQUEST_SETTINGS);
+    public void requestP2pSettings(Channel c) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(REQUEST_SETTINGS);
     }
 
     /**
      * Request the list of peers. This returns a RESPONSE_PEERS on the source
      * handler.
      */
-    public void requestPeers() {
-        mAsyncChannel.sendMessage(REQUEST_PEERS);
+    public void requestPeers(Channel c) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(REQUEST_PEERS);
     }
 
     /**
@@ -322,8 +368,9 @@
      * Request device connection status. This returns a RESPONSE_CONNECTION_STATUS on
      * the source handler.
      */
-    public void requestConnectionStatus() {
-        mAsyncChannel.sendMessage(REQUEST_CONNECTION_STATUS);
+    public void requestConnectionStatus(Channel c) {
+        if (c == null) return;
+        c.mAsyncChannel.sendMessage(REQUEST_CONNECTION_STATUS);
     }
 
 
@@ -341,4 +388,38 @@
             return null;
         }
     }
+
+
+    /**
+     * Setup DNS connectivity on the current process to the connected Wi-Fi p2p peers
+     *
+     * @return -1 on failure
+     * @hide
+     */
+    public int startPeerCommunication() {
+        IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+        IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
+        try {
+            return cm.startUsingNetworkFeature(ConnectivityManager.TYPE_WIFI, "p2p", new Binder());
+        } catch (RemoteException e) {
+            return -1;
+        }
+    }
+
+    /**
+     * Tear down connectivity to the connected Wi-Fi p2p peers
+     *
+     * @return -1 on failure
+     * @hide
+     */
+    public int stopPeerCommunication() {
+        IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+        IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
+        try {
+            return cm.stopUsingNetworkFeature(ConnectivityManager.TYPE_WIFI, "p2p");
+        } catch (RemoteException e) {
+            return -1;
+        }
+    }
+
 }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 3678cfc..176191e 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -25,6 +25,15 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
+import android.net.DhcpInfoInternal;
+import android.net.DhcpStateMachine;
+import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.net.NetworkUtils;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiMonitor;
 import android.net.wifi.WifiNative;
@@ -47,16 +56,16 @@
 import android.view.WindowManager;
 import android.widget.EditText;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Collection;
-
 import com.android.internal.R;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collection;
+
 /**
  * WifiP2pService inclues a state machine to perform Wi-Fi p2p operations. Applications
  * communicate with this service to issue device discovery and connectivity requests
@@ -70,14 +79,16 @@
 public class WifiP2pService extends IWifiP2pManager.Stub {
     private static final String TAG = "WifiP2pService";
     private static final boolean DBG = true;
+    private static final String NETWORKTYPE = "WIFI_P2P";
 
     private Context mContext;
     private String mInterface;
 
     INetworkManagementService mNwService;
+    private DhcpStateMachine mDhcpStateMachine;
 
-    // Tracked to notify the user about wifi client/hotspot being shut down
-    // during p2p bring up
+    //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;
 
@@ -85,6 +96,9 @@
     private AsyncChannel mReplyChannel = new AsyncChannel();;
     private AsyncChannel mWifiChannel;
 
+    private static final int GROUP_NEGOTIATION_WAIT_TIME_MS = 60 * 1000;
+    private static int mGroupNegotiationTimeoutIndex = 0;
+
     private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE;
 
     /* Message sent to WifiStateMachine to indicate p2p enable is pending */
@@ -92,15 +106,24 @@
     /* 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 of group negotiation */
+    public static final int GROUP_NEGOTIATION_TIMED_OUT     =   BASE + 3;
+
     /* User accepted to disable Wi-Fi in order to enable p2p */
     private static final int WIFI_DISABLE_USER_ACCEPT       =   BASE + 11;
 
     private final boolean mP2pSupported;
 
+    private NetworkInfo mNetworkInfo;
+    private LinkProperties mLinkProperties;
+
     public WifiP2pService(Context context) {
         mContext = context;
 
         mInterface = SystemProperties.get("wifi.interface", "wlan0");
+        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");
+        mLinkProperties = new LinkProperties();
+
         mP2pSupported = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_wifi_p2p_support);
 
@@ -113,7 +136,7 @@
         filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
         mContext.registerReceiver(new WifiStateReceiver(), filter);
 
-   }
+    }
 
     public void connectivityServiceReady() {
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
@@ -300,6 +323,7 @@
                     break;
                 // Ignore
                 case WIFI_DISABLE_USER_ACCEPT:
+                case GROUP_NEGOTIATION_TIMED_OUT:
                     break;
                 default:
                     Slog.e(TAG, "Unhandled message " + message);
@@ -459,6 +483,10 @@
                             WifiP2pManager.ENABLE_P2P_SUCCEEDED);
                     transitionTo(mInactiveState);
                     break;
+                case WifiP2pManager.DISABLE_P2P:
+                    //TODO: fix
+                    WifiNative.killSupplicant();
+                    transitionTo(mP2pDisabledState);
                 default:
                     return NOT_HANDLED;
             }
@@ -471,6 +499,7 @@
         public void enter() {
             if (DBG) Slog.d(TAG, getName());
             sendP2pStateChangedBroadcast(true);
+            mNetworkInfo.setIsAvailable(true);
         }
 
         @Override
@@ -526,6 +555,7 @@
         @Override
         public void exit() {
             sendP2pStateChangedBroadcast(false);
+            mNetworkInfo.setIsAvailable(false);
         }
     }
 
@@ -550,6 +580,8 @@
                     WifiP2pGroup group = (WifiP2pGroup) message.obj;
                     notifyP2pInvitationReceived(group);
                     break;
+                case WifiP2pManager.REQUEST_PEERS:
+                    return NOT_HANDLED;
                default:
                     return NOT_HANDLED;
             }
@@ -558,8 +590,11 @@
     }
 
     class GroupNegotiationState extends State {
-        @Override public void enter() {
+        @Override
+        public void enter() {
             if (DBG) Slog.d(TAG, getName());
+            sendMessageDelayed(obtainMessage(GROUP_NEGOTIATION_TIMED_OUT,
+                    ++mGroupNegotiationTimeoutIndex, 0), GROUP_NEGOTIATION_WAIT_TIME_MS);
         }
 
         @Override
@@ -582,18 +617,29 @@
                 case WifiMonitor.P2P_GROUP_STARTED_EVENT:
                     mGroup = (WifiP2pGroup) message.obj;
                     if (DBG) Slog.d(TAG, getName() + " group started");
-                    // If this device is GO, do nothing since there is a follow up
-                    // AP_STA_CONNECTED event
-                    if (!mGroup.isGroupOwner()) {
+                    if (mGroup.isGroupOwner()) {
+                        startDhcpServer(mGroup.getInterface());
+                    } else {
+                        mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext,
+                                P2pStateMachine.this, mGroup.getInterface());
+                        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
                         WifiP2pDevice groupOwner = mGroup.getOwner();
                         updateDeviceStatus(groupOwner.deviceAddress, Status.CONNECTED);
                         sendP2pPeersChangedBroadcast();
                     }
                     transitionTo(mGroupCreatedState);
                     break;
-               case WifiP2pManager.CANCEL_CONNECT:
+                case WifiP2pManager.CANCEL_CONNECT:
                     // TODO: fix
                     break;
+                case GROUP_NEGOTIATION_TIMED_OUT:
+                    if (mGroupNegotiationTimeoutIndex == message.arg1) {
+                        if (DBG) Slog.d(TAG, "Group negotiation timed out");
+                        updateDeviceStatus(mSavedConnectConfig.deviceAddress, Status.FAILED);
+                        mSavedConnectConfig = null;
+                        transitionTo(mInactiveState);
+                    }
+                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -605,6 +651,11 @@
         @Override
         public void enter() {
             if (DBG) Slog.d(TAG, getName());
+            mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+
+            if (mGroup.isGroupOwner()) {
+                sendP2pConnectionChangedBroadcast();
+            }
         }
 
         @Override
@@ -638,7 +689,16 @@
                     }
                     if (DBG) Slog.e(TAG, getName() + " ap sta disconnected");
                     break;
-                // Disconnect & remove group have same effect when connected
+                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
+                    DhcpInfoInternal dhcpInfo = (DhcpInfoInternal) message.obj;
+                    if (DBG) Slog.d(TAG, "DhcpInfo: " + dhcpInfo);
+                    if (dhcpInfo != null) {
+                        mLinkProperties = dhcpInfo.makeLinkProperties();
+                        mLinkProperties.setInterfaceName(mGroup.getInterface());
+                        sendP2pConnectionChangedBroadcast();
+                    }
+                    break;
+                //disconnect & remove group have same effect when connected
                 case WifiP2pManager.CANCEL_CONNECT:
                 case WifiP2pManager.REMOVE_GROUP:
                     if (DBG) Slog.e(TAG, getName() + " remove group");
@@ -655,6 +715,16 @@
                             changed = true;
                         }
                     }
+
+                    if (mGroup.isGroupOwner()) {
+                        stopDhcpServer();
+                    } else {
+                        if (DBG) Slog.d(TAG, "stop DHCP client");
+                        mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
+                        mDhcpStateMachine.quit();
+                        mDhcpStateMachine = null;
+                    }
+
                     mGroup = null;
                     if (changed) sendP2pPeersChangedBroadcast();
                     transitionTo(mInactiveState);
@@ -663,10 +733,12 @@
                     WifiP2pDevice device = (WifiP2pDevice) message.obj;
                     if (device.equals(mGroup.getOwner())) {
                         Slog.d(TAG, "Lost the group owner, killing p2p connection");
-                        sendMessage(WifiP2pManager.REMOVE_GROUP);
+                        WifiNative.p2pFlush();
+                        WifiNative.p2pGroupRemove(mGroup.getInterface());
                     } else if (mGroup.removeClient(device) && mGroup.isClientListEmpty()) {
                         Slog.d(TAG, "Client list empty, killing p2p connection");
-                        sendMessage(WifiP2pManager.REMOVE_GROUP);
+                        WifiNative.p2pFlush();
+                        WifiNative.p2pGroupRemove(mGroup.getInterface());
                     }
                     return NOT_HANDLED; // Do the regular device lost handling
                 case WifiP2pManager.DISABLE_P2P:
@@ -705,6 +777,10 @@
             }
             return HANDLED;
         }
+
+        public void exit() {
+            mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
+        }
     }
 
     private void sendP2pStateChangedBroadcast(boolean enabled) {
@@ -726,6 +802,55 @@
         mContext.sendBroadcast(intent);
     }
 
+    private void sendP2pConnectionChangedBroadcast() {
+        if (DBG) Slog.d(TAG, "sending p2p connection changed broadcast");
+        Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
+        intent.putExtra(WifiP2pManager.EXTRA_LINK_PROPERTIES,
+                new LinkProperties (mLinkProperties));
+        mContext.sendStickyBroadcast(intent);
+    }
+
+    private void startDhcpServer(String intf) {
+        /* Is chosen as a unique range to avoid conflict with
+           the range defined in Tethering.java */
+        String[] dhcp_range = {"192.168.49.2", "192.168.49.254"};
+        String serverAddress = "192.168.49.1";
+
+        mLinkProperties.clear();
+        mLinkProperties.setInterfaceName(mGroup.getInterface());
+
+        InterfaceConfiguration ifcg = null;
+        try {
+            ifcg = mNwService.getInterfaceConfig(intf);
+            ifcg.addr = new LinkAddress(NetworkUtils.numericToInetAddress(
+                        serverAddress), 24);
+            ifcg.interfaceFlags = "[up]";
+            mNwService.setInterfaceConfig(intf, ifcg);
+            /* This starts the dnsmasq server */
+            mNwService.startTethering(dhcp_range);
+        } catch (Exception e) {
+            Slog.e(TAG, "Error configuring interface " + intf + ", :" + e);
+            return;
+        }
+
+        mLinkProperties.addDns(NetworkUtils.numericToInetAddress(serverAddress));
+        Slog.d(TAG, "Started Dhcp server on " + intf);
+    }
+
+    private void stopDhcpServer() {
+        try {
+            mNwService.stopTethering();
+        } catch (Exception e) {
+            Slog.e(TAG, "Error stopping Dhcp server" + e);
+            return;
+        }
+
+        Slog.d(TAG, "Stopped Dhcp server");
+    }
+
     private void notifyP2pEnableFailure() {
         Resources r = Resources.getSystem();
         AlertDialog dialog = new AlertDialog.Builder(mContext)
@@ -760,11 +885,16 @@
             .setView(textEntryView)
             .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
                         public void onClick(DialogInterface dialog, int which) {
-                                if (DBG) Slog.d(TAG, getName() + " connect " + pin.getText());
+                            if (DBG) Slog.d(TAG, getName() + " connect " + pin.getText());
+
+                            if (pin.getVisibility() == View.GONE) {
+                                mSavedGoNegotiationConfig.wpsConfig.setup = Setup.PBC;
+                            } else {
                                 mSavedGoNegotiationConfig.wpsConfig.setup = Setup.KEYPAD;
                                 mSavedGoNegotiationConfig.wpsConfig.pin = pin.getText().toString();
-                                sendMessage(WifiP2pManager.CONNECT, mSavedGoNegotiationConfig);
-                                mSavedGoNegotiationConfig = null;
+                            }
+                            sendMessage(WifiP2pManager.CONNECT, mSavedGoNegotiationConfig);
+                            mSavedGoNegotiationConfig = null;
                         }
                     })
             .setNegativeButton(r.getString(R.string.cancel), new OnClickListener() {