Merge "Bug 5166707: NPE fixed in getFocusedRect"
diff --git a/api/current.txt b/api/current.txt
index 88a708c..01b07a6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11689,6 +11689,7 @@
     method public static long getUidUdpRxPackets(int);
     method public static long getUidUdpTxBytes(int);
     method public static long getUidUdpTxPackets(int);
+    method public static void incrementOperationCount(int);
     method public static void incrementOperationCount(int, int);
     method public static void setThreadStatsTag(int);
     method public static deprecated void setThreadStatsTag(java.lang.String);
@@ -16685,6 +16686,7 @@
     field public static final java.lang.String ACCOUNT_NAME = "account_name";
     field public static final java.lang.String ACCOUNT_TYPE = "account_type";
     field public static final java.lang.String COMMENTS = "comments";
+    field public static final java.lang.String CONTACT_ID = "contact_id";
     field public static final java.lang.String DATA_SET = "data_set";
     field public static final java.lang.String RAW_CONTACT_ID = "raw_contact_id";
     field public static final java.lang.String RAW_CONTACT_SOURCE_ID = "raw_contact_source_id";
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index ee91c29..aa3bc03 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "stream"
+#include "utils/Log.h"
+
 #include <binder/ProcessState.h>
 
 #include <media/IStreamSource.h>
@@ -50,7 +54,7 @@
 private:
     int mFd;
     off64_t mFileSize;
-    int64_t mNextSeekTimeUs;
+    uint64_t mNumPacketsSent;
 
     sp<IStreamListener> mListener;
     Vector<sp<IMemory> > mBuffers;
@@ -61,7 +65,7 @@
 MyStreamSource::MyStreamSource(int fd)
     : mFd(fd),
       mFileSize(0),
-      mNextSeekTimeUs(-1) {  // ALooper::GetNowUs() + 5000000ll) {
+      mNumPacketsSent(0) {
     CHECK_GE(fd, 0);
 
     mFileSize = lseek64(fd, 0, SEEK_END);
@@ -84,18 +88,24 @@
 void MyStreamSource::onBufferAvailable(size_t index) {
     CHECK_LT(index, mBuffers.size());
 
-    if (mNextSeekTimeUs >= 0 && mNextSeekTimeUs <= ALooper::GetNowUs()) {
-        off64_t offset = (off64_t)(((float)rand() / RAND_MAX) * mFileSize * 0.8);
-        offset = (offset / 188) * 188;
+#if 0
+    if (mNumPacketsSent >= 20000) {
+        LOGI("signalling discontinuity now");
+
+        off64_t offset = 0;
+        CHECK((offset % 188) == 0);
 
         lseek(mFd, offset, SEEK_SET);
 
-        mListener->issueCommand(
-                IStreamListener::DISCONTINUITY, false /* synchronous */);
+        sp<AMessage> extra = new AMessage;
+        extra->setInt32(IStreamListener::kKeyFormatChange, 0);
 
-        mNextSeekTimeUs = -1;
-        mNextSeekTimeUs = ALooper::GetNowUs() + 5000000ll;
+        mListener->issueCommand(
+                IStreamListener::DISCONTINUITY, false /* synchronous */, extra);
+
+        mNumPacketsSent = 0;
     }
+#endif
 
     sp<IMemory> mem = mBuffers.itemAt(index);
 
@@ -104,6 +114,8 @@
         mListener->issueCommand(IStreamListener::EOS, false /* synchronous */);
     } else {
         mListener->queueBuffer(index, n);
+
+        mNumPacketsSent += n / 188;
     }
 }
 ////////////////////////////////////////////////////////////////////////////////
@@ -293,12 +305,17 @@
     sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient;
     CHECK_EQ(composerClient->initCheck(), (status_t)OK);
 
+    ssize_t displayWidth = composerClient->getDisplayWidth(0);
+    ssize_t displayHeight = composerClient->getDisplayHeight(0);
+
+    LOGV("display is %d x %d\n", displayWidth, displayHeight);
+
     sp<SurfaceControl> control =
         composerClient->createSurface(
                 String8("A Surface"),
                 0,
-                1280,
-                800,
+                displayWidth,
+                displayHeight,
                 PIXEL_FORMAT_RGB_565,
                 0);
 
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 272545d..5b883a0 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -313,6 +313,22 @@
     }
 
     /**
+     * Return total bytes represented by this snapshot object, usually used when
+     * checking if a {@link #subtract(NetworkStats)} delta passes a threshold.
+     */
+    public long getTotalBytes() {
+        long totalBytes = 0;
+        for (int i = 0; i < size; i++) {
+            // skip specific tags, since already counted in TAG_NONE
+            if (tag[i] != TAG_NONE) continue;
+
+            totalBytes += rxBytes[i];
+            totalBytes += txBytes[i];
+        }
+        return totalBytes;
+    }
+
+    /**
      * Subtract the given {@link NetworkStats}, effectively leaving the delta
      * between two snapshots in time. Assumes that statistics rows collect over
      * time, and that none of them have disappeared.
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index b4f15ac..b19949e 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -53,18 +53,21 @@
 public class NetworkStatsHistory implements Parcelable {
     private static final int VERSION_INIT = 1;
     private static final int VERSION_ADD_PACKETS = 2;
+    private static final int VERSION_ADD_ACTIVE = 3;
 
-    public static final int FIELD_RX_BYTES = 0x01;
-    public static final int FIELD_RX_PACKETS = 0x02;
-    public static final int FIELD_TX_BYTES = 0x04;
-    public static final int FIELD_TX_PACKETS = 0x08;
-    public static final int FIELD_OPERATIONS = 0x10;
+    public static final int FIELD_ACTIVE_TIME = 0x01;
+    public static final int FIELD_RX_BYTES = 0x02;
+    public static final int FIELD_RX_PACKETS = 0x04;
+    public static final int FIELD_TX_BYTES = 0x08;
+    public static final int FIELD_TX_PACKETS = 0x10;
+    public static final int FIELD_OPERATIONS = 0x20;
 
     public static final int FIELD_ALL = 0xFFFFFFFF;
 
     private long bucketDuration;
     private int bucketCount;
     private long[] bucketStart;
+    private long[] activeTime;
     private long[] rxBytes;
     private long[] rxPackets;
     private long[] txBytes;
@@ -74,8 +77,9 @@
     public static class Entry {
         public static final long UNKNOWN = -1;
 
-        public long bucketStart;
         public long bucketDuration;
+        public long bucketStart;
+        public long activeTime;
         public long rxBytes;
         public long rxPackets;
         public long txBytes;
@@ -94,6 +98,7 @@
     public NetworkStatsHistory(long bucketDuration, int initialSize, int fields) {
         this.bucketDuration = bucketDuration;
         bucketStart = new long[initialSize];
+        if ((fields & FIELD_ACTIVE_TIME) != 0) activeTime = new long[initialSize];
         if ((fields & FIELD_RX_BYTES) != 0) rxBytes = new long[initialSize];
         if ((fields & FIELD_RX_PACKETS) != 0) rxPackets = new long[initialSize];
         if ((fields & FIELD_TX_BYTES) != 0) txBytes = new long[initialSize];
@@ -105,6 +110,7 @@
     public NetworkStatsHistory(Parcel in) {
         bucketDuration = in.readLong();
         bucketStart = readLongArray(in);
+        activeTime = readLongArray(in);
         rxBytes = readLongArray(in);
         rxPackets = readLongArray(in);
         txBytes = readLongArray(in);
@@ -117,6 +123,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeLong(bucketDuration);
         writeLongArray(out, bucketStart, bucketCount);
+        writeLongArray(out, activeTime, bucketCount);
         writeLongArray(out, rxBytes, bucketCount);
         writeLongArray(out, rxPackets, bucketCount);
         writeLongArray(out, txBytes, bucketCount);
@@ -138,9 +145,12 @@
                 bucketCount = bucketStart.length;
                 break;
             }
-            case VERSION_ADD_PACKETS: {
+            case VERSION_ADD_PACKETS:
+            case VERSION_ADD_ACTIVE: {
                 bucketDuration = in.readLong();
                 bucketStart = readVarLongArray(in);
+                activeTime = (version >= VERSION_ADD_ACTIVE) ? readVarLongArray(in)
+                        : new long[bucketStart.length];
                 rxBytes = readVarLongArray(in);
                 rxPackets = readVarLongArray(in);
                 txBytes = readVarLongArray(in);
@@ -156,9 +166,10 @@
     }
 
     public void writeToStream(DataOutputStream out) throws IOException {
-        out.writeInt(VERSION_ADD_PACKETS);
+        out.writeInt(VERSION_ADD_ACTIVE);
         out.writeLong(bucketDuration);
         writeVarLongArray(out, bucketStart, bucketCount);
+        writeVarLongArray(out, activeTime, bucketCount);
         writeVarLongArray(out, rxBytes, bucketCount);
         writeVarLongArray(out, rxPackets, bucketCount);
         writeVarLongArray(out, txBytes, bucketCount);
@@ -202,6 +213,7 @@
         final Entry entry = recycle != null ? recycle : new Entry();
         entry.bucketStart = bucketStart[i];
         entry.bucketDuration = bucketDuration;
+        entry.activeTime = getLong(activeTime, i, UNKNOWN);
         entry.rxBytes = getLong(rxBytes, i, UNKNOWN);
         entry.rxPackets = getLong(rxPackets, i, UNKNOWN);
         entry.txBytes = getLong(txBytes, i, UNKNOWN);
@@ -252,8 +264,9 @@
             final long fracRxPackets = entry.rxPackets * overlap / duration;
             final long fracTxBytes = entry.txBytes * overlap / duration;
             final long fracTxPackets = entry.txPackets * overlap / duration;
-            final int fracOperations = (int) (entry.operations * overlap / duration);
+            final long fracOperations = entry.operations * overlap / duration;
 
+            addLong(activeTime, i, overlap);
             addLong(rxBytes, i, fracRxBytes); entry.rxBytes -= fracRxBytes;
             addLong(rxPackets, i, fracRxPackets); entry.rxPackets -= fracRxPackets;
             addLong(txBytes, i, fracTxBytes); entry.txBytes -= fracTxBytes;
@@ -311,6 +324,7 @@
         if (bucketCount >= bucketStart.length) {
             final int newLength = Math.max(bucketStart.length, 10) * 3 / 2;
             bucketStart = Arrays.copyOf(bucketStart, newLength);
+            if (activeTime != null) activeTime = Arrays.copyOf(activeTime, newLength);
             if (rxBytes != null) rxBytes = Arrays.copyOf(rxBytes, newLength);
             if (rxPackets != null) rxPackets = Arrays.copyOf(rxPackets, newLength);
             if (txBytes != null) txBytes = Arrays.copyOf(txBytes, newLength);
@@ -324,6 +338,7 @@
             final int length = bucketCount - index;
 
             System.arraycopy(bucketStart, index, bucketStart, dstPos, length);
+            if (activeTime != null) System.arraycopy(activeTime, index, activeTime, dstPos, length);
             if (rxBytes != null) System.arraycopy(rxBytes, index, rxBytes, dstPos, length);
             if (rxPackets != null) System.arraycopy(rxPackets, index, rxPackets, dstPos, length);
             if (txBytes != null) System.arraycopy(txBytes, index, txBytes, dstPos, length);
@@ -332,6 +347,7 @@
         }
 
         bucketStart[index] = start;
+        setLong(activeTime, index, 0L);
         setLong(rxBytes, index, 0L);
         setLong(rxPackets, index, 0L);
         setLong(txBytes, index, 0L);
@@ -357,6 +373,7 @@
         if (i > 0) {
             final int length = bucketStart.length;
             bucketStart = Arrays.copyOfRange(bucketStart, i, length);
+            if (activeTime != null) activeTime = Arrays.copyOfRange(activeTime, i, length);
             if (rxBytes != null) rxBytes = Arrays.copyOfRange(rxBytes, i, length);
             if (rxPackets != null) rxPackets = Arrays.copyOfRange(rxPackets, i, length);
             if (txBytes != null) txBytes = Arrays.copyOfRange(txBytes, i, length);
@@ -380,8 +397,9 @@
      */
     public Entry getValues(long start, long end, long now, Entry recycle) {
         final Entry entry = recycle != null ? recycle : new Entry();
-        entry.bucketStart = start;
         entry.bucketDuration = end - start;
+        entry.bucketStart = start;
+        entry.activeTime = activeTime != null ? 0 : UNKNOWN;
         entry.rxBytes = rxBytes != null ? 0 : UNKNOWN;
         entry.rxPackets = rxPackets != null ? 0 : UNKNOWN;
         entry.txBytes = txBytes != null ? 0 : UNKNOWN;
@@ -404,6 +422,7 @@
             if (overlap <= 0) continue;
 
             // integer math each time is faster than floating point
+            if (activeTime != null) entry.activeTime += activeTime[i] * overlap / bucketDuration;
             if (rxBytes != null) entry.rxBytes += rxBytes[i] * overlap / bucketDuration;
             if (rxPackets != null) entry.rxPackets += rxPackets[i] * overlap / bucketDuration;
             if (txBytes != null) entry.txBytes += txBytes[i] * overlap / bucketDuration;
@@ -463,6 +482,7 @@
         for (int i = start; i < bucketCount; i++) {
             pw.print(prefix);
             pw.print("  bucketStart="); pw.print(bucketStart[i]);
+            if (activeTime != null) pw.print(" activeTime="); pw.print(activeTime[i]);
             if (rxBytes != null) pw.print(" rxBytes="); pw.print(rxBytes[i]);
             if (rxPackets != null) pw.print(" rxPackets="); pw.print(rxPackets[i]);
             if (txBytes != null) pw.print(" txBytes="); pw.print(txBytes[i]);
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index c2c5c18..ec3b1e1 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -198,6 +198,18 @@
     }
 
     /**
+     * Increment count of network operations performed under the accounting tag
+     * currently active on the calling thread. This can be used to derive
+     * bytes-per-operation.
+     *
+     * @param operationCount Number of operations to increment count by.
+     */
+    public static void incrementOperationCount(int operationCount) {
+        final int tag = getThreadStatsTag();
+        incrementOperationCount(tag, operationCount);
+    }
+
+    /**
      * Increment count of network operations performed under the given
      * accounting tag. This can be used to derive bytes-per-operation.
      *
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 55f3b4a..e205f97 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -3060,6 +3060,12 @@
      */
     protected interface StreamItemsColumns {
         /**
+         * A reference to the {@link android.provider.ContactsContract.Contacts#_ID}
+         * that this stream item belongs to.
+         */
+        public static final String CONTACT_ID = "contact_id";
+
+        /**
          * A reference to the {@link RawContacts#_ID}
          * that this stream item belongs to.
          */
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index cfbb47c..c934c7b 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -541,9 +541,19 @@
     
     @Override
     public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
-        return saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint, saveFlags);
+        if (bounds != null) {
+            return saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint, saveFlags);
+        }
+
+        int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
+        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
+        int count = nSaveLayer(mRenderer, nativePaint, saveFlags);
+        if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
+        return count;
     }
 
+    private static native int nSaveLayer(int renderer, int paint, int saveFlags);    
+
     @Override
     public int saveLayer(float left, float top, float right, float bottom, Paint paint,
             int saveFlags) {
@@ -562,10 +572,15 @@
 
     @Override
     public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) {
-        return saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom,
-                alpha, saveFlags);
+        if (bounds != null) {
+            return saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom,
+                    alpha, saveFlags);
+        }
+        return nSaveLayerAlpha(mRenderer, alpha, saveFlags);
     }
 
+    private static native int nSaveLayerAlpha(int renderer, int alpha, int saveFlags);    
+
     @Override
     public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
             int saveFlags) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 615aaae..c92b5f9 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7634,12 +7634,6 @@
                 getSpellChecker().addSpellCheckSpan((SpellCheckSpan) what);
             }
         }
-
-        if (what instanceof SuggestionSpan) {
-            if (newStart < 0) {
-                Log.d("spellcheck", "REMOVE suggspan " + mText.subSequence(oldStart, oldEnd));
-            }
-        }
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 4efb29f..8988c9f 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -826,15 +826,18 @@
             rightOfCenter = Math.max(0, rightOfCenter - mMenuView.getMeasuredWidth());
         }
 
-        if (mExpandedActionView == null) {
-            boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE &&
-                    (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
-            if (showTitle) {
-                availableWidth = measureChildView(mTitleLayout, availableWidth,
-                        MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY), 0);
-                leftOfCenter = Math.max(0, leftOfCenter - mTitleLayout.getMeasuredWidth());
-            }
+        if (mIndeterminateProgressView != null &&
+                mIndeterminateProgressView.getVisibility() != GONE) {
+            availableWidth = measureChildView(mIndeterminateProgressView, availableWidth,
+                    childSpecHeight, 0);
+            rightOfCenter = Math.max(0,
+                    rightOfCenter - mIndeterminateProgressView.getMeasuredWidth());
+        }
 
+        final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE &&
+                (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
+
+        if (mExpandedActionView == null) {
             switch (mNavigationMode) {
                 case ActionBar.NAVIGATION_MODE_LIST:
                     if (mListNavLayout != null) {
@@ -865,14 +868,6 @@
             }
         }
 
-        if (mIndeterminateProgressView != null &&
-                mIndeterminateProgressView.getVisibility() != GONE) {
-            availableWidth = measureChildView(mIndeterminateProgressView, availableWidth,
-                    childSpecHeight, 0);
-            rightOfCenter = Math.max(0,
-                    rightOfCenter - mIndeterminateProgressView.getMeasuredWidth());
-        }
-
         View customView = null;
         if (mExpandedActionView != null) {
             customView = mExpandedActionView;
@@ -922,6 +917,13 @@
             customView.measure(
                     MeasureSpec.makeMeasureSpec(customNavWidth, customNavWidthMode),
                     MeasureSpec.makeMeasureSpec(customNavHeight, customNavHeightMode));
+            availableWidth -= horizontalMargin + customView.getMeasuredWidth();
+        }
+
+        if (mExpandedActionView == null && showTitle) {
+            availableWidth = measureChildView(mTitleLayout, availableWidth,
+                    MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY), 0);
+            leftOfCenter = Math.max(0, leftOfCenter - mTitleLayout.getMeasuredWidth());
         }
 
         if (mContentHeight <= 0) {
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index fb5e5fe..bcf8e71 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -222,12 +222,26 @@
     return renderer->saveLayer(left, top, right, bottom, paint, saveFlags);
 }
 
+static jint android_view_GLES20Canvas_saveLayerClip(JNIEnv* env, jobject clazz,
+        OpenGLRenderer* renderer, SkPaint* paint, jint saveFlags) {
+    const android::uirenderer::Rect& bounds(renderer->getClipBounds());
+    return renderer->saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom,
+            paint, saveFlags);
+}
+
 static jint android_view_GLES20Canvas_saveLayerAlpha(JNIEnv* env, jobject clazz,
         OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
         jint alpha, jint saveFlags) {
     return renderer->saveLayerAlpha(left, top, right, bottom, alpha, saveFlags);
 }
 
+static jint android_view_GLES20Canvas_saveLayerAlphaClip(JNIEnv* env, jobject clazz,
+        OpenGLRenderer* renderer, jint alpha, jint saveFlags) {
+    const android::uirenderer::Rect& bounds(renderer->getClipBounds());
+    return renderer->saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom,
+            alpha, saveFlags);
+}
+
 // ----------------------------------------------------------------------------
 // Clipping
 // ----------------------------------------------------------------------------
@@ -759,7 +773,9 @@
     { "nGetSaveCount",      "(I)I",            (void*) android_view_GLES20Canvas_getSaveCount },
 
     { "nSaveLayer",         "(IFFFFII)I",      (void*) android_view_GLES20Canvas_saveLayer },
+    { "nSaveLayer",         "(III)I",          (void*) android_view_GLES20Canvas_saveLayerClip },
     { "nSaveLayerAlpha",    "(IFFFFII)I",      (void*) android_view_GLES20Canvas_saveLayerAlpha },
+    { "nSaveLayerAlpha",    "(III)I",          (void*) android_view_GLES20Canvas_saveLayerAlphaClip },
 
     { "nQuickReject",       "(IFFFFI)Z",       (void*) android_view_GLES20Canvas_quickReject },
     { "nClipRect",          "(IFFFFI)Z",       (void*) android_view_GLES20Canvas_clipRectF },
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
index 4db4ea5..b888d9a 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
@@ -99,7 +99,7 @@
                 new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L));
 
         assertEquals(1, stats.size());
-        assertValues(stats, 0, 1024L, 10L, 2048L, 20L, 2L);
+        assertValues(stats, 0, SECOND_IN_MILLIS, 1024L, 10L, 2048L, 20L, 2L);
     }
 
     public void testRecordEqualBuckets() throws Exception {
@@ -112,8 +112,8 @@
                 new NetworkStats.Entry(1024L, 10L, 128L, 2L, 2L));
 
         assertEquals(2, stats.size());
-        assertValues(stats, 0, 512L, 5L, 64L, 1L, 1L);
-        assertValues(stats, 1, 512L, 5L, 64L, 1L, 1L);
+        assertValues(stats, 0, HOUR_IN_MILLIS / 2, 512L, 5L, 64L, 1L, 1L);
+        assertValues(stats, 1, HOUR_IN_MILLIS / 2, 512L, 5L, 64L, 1L, 1L);
     }
 
     public void testRecordTouchingBuckets() throws Exception {
@@ -129,11 +129,11 @@
 
         assertEquals(3, stats.size());
         // first bucket should have (1/20 of value)
-        assertValues(stats, 0, 50L, 100L, 250L, 500L, 5L);
+        assertValues(stats, 0, MINUTE_IN_MILLIS, 50L, 100L, 250L, 500L, 5L);
         // second bucket should have (15/20 of value)
-        assertValues(stats, 1, 750L, 1500L, 3750L, 7500L, 75L);
+        assertValues(stats, 1, 15 * MINUTE_IN_MILLIS, 750L, 1500L, 3750L, 7500L, 75L);
         // final bucket should have (4/20 of value)
-        assertValues(stats, 2, 200L, 400L, 1000L, 2000L, 20L);
+        assertValues(stats, 2, 4 * MINUTE_IN_MILLIS, 200L, 400L, 1000L, 2000L, 20L);
     }
 
     public void testRecordGapBuckets() throws Exception {
@@ -150,8 +150,8 @@
 
         // we should have two buckets, far apart from each other
         assertEquals(2, stats.size());
-        assertValues(stats, 0, 128L, 2L, 256L, 4L, 1L);
-        assertValues(stats, 1, 64L, 1L, 512L, 8L, 2L);
+        assertValues(stats, 0, SECOND_IN_MILLIS, 128L, 2L, 256L, 4L, 1L);
+        assertValues(stats, 1, SECOND_IN_MILLIS, 64L, 1L, 512L, 8L, 2L);
 
         // now record something in middle, spread across two buckets
         final long middleStart = TEST_START + DAY_IN_MILLIS;
@@ -161,10 +161,10 @@
 
         // now should have four buckets, with new record in middle two buckets
         assertEquals(4, stats.size());
-        assertValues(stats, 0, 128L, 2L, 256L, 4L, 1L);
-        assertValues(stats, 1, 1024L, 2L, 1024L, 2L, 1L);
-        assertValues(stats, 2, 1024L, 2L, 1024L, 2L, 1L);
-        assertValues(stats, 3, 64L, 1L, 512L, 8L, 2L);
+        assertValues(stats, 0, SECOND_IN_MILLIS, 128L, 2L, 256L, 4L, 1L);
+        assertValues(stats, 1, HOUR_IN_MILLIS, 1024L, 2L, 1024L, 2L, 1L);
+        assertValues(stats, 2, HOUR_IN_MILLIS, 1024L, 2L, 1024L, 2L, 1L);
+        assertValues(stats, 3, SECOND_IN_MILLIS, 64L, 1L, 512L, 8L, 2L);
     }
 
     public void testRecordOverlapBuckets() throws Exception {
@@ -180,8 +180,8 @@
 
         // should have two buckets, with some data mixed together
         assertEquals(2, stats.size());
-        assertValues(stats, 0, 768L, 7L, 768L, 7L, 6L);
-        assertValues(stats, 1, 512L, 5L, 512L, 5L, 5L);
+        assertValues(stats, 0, SECOND_IN_MILLIS + (HOUR_IN_MILLIS / 2), 768L, 7L, 768L, 7L, 6L);
+        assertValues(stats, 1, (HOUR_IN_MILLIS / 2), 512L, 5L, 512L, 5L, 5L);
     }
 
     public void testRecordEntireGapIdentical() throws Exception {
@@ -345,11 +345,10 @@
 
         history.recordData(0, MINUTE_IN_MILLIS,
                 new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L));
-        history.recordData(0, MINUTE_IN_MILLIS * 2,
+        history.recordData(0, 2 * MINUTE_IN_MILLIS,
                 new NetworkStats.Entry(2L, 2L, 2L, 2L, 2L));
 
-        assertValues(
-                history, Long.MIN_VALUE, Long.MAX_VALUE, 1026L, UNKNOWN, 2050L, UNKNOWN, UNKNOWN);
+        assertFullValues(history, UNKNOWN, 1026L, UNKNOWN, 2050L, UNKNOWN, UNKNOWN);
     }
 
     public void testIgnoreFieldsRecordIn() throws Exception {
@@ -361,7 +360,7 @@
                 new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L));
         partial.recordEntireHistory(full);
 
-        assertValues(partial, Long.MIN_VALUE, Long.MAX_VALUE, UNKNOWN, 10L, UNKNOWN, UNKNOWN, 4L);
+        assertFullValues(partial, UNKNOWN, UNKNOWN, 10L, UNKNOWN, UNKNOWN, 4L);
     }
 
     public void testIgnoreFieldsRecordOut() throws Exception {
@@ -373,12 +372,12 @@
                 new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L));
         full.recordEntireHistory(partial);
 
-        assertValues(full, Long.MIN_VALUE, Long.MAX_VALUE, 0L, 10L, 0L, 0L, 4L);
+        assertFullValues(full, MINUTE_IN_MILLIS, 0L, 10L, 0L, 0L, 4L);
     }
 
     public void testSerialize() throws Exception {
         final NetworkStatsHistory before = new NetworkStatsHistory(MINUTE_IN_MILLIS, 40, FIELD_ALL);
-        before.recordData(0, MINUTE_IN_MILLIS * 4,
+        before.recordData(0, 4 * MINUTE_IN_MILLIS,
                 new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L));
         before.recordData(DAY_IN_MILLIS, DAY_IN_MILLIS + MINUTE_IN_MILLIS,
                 new NetworkStats.Entry(10L, 20L, 30L, 40L, 50L));
@@ -391,8 +390,8 @@
         final NetworkStatsHistory after = new NetworkStatsHistory(new DataInputStream(in));
 
         // must have identical totals before and after
-        assertValues(before, Long.MIN_VALUE, Long.MAX_VALUE, 1034L, 30L, 2078L, 60L, 54L);
-        assertValues(after, Long.MIN_VALUE, Long.MAX_VALUE, 1034L, 30L, 2078L, 60L, 54L);
+        assertFullValues(before, 5 * MINUTE_IN_MILLIS, 1034L, 30L, 2078L, 60L, 54L);
+        assertFullValues(after, 5 * MINUTE_IN_MILLIS, 1034L, 30L, 2078L, 60L, 54L);
     }
 
     public void testVarLong() throws Exception {
@@ -441,9 +440,10 @@
         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
     }
 
-    private static void assertValues(NetworkStatsHistory stats, int index, long rxBytes,
-            long rxPackets, long txBytes, long txPackets, long operations) {
+    private static void assertValues(NetworkStatsHistory stats, int index, long activeTime,
+            long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
         final NetworkStatsHistory.Entry entry = stats.getValues(index, null);
+        assertEquals("unexpected activeTime", activeTime, entry.activeTime);
         assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
         assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
@@ -451,9 +451,17 @@
         assertEquals("unexpected operations", operations, entry.operations);
     }
 
-    private static void assertValues(NetworkStatsHistory stats, long start, long end, long rxBytes,
+    private static void assertFullValues(NetworkStatsHistory stats, long activeTime, long rxBytes,
             long rxPackets, long txBytes, long txPackets, long operations) {
+        assertValues(stats, Long.MIN_VALUE, Long.MAX_VALUE, activeTime, rxBytes, rxPackets, txBytes,
+                txPackets, operations);
+    }
+
+    private static void assertValues(NetworkStatsHistory stats, long start, long end,
+            long activeTime, long rxBytes, long rxPackets, long txBytes, long txPackets,
+            long operations) {
         final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null);
+        assertEquals("unexpected activeTime", activeTime, entry.activeTime);
         assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
         assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
         assertEquals("unexpected txBytes", txBytes, entry.txBytes);
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index 47ba88a..c36685d 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -17,7 +17,9 @@
 package android.net;
 
 import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.SET_FOREGROUND;
 import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStats.UID_ALL;
 
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -27,6 +29,7 @@
 public class NetworkStatsTest extends TestCase {
 
     private static final String TEST_IFACE = "test0";
+    private static final String TEST_IFACE2 = "test2";
     private static final int TEST_UID = 1001;
     private static final long TEST_START = 1194220800000L;
 
@@ -135,6 +138,44 @@
         assertValues(result, 2, TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 20);
     }
 
+    public void testSubtractMissingRows() throws Exception {
+        final NetworkStats before = new NetworkStats(TEST_START, 2)
+                .addValues(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 1024L, 0L, 0L, 0L, 0)
+                .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2048L, 0L, 0L, 0L, 0);
+
+        final NetworkStats after = new NetworkStats(TEST_START, 1)
+                .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 2049L, 2L, 3L, 4L, 0);
+
+        final NetworkStats result = after.subtract(before);
+
+        // should silently drop omitted rows
+        assertEquals(1, result.size());
+        assertValues(result, 0, TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 1L, 2L, 3L, 4L, 0);
+        assertEquals(4L, result.getTotalBytes());
+    }
+
+    public void testTotalBytes() throws Exception {
+        final NetworkStats iface = new NetworkStats(TEST_START, 2)
+                .addValues(TEST_IFACE, UID_ALL, SET_DEFAULT, TAG_NONE, 128L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, UID_ALL, SET_DEFAULT, TAG_NONE, 256L, 0L, 0L, 0L, 0L);
+        assertEquals(384L, iface.getTotalBytes());
+
+        final NetworkStats uidSet = new NetworkStats(TEST_START, 3)
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 32L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_FOREGROUND, TAG_NONE, 32L, 0L, 0L, 0L, 0L);
+        assertEquals(96L, uidSet.getTotalBytes());
+
+        final NetworkStats uidTag = new NetworkStats(TEST_START, 3)
+                .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 16L, 0L, 0L, 0L, 0L)
+                .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 8L, 0L, 0L, 0L, 0L);
+        assertEquals(64L, uidTag.getTotalBytes());
+    }
+
     private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set,
             int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, int operations) {
         final NetworkStats.Entry entry = stats.getValues(index, null);
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index 18f47a6..bbf7cbe 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -22,6 +22,11 @@
     </li>
     <li><a href="#updating">Updating the ADT Plugin</a></li>
   </ol>
+  
+  <h2>See also</h2>
+  <ol>
+    <li><a href="{@docRoot}guide/developing/tools/adt.html">Android Developer Tools</a></li>
+  </ol>
 
 </div>
 </div>
@@ -50,6 +55,12 @@
 how to update ADT to the latest version or how to uninstall it, if necessary.
 </p>
 
+<p>For information about the features provided by the ADT plugin, such as code
+editor features, SDK tool integration, and the graphical layout editor (for drag-and-drop layout
+editing), see the <a href="{@docRoot}guide/developing/tools/adt.html">Android Developer Tools</a>
+document.</p>
+
+
 <h2 id="notes">Revisions</h2>
 
 <p>The sections below provide notes about successive releases of
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 2e66a2c..ce07e32 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -136,25 +136,29 @@
 void AnotherPacketSource::queueDiscontinuity(
         ATSParser::DiscontinuityType type,
         const sp<AMessage> &extra) {
+    Mutex::Autolock autoLock(mLock);
+
+    // Leave only discontinuities in the queue.
+    List<sp<ABuffer> >::iterator it = mBuffers.begin();
+    while (it != mBuffers.end()) {
+        sp<ABuffer> oldBuffer = *it;
+
+        int32_t oldDiscontinuityType;
+        if (!oldBuffer->meta()->findInt32(
+                    "discontinuity", &oldDiscontinuityType)) {
+            it = mBuffers.erase(it);
+            continue;
+        }
+
+        ++it;
+    }
+
+    mEOSResult = OK;
+
     sp<ABuffer> buffer = new ABuffer(0);
     buffer->meta()->setInt32("discontinuity", static_cast<int32_t>(type));
     buffer->meta()->setMessage("extra", extra);
 
-    Mutex::Autolock autoLock(mLock);
-
-#if 0
-    if (type == ATSParser::DISCONTINUITY_SEEK
-            || type == ATSParser::DISCONTINUITY_FORMATCHANGE) {
-        // XXX Fix this: This will also clear any pending discontinuities,
-        // If there's a pending DISCONTINUITY_FORMATCHANGE and the new
-        // discontinuity is "just" a DISCONTINUITY_SEEK, this will effectively
-        // downgrade the type of discontinuity received by the client.
-
-        mBuffers.clear();
-        mEOSResult = OK;
-    }
-#endif
-
     mBuffers.push_back(buffer);
     mCondition.signal();
 }
diff --git a/packages/SystemUI/res/values-xhdpi/dimens.xml b/packages/SystemUI/res/values-xhdpi/dimens.xml
new file mode 100644
index 0000000..aa75c20
--- /dev/null
+++ b/packages/SystemUI/res/values-xhdpi/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+<resources>
+    <!-- thickness (height) of each notification row, including any separators or padding -->
+    <!-- note: this is the same value as in values/dimens.xml; the value is overridden in
+         values-hdpi/dimens.xml and so we need to re-assert the general value here -->
+    <dimen name="notification_height">65dp</dimen>
+
+    <!-- thickness (height) of dividers between each notification row -->
+    <!-- same as in values/dimens.xml; see note at notification_height -->
+    <dimen name="notification_divider_height">1dp</dimen>
+</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 5a3850d..049a284 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -58,7 +58,7 @@
     private static final boolean DEBUG = false;
 
     static final boolean FIXED_SIZED_SURFACE = true;
-    static final boolean USE_OPENGL = false;
+    static final boolean USE_OPENGL = true;
 
     WallpaperManager mWallpaperManager;
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index be129a8..dfd1b00 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -124,6 +124,7 @@
 import android.view.WindowManagerImpl;
 import android.view.WindowManagerPolicy;
 import android.view.KeyCharacterMap.FallbackAction;
+import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.media.IAudioService;
@@ -3104,7 +3105,29 @@
         mHandler.post(new Runnable() {
             @Override public void run() {
                 if (mBootMsgDialog == null) {
-                    mBootMsgDialog = new ProgressDialog(mContext);
+                    mBootMsgDialog = new ProgressDialog(mContext) {
+                        // This dialog will consume all events coming in to
+                        // it, to avoid it trying to do things too early in boot.
+                        @Override public boolean dispatchKeyEvent(KeyEvent event) {
+                            return true;
+                        }
+                        @Override public boolean dispatchKeyShortcutEvent(KeyEvent event) {
+                            return true;
+                        }
+                        @Override public boolean dispatchTouchEvent(MotionEvent ev) {
+                            return true;
+                        }
+                        @Override public boolean dispatchTrackballEvent(MotionEvent ev) {
+                            return true;
+                        }
+                        @Override public boolean dispatchGenericMotionEvent(MotionEvent ev) {
+                            return true;
+                        }
+                        @Override public boolean dispatchPopulateAccessibilityEvent(
+                                AccessibilityEvent event) {
+                            return true;
+                        }
+                    };
                     mBootMsgDialog.setTitle(R.string.android_upgrading_title);
                     mBootMsgDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                     mBootMsgDialog.setIndeterminate(true);
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 06077dd..e72d09f 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -69,7 +69,8 @@
 /**
  * @hide
  */
-class NetworkManagementService extends INetworkManagementService.Stub implements Watchdog.Monitor {
+public class NetworkManagementService extends INetworkManagementService.Stub
+        implements Watchdog.Monitor {
     private static final String TAG = "NetworkManagementService";
     private static final boolean DBG = false;
     private static final String NETD_TAG = "NetdConnector";
@@ -87,6 +88,12 @@
     /** Path to {@code /proc/net/xt_qtaguid/iface_stat}. */
     private final File mStatsXtIface;
 
+    /**
+     * Name representing {@link #setGlobalAlert(long)} limit when delivered to
+     * {@link INetworkManagementEventObserver#limitReached(String, String)}.
+     */
+    public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
+
     /** {@link #mStatsXtUid} headers. */
     private static final String KEY_IFACE = "iface";
     private static final String KEY_UID = "uid_tag_int";
@@ -1056,8 +1063,12 @@
                     Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
                 }
             }
+        } catch (NullPointerException e) {
+            throw new IllegalStateException("problem parsing stats: " + e);
+        } catch (NumberFormatException e) {
+            throw new IllegalStateException("problem parsing stats: " + e);
         } catch (IOException e) {
-            Slog.w(TAG, "problem parsing stats: " + e);
+            throw new IllegalStateException("problem parsing stats: " + e);
         } finally {
             IoUtils.closeQuietly(reader);
         }
@@ -1338,8 +1349,12 @@
                     Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
                 }
             }
+        } catch (NullPointerException e) {
+            throw new IllegalStateException("problem parsing stats: " + e);
+        } catch (NumberFormatException e) {
+            throw new IllegalStateException("problem parsing stats: " + e);
         } catch (IOException e) {
-            Slog.w(TAG, "problem parsing stats: " + e);
+            throw new IllegalStateException("problem parsing stats: " + e);
         } finally {
             IoUtils.closeQuietly(reader);
         }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e6f92a5..d0e8b5e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -182,7 +182,7 @@
 
             // only initialize the power service after we have started the
             // lights service, content providers and the battery service.
-            power.init(context, lights, ActivityManagerService.getDefault(), battery);
+            power.init(context, lights, ActivityManagerService.self(), battery);
 
             Slog.i(TAG, "Alarm Manager");
             alarm = new AlarmManagerService(context);
@@ -197,8 +197,7 @@
                     factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL);
             ServiceManager.addService(Context.WINDOW_SERVICE, wm);
 
-            ((ActivityManagerService)ServiceManager.getService("activity"))
-                    .setWindowManager(wm);
+            ActivityManagerService.self().setWindowManager(wm);
 
             // Skip Bluetooth if we have an emulator kernel
             // TODO: Use a more reliable check to see if this product should
@@ -265,7 +264,7 @@
         } catch (Throwable e) {
             reportWtf("making display ready", e);
         }
- 
+
         try {
             pm.performBootDexOpt();
         } catch (Throwable e) {
@@ -618,8 +617,7 @@
         // where third party code can really run (but before it has actually
         // started launching the initial applications), for us to complete our
         // initialization.
-        ((ActivityManagerService)ActivityManagerNative.getDefault())
-                .systemReady(new Runnable() {
+        ActivityManagerService.self().systemReady(new Runnable() {
             public void run() {
                 Slog.i(TAG, "Making services ready");
 
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index 24b6ac3..2155147 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -532,9 +532,12 @@
                     mLastRead = 0;
                     mLastWrite = 0;
                 }
+            } catch (IllegalStateException e) {
+                Slog.e(TAG, "problem during onPollAlarm: " + e);
             } catch (RemoteException e) {
-                Slog.e(TAG, "got remoteException in onPollAlarm:" + e);
+                Slog.e(TAG, "problem during onPollAlarm: " + e);
             }
+
             // don't count this data if we're roaming.
             boolean roaming = "true".equals(
                     SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING));
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 7232a94..bb5e989 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -91,7 +91,6 @@
 import android.os.FileObserver;
 import android.os.FileUtils;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.IPermissionController;
 import android.os.Looper;
@@ -5518,6 +5517,48 @@
         return msg;
     }
 
+    boolean incProviderCount(ProcessRecord r, ContentProviderRecord cpr) {
+        if (r != null) {
+            Integer cnt = r.conProviders.get(cpr);
+            if (DEBUG_PROVIDER) Slog.v(TAG,
+                    "Adding provider requested by "
+                    + r.processName + " from process "
+                    + cpr.info.processName + ": " + cpr.name.flattenToShortString()
+                    + " cnt=" + (cnt == null ? 1 : cnt));
+            if (cnt == null) {
+                cpr.clients.add(r);
+                r.conProviders.put(cpr, new Integer(1));
+                return true;
+            } else {
+                r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
+            }
+        } else {
+            cpr.externals++;
+        }
+        return false;
+    }
+
+    boolean decProviderCount(ProcessRecord r, ContentProviderRecord cpr) {
+        if (r != null) {
+            Integer cnt = r.conProviders.get(cpr);
+            if (DEBUG_PROVIDER) Slog.v(TAG,
+                    "Removing provider requested by "
+                    + r.processName + " from process "
+                    + cpr.info.processName + ": " + cpr.name.flattenToShortString()
+                    + " cnt=" + cnt);
+            if (cnt == null || cnt.intValue() <= 1) {
+                cpr.clients.remove(r);
+                r.conProviders.remove(cpr);
+                return true;
+            } else {
+                r.conProviders.put(cpr, new Integer(cnt.intValue()-1));
+            }
+        } else {
+            cpr.externals++;
+        }
+        return false;
+    }
+
     private final ContentProviderHolder getContentProviderImpl(
         IApplicationThread caller, String name) {
         ContentProviderRecord cpr;
@@ -5537,7 +5578,8 @@
 
             // First check if this content provider has been published...
             cpr = mProvidersByName.get(name);
-            if (cpr != null) {
+            boolean providerRunning = cpr != null;
+            if (providerRunning) {
                 cpi = cpr.info;
                 String msg;
                 if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
@@ -5561,18 +5603,8 @@
 
                 // In this case the provider instance already exists, so we can
                 // return it right away.
-                if (r != null) {
-                    if (DEBUG_PROVIDER) Slog.v(TAG,
-                            "Adding provider requested by "
-                            + r.processName + " from process "
-                            + cpr.info.processName);
-                    Integer cnt = r.conProviders.get(cpr);
-                    if (cnt == null) {
-                        r.conProviders.put(cpr, new Integer(1));
-                    } else {
-                        r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
-                    }
-                    cpr.clients.add(r);
+                final boolean countChanged = incProviderCount(r, cpr);
+                if (countChanged) {
                     if (cpr.app != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
                         // If this is a perceptible app accessing the provider,
                         // make sure to count it as being accessed and thus
@@ -5580,17 +5612,46 @@
                         // content providers are often expensive to start.
                         updateLruProcessLocked(cpr.app, false, true);
                     }
-                } else {
-                    cpr.externals++;
                 }
 
                 if (cpr.app != null) {
-                    updateOomAdjLocked(cpr.app);
+                    if (false) {
+                        if (cpr.name.flattenToShortString().equals(
+                                "com.android.providers.calendar/.CalendarProvider2")) {
+                            Slog.v(TAG, "****************** KILLING "
+                                + cpr.name.flattenToShortString());
+                            Process.killProcess(cpr.app.pid);
+                        }
+                    }
+                    boolean success = updateOomAdjLocked(cpr.app);
+                    if (DEBUG_PROVIDER) Slog.i(TAG, "Adjust success: " + success);
+                    // NOTE: there is still a race here where a signal could be
+                    // pending on the process even though we managed to update its
+                    // adj level.  Not sure what to do about this, but at least
+                    // the race is now smaller.
+                    if (!success) {
+                        // Uh oh...  it looks like the provider's process
+                        // has been killed on us.  We need to wait for a new
+                        // process to be started, and make sure its death
+                        // doesn't kill our process.
+                        Slog.i(TAG,
+                                "Existing provider " + cpr.name.flattenToShortString()
+                                + " is crashing; detaching " + r);
+                        boolean lastRef = decProviderCount(r, cpr);
+                        appDiedLocked(cpr.app, cpr.app.pid, cpr.app.thread);
+                        if (!lastRef) {
+                            // This wasn't the last ref our process had on
+                            // the provider...  we have now been killed, bail.
+                            return null;
+                        }
+                        providerRunning = false;
+                    }
                 }
 
                 Binder.restoreCallingIdentity(origId);
+            }
 
-            } else {
+            if (!providerRunning) {
                 try {
                     cpi = AppGlobals.getPackageManager().
                         resolveContentProvider(name,
@@ -5701,22 +5762,7 @@
                     mProvidersByClass.put(comp, cpr);
                 }
                 mProvidersByName.put(name, cpr);
-
-                if (r != null) {
-                    if (DEBUG_PROVIDER) Slog.v(TAG,
-                            "Adding provider requested by "
-                            + r.processName + " from process "
-                            + cpr.info.processName);
-                    Integer cnt = r.conProviders.get(cpr);
-                    if (cnt == null) {
-                        r.conProviders.put(cpr, new Integer(1));
-                    } else {
-                        r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
-                    }
-                    cpr.clients.add(r);
-                } else {
-                    cpr.externals++;
-                }
+                incProviderCount(r, cpr);
             }
         }
 
@@ -5780,24 +5826,16 @@
             //update content provider record entry info
             ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
             ContentProviderRecord localCpr = mProvidersByClass.get(comp);
-            if (DEBUG_PROVIDER) Slog.v(TAG, "Removing provider requested by "
-                    + r.info.processName + " from process "
-                    + localCpr.appInfo.processName);
             if (localCpr.app == r) {
                 //should not happen. taken care of as a local provider
                 Slog.w(TAG, "removeContentProvider called on local provider: "
                         + cpr.info.name + " in process " + r.processName);
                 return;
             } else {
-                Integer cnt = r.conProviders.get(localCpr);
-                if (cnt == null || cnt.intValue() <= 1) {
-                    localCpr.clients.remove(r);
-                    r.conProviders.remove(localCpr);
-                } else {
-                    r.conProviders.put(localCpr, new Integer(cnt.intValue()-1));
+                if (decProviderCount(r, localCpr)) {
+                    updateOomAdjLocked();
                 }
             }
-            updateOomAdjLocked();
         }
     }
 
@@ -13458,16 +13496,18 @@
         }
     }
 
-    private final void updateOomAdjLocked(
+    private final boolean updateOomAdjLocked(
             ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) {
         app.hiddenAdj = hiddenAdj;
 
         if (app.thread == null) {
-            return;
+            return false;
         }
 
         final boolean wasKeeping = app.keeping;
 
+        boolean success = true;
+
         computeOomAdjLocked(app, hiddenAdj, TOP_APP, false);
 
         if (app.curRawAdj != app.setRawAdj) {
@@ -13504,6 +13544,7 @@
                     " oom adj to " + app.curAdj + " because " + app.adjType);
                 app.setAdj = app.curAdj;
             } else {
+                success = false;
                 Slog.w(TAG, "Failed setting oom adj of " + app + " to " + app.curAdj);
             }
         }
@@ -13518,6 +13559,7 @@
                 EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
                         app.processName, app.setAdj, app.waitingToKill);
                 Process.killProcessQuiet(app.pid);
+                success = false;
             } else {
                 if (true) {
                     long oldId = Binder.clearCallingIdentity();
@@ -13540,6 +13582,7 @@
                 }
             }
         }
+        return success;
     }
 
     private final ActivityRecord resumedAppLocked() {
@@ -13553,7 +13596,7 @@
         return resumedActivity;
     }
 
-    private final void updateOomAdjLocked(ProcessRecord app) {
+    private final boolean updateOomAdjLocked(ProcessRecord app) {
         final ActivityRecord TOP_ACT = resumedAppLocked();
         final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
         int curAdj = app.curAdj;
@@ -13562,7 +13605,7 @@
 
         mAdjSeq++;
 
-        updateOomAdjLocked(app, app.hiddenAdj, TOP_APP);
+        boolean success = updateOomAdjLocked(app, app.hiddenAdj, TOP_APP);
         final boolean nowHidden = app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ
             && app.curAdj <= ProcessList.HIDDEN_APP_MAX_ADJ;
         if (nowHidden != wasHidden) {
@@ -13570,6 +13613,7 @@
             // list may also be changed.
             updateOomAdjLocked();
         }
+        return success;
     }
 
     final void updateOomAdjLocked() {
diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java
index dfcc0bfa..131255f 100644
--- a/services/java/com/android/server/am/ProcessList.java
+++ b/services/java/com/android/server/am/ProcessList.java
@@ -163,7 +163,7 @@
         int minSize = 320*480;  //  153600
         int maxSize = 1280*800; // 1024000  230400 870400  .264
         float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
-        Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth + " dh=" + displayHeight);
+        //Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth + " dh=" + displayHeight);
 
         StringBuilder adjString = new StringBuilder();
         StringBuilder memString = new StringBuilder();
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 84880f9..84e5eae 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -60,6 +60,7 @@
 import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
 
 import android.app.IActivityManager;
 import android.app.INotificationManager;
@@ -454,7 +455,7 @@
             mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
             synchronized (mRulesLock) {
-                if (mMeteredIfaces.contains(iface)) {
+                if (mMeteredIfaces.contains(iface) && !LIMIT_GLOBAL_ALERT.equals(limitName)) {
                     try {
                         // force stats update to make sure we have numbers that
                         // caused alert to trigger.
@@ -763,7 +764,12 @@
             // disable data connection when over limit and not snoozed
             final boolean overLimit = policy.limitBytes != LIMIT_DISABLED
                     && totalBytes > policy.limitBytes && policy.lastSnooze < start;
-            setNetworkTemplateEnabled(policy.template, !overLimit);
+            final boolean enabled = !overLimit;
+
+            if (LOGD) {
+                Slog.d(TAG, "setting template=" + policy.template + " enabled=" + enabled);
+            }
+            setNetworkTemplateEnabled(policy.template, enabled);
         }
     }
 
@@ -772,7 +778,6 @@
      * for the given {@link NetworkTemplate}.
      */
     private void setNetworkTemplateEnabled(NetworkTemplate template, boolean enabled) {
-        if (LOGD) Slog.d(TAG, "setting template=" + template + " enabled=" + enabled);
         switch (template.getMatchRule()) {
             case MATCH_MOBILE_3G_LOWER:
             case MATCH_MOBILE_4G:
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index c911687..80ae9bc 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -43,6 +43,7 @@
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
 import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
 import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
 
@@ -56,6 +57,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.net.IConnectivityManager;
+import android.net.INetworkManagementEventObserver;
 import android.net.INetworkStatsService;
 import android.net.NetworkIdentity;
 import android.net.NetworkInfo;
@@ -121,7 +123,8 @@
     private static final int VERSION_UID_WITH_TAG = 3;
     private static final int VERSION_UID_WITH_SET = 4;
 
-    private static final int MSG_FORCE_UPDATE = 0x1;
+    private static final int MSG_PERFORM_POLL = 0x1;
+    private static final int MSG_PERFORM_POLL_DETAILED = 0x2;
 
     private final Context mContext;
     private final INetworkManagementService mNetworkManager;
@@ -141,7 +144,6 @@
 
     private PendingIntent mPollIntent;
 
-    // TODO: listen for kernel push events through netd instead of polling
     // TODO: trim empty history objects entirely
 
     private static final long KB_IN_BYTES = 1024;
@@ -174,17 +176,18 @@
     /** Flag if {@link #mUidStats} have been loaded from disk. */
     private boolean mUidStatsLoaded = false;
 
-    private NetworkStats mLastNetworkSnapshot;
-    private NetworkStats mLastPersistNetworkSnapshot;
+    private NetworkStats mLastPollNetworkSnapshot;
+    private NetworkStats mLastPollUidSnapshot;
+    private NetworkStats mLastPollOperationsSnapshot;
 
-    private NetworkStats mLastUidSnapshot;
+    private NetworkStats mLastPersistNetworkSnapshot;
+    private NetworkStats mLastPersistUidSnapshot;
 
     /** Current counter sets for each UID. */
     private SparseIntArray mActiveUidCounterSet = new SparseIntArray();
 
     /** Data layer operation counters for splicing into other structures. */
     private NetworkStats mOperations = new NetworkStats(0L, 10);
-    private NetworkStats mLastOperationsSnapshot;
 
     private final HandlerThread mHandlerThread;
     private final Handler mHandler;
@@ -252,13 +255,18 @@
         mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
 
         try {
-            registerPollAlarmLocked();
+            mNetworkManager.registerObserver(mAlertObserver);
         } catch (RemoteException e) {
-            Slog.w(TAG, "unable to register poll alarm");
+            // ouch, no push updates means we fall back to
+            // ACTION_NETWORK_STATS_POLL intervals.
+            Slog.e(TAG, "unable to register INetworkManagementEventObserver", e);
         }
 
-        // kick off background poll to bootstrap deltas
-        mHandler.obtainMessage(MSG_FORCE_UPDATE).sendToTarget();
+        registerPollAlarmLocked();
+        registerGlobalAlert();
+
+        // bootstrap initial stats to prevent double-counting later
+        bootstrapStats();
     }
 
     private void shutdownLocked() {
@@ -280,17 +288,37 @@
      * Clear any existing {@link #ACTION_NETWORK_STATS_POLL} alarms, and
      * reschedule based on current {@link NetworkStatsSettings#getPollInterval()}.
      */
-    private void registerPollAlarmLocked() throws RemoteException {
-        if (mPollIntent != null) {
-            mAlarmManager.remove(mPollIntent);
+    private void registerPollAlarmLocked() {
+        try {
+            if (mPollIntent != null) {
+                mAlarmManager.remove(mPollIntent);
+            }
+
+            mPollIntent = PendingIntent.getBroadcast(
+                    mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0);
+
+            final long currentRealtime = SystemClock.elapsedRealtime();
+            mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime,
+                    mSettings.getPollInterval(), mPollIntent);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "problem registering for poll alarm: " + e);
         }
+    }
 
-        mPollIntent = PendingIntent.getBroadcast(
-                mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0);
-
-        final long currentRealtime = SystemClock.elapsedRealtime();
-        mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime,
-                mSettings.getPollInterval(), mPollIntent);
+    /**
+     * Register for a global alert that is delivered through
+     * {@link INetworkManagementEventObserver} once a threshold amount of data
+     * has been transferred.
+     */
+    private void registerGlobalAlert() {
+        try {
+            final long alertBytes = mSettings.getPersistThreshold();
+            mNetworkManager.setGlobalAlert(alertBytes);
+        } catch (IllegalStateException e) {
+            Slog.w(TAG, "problem registering for global alert: " + e);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "problem registering for global alert: " + e);
+        }
     }
 
     @Override
@@ -475,10 +503,7 @@
     @Override
     public void forceUpdate() {
         mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
-
-        synchronized (mStatsLock) {
-            performPollLocked(true, false);
-        }
+        performPoll(true, false);
     }
 
     /**
@@ -507,14 +532,10 @@
         public void onReceive(Context context, Intent intent) {
             // on background handler thread, and verified UPDATE_DEVICE_STATS
             // permission above.
-            synchronized (mStatsLock) {
-                mWakeLock.acquire();
-                try {
-                    performPollLocked(true, false);
-                } finally {
-                    mWakeLock.release();
-                }
-            }
+            performPoll(true, false);
+
+            // verify that we're watching global alert
+            registerGlobalAlert();
         }
     };
 
@@ -547,6 +568,26 @@
     };
 
     /**
+     * Observer that watches for {@link INetworkManagementService} alerts.
+     */
+    private INetworkManagementEventObserver mAlertObserver = new NetworkAlertObserver() {
+        @Override
+        public void limitReached(String limitName, String iface) {
+            // only someone like NMS should be calling us
+            mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+            if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
+                // kick off background poll to collect network stats; UID stats
+                // are handled during normal polling interval.
+                mHandler.obtainMessage(MSG_PERFORM_POLL).sendToTarget();
+
+                // re-arm global alert for next update
+                registerGlobalAlert();
+            }
+        }
+    };
+
+    /**
      * Inspect all current {@link NetworkState} to derive mapping from {@code
      * iface} to {@link NetworkStatsHistory}. When multiple {@link NetworkInfo}
      * are active on a single {@code iface}, they are combined under a single
@@ -588,6 +629,33 @@
     }
 
     /**
+     * Bootstrap initial stats snapshot, usually during {@link #systemReady()}
+     * so we have baseline values without double-counting.
+     */
+    private void bootstrapStats() {
+        try {
+            mLastPollNetworkSnapshot = mNetworkManager.getNetworkStatsSummary();
+            mLastPollUidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
+            mLastPollOperationsSnapshot = new NetworkStats(0L, 0);
+        } catch (IllegalStateException e) {
+            Slog.w(TAG, "problem reading network stats: " + e);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "problem reading network stats: " + e);
+        }
+    }
+
+    private void performPoll(boolean detailedPoll, boolean forcePersist) {
+        synchronized (mStatsLock) {
+            mWakeLock.acquire();
+            try {
+                performPollLocked(detailedPoll, forcePersist);
+            } finally {
+                mWakeLock.release();
+            }
+        }
+    }
+
+    /**
      * Periodic poll operation, reading current statistics and recording into
      * {@link NetworkStatsHistory}.
      *
@@ -596,6 +664,7 @@
      */
     private void performPollLocked(boolean detailedPoll, boolean forcePersist) {
         if (LOGV) Slog.v(TAG, "performPollLocked()");
+        final long startRealtime = SystemClock.elapsedRealtime();
 
         // try refreshing time source when stale
         if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) {
@@ -605,6 +674,7 @@
         // TODO: consider marking "untrusted" times in historical stats
         final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
                 : System.currentTimeMillis();
+        final long persistThreshold = mSettings.getPersistThreshold();
 
         final NetworkStats networkSnapshot;
         final NetworkStats uidSnapshot;
@@ -620,30 +690,32 @@
         }
 
         performNetworkPollLocked(networkSnapshot, currentTime);
-        if (detailedPoll) {
-            performUidPollLocked(uidSnapshot, currentTime);
+
+        // persist when enough network data has occurred
+        final NetworkStats persistNetworkDelta = computeStatsDelta(
+                mLastPersistNetworkSnapshot, networkSnapshot, true);
+        if (forcePersist || persistNetworkDelta.getTotalBytes() > persistThreshold) {
+            writeNetworkStatsLocked();
+            mLastPersistNetworkSnapshot = networkSnapshot;
         }
 
-        // decide if enough has changed to trigger persist
-        final NetworkStats persistDelta = computeStatsDelta(
-                mLastPersistNetworkSnapshot, networkSnapshot, true);
-        final long persistThreshold = mSettings.getPersistThreshold();
+        if (detailedPoll) {
+            performUidPollLocked(uidSnapshot, currentTime);
 
-        NetworkStats.Entry entry = null;
-        for (String iface : persistDelta.getUniqueIfaces()) {
-            final int index = persistDelta.findIndex(iface, UID_ALL, SET_DEFAULT, TAG_NONE);
-            entry = persistDelta.getValues(index, entry);
-            if (forcePersist || entry.rxBytes > persistThreshold
-                    || entry.txBytes > persistThreshold) {
-                writeNetworkStatsLocked();
-                if (mUidStatsLoaded) {
-                    writeUidStatsLocked();
-                }
+            // persist when enough network data has occurred
+            final NetworkStats persistUidDelta = computeStatsDelta(
+                    mLastPersistUidSnapshot, uidSnapshot, true);
+            if (forcePersist || persistUidDelta.getTotalBytes() > persistThreshold) {
+                writeUidStatsLocked();
                 mLastPersistNetworkSnapshot = networkSnapshot;
-                break;
             }
         }
 
+        if (LOGV) {
+            final long duration = SystemClock.elapsedRealtime() - startRealtime;
+            Slog.v(TAG, "performPollLocked() took " + duration + "ms");
+        }
+
         // finally, dispatch updated event to any listeners
         final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED);
         updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -656,7 +728,7 @@
     private void performNetworkPollLocked(NetworkStats networkSnapshot, long currentTime) {
         final HashSet<String> unknownIface = Sets.newHashSet();
 
-        final NetworkStats delta = computeStatsDelta(mLastNetworkSnapshot, networkSnapshot, false);
+        final NetworkStats delta = computeStatsDelta(mLastPollNetworkSnapshot, networkSnapshot, false);
         final long timeStart = currentTime - delta.getElapsedRealtime();
 
         NetworkStats.Entry entry = null;
@@ -678,7 +750,7 @@
             history.removeBucketsBefore(currentTime - maxHistory);
         }
 
-        mLastNetworkSnapshot = networkSnapshot;
+        mLastPollNetworkSnapshot = networkSnapshot;
 
         if (LOGD && unknownIface.size() > 0) {
             Slog.w(TAG, "unknown interfaces " + unknownIface.toString() + ", ignoring those stats");
@@ -691,9 +763,9 @@
     private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) {
         ensureUidStatsLoadedLocked();
 
-        final NetworkStats delta = computeStatsDelta(mLastUidSnapshot, uidSnapshot, false);
+        final NetworkStats delta = computeStatsDelta(mLastPollUidSnapshot, uidSnapshot, false);
         final NetworkStats operationsDelta = computeStatsDelta(
-                mLastOperationsSnapshot, mOperations, false);
+                mLastPollOperationsSnapshot, mOperations, false);
         final long timeStart = currentTime - delta.getElapsedRealtime();
 
         NetworkStats.Entry entry = null;
@@ -731,8 +803,8 @@
             }
         }
 
-        mLastUidSnapshot = uidSnapshot;
-        mLastOperationsSnapshot = mOperations;
+        mLastPollUidSnapshot = uidSnapshot;
+        mLastPollOperationsSnapshot = mOperations;
         mOperations = new NetworkStats(0L, 10);
     }
 
@@ -1162,8 +1234,12 @@
         /** {@inheritDoc} */
         public boolean handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_FORCE_UPDATE: {
-                    forceUpdate();
+                case MSG_PERFORM_POLL: {
+                    performPoll(false, false);
+                    return true;
+                }
+                case MSG_PERFORM_POLL_DETAILED: {
+                    performPoll(true, false);
                     return true;
                 }
                 default: {
@@ -1226,10 +1302,10 @@
         }
 
         public long getPollInterval() {
-            return getSecureLong(NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS);
+            return getSecureLong(NETSTATS_POLL_INTERVAL, 30 * MINUTE_IN_MILLIS);
         }
         public long getPersistThreshold() {
-            return getSecureLong(NETSTATS_PERSIST_THRESHOLD, 16 * KB_IN_BYTES);
+            return getSecureLong(NETSTATS_PERSIST_THRESHOLD, 512 * KB_IN_BYTES);
         }
         public long getNetworkBucketDuration() {
             return getSecureLong(NETSTATS_NETWORK_BUCKET_DURATION, HOUR_IN_MILLIS);
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 6138490..6dd8cd6 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -38,6 +38,7 @@
 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
 import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
 import static org.easymock.EasyMock.anyLong;
+import static org.easymock.EasyMock.capture;
 import static org.easymock.EasyMock.createMock;
 import static org.easymock.EasyMock.eq;
 import static org.easymock.EasyMock.expect;
@@ -49,6 +50,7 @@
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.net.IConnectivityManager;
+import android.net.INetworkManagementEventObserver;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
@@ -65,10 +67,10 @@
 import com.android.server.net.NetworkStatsService;
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
 
+import org.easymock.Capture;
 import org.easymock.EasyMock;
 
 import java.io.File;
-import java.util.concurrent.Future;
 
 import libcore.io.IoUtils;
 
@@ -105,6 +107,7 @@
     private IConnectivityManager mConnManager;
 
     private NetworkStatsService mService;
+    private INetworkManagementEventObserver mNetworkObserver;
 
     @Override
     public void setUp() throws Exception {
@@ -132,13 +135,20 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
-        final Future<?> firstPoll = expectSystemReady();
+        expectSystemReady();
+
+        // catch INetworkManagementEventObserver during systemReady()
+        final Capture<INetworkManagementEventObserver> networkObserver = new Capture<
+                INetworkManagementEventObserver>();
+        mNetManager.registerObserver(capture(networkObserver));
+        expectLastCall().atLeastOnce();
 
         replay();
         mService.systemReady();
-        firstPoll.get();
         verifyAndReset();
 
+        mNetworkObserver = networkObserver.getValue();
+
     }
 
     @Override
@@ -183,6 +193,7 @@
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 1024L, 1L, 2048L, 2L));
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
 
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
@@ -199,6 +210,7 @@
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 4096L, 4L, 8192L, 8L));
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
 
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
@@ -238,6 +250,7 @@
                 .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 256L, 2L, 128L, 1L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 128L, 1L, 0L));
+        expectNetworkStatsPoll();
 
         mService.setUidForeground(UID_RED, false);
         mService.incrementOperationCount(UID_RED, 0xFAAD, 4);
@@ -273,11 +286,18 @@
         expectDefaultSettings();
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
-        final Future<?> firstPoll = expectSystemReady();
+        expectSystemReady();
+
+        // catch INetworkManagementEventObserver during systemReady()
+        final Capture<INetworkManagementEventObserver> networkObserver = new Capture<
+                INetworkManagementEventObserver>();
+        mNetManager.registerObserver(capture(networkObserver));
+        expectLastCall().atLeastOnce();
 
         replay();
         mService.systemReady();
-        firstPoll.get();
+
+        mNetworkObserver = networkObserver.getValue();
 
         // after systemReady(), we should have historical stats loaded again
         assertNetworkTotal(sTemplateWifi, 1024L, 8L, 2048L, 16L, 0);
@@ -312,6 +332,7 @@
         expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 512L, 4L, 512L, 4L));
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
 
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
@@ -329,6 +350,7 @@
         expectSettings(0L, 30 * MINUTE_IN_MILLIS, WEEK_IN_MILLIS);
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
 
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
@@ -363,6 +385,7 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
+        expectNetworkStatsPoll();
 
         mService.incrementOperationCount(UID_RED, 0xF00D, 10);
 
@@ -384,6 +407,7 @@
         expectNetworkState(buildMobile3gState(IMSI_2));
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
 
         replay();
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
@@ -399,6 +423,7 @@
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 1024L, 8L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
+        expectNetworkStatsPoll();
 
         mService.incrementOperationCount(UID_BLUE, 0xFAAD, 10);
 
@@ -441,6 +466,7 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
                 .addValues(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+        expectNetworkStatsPoll();
 
         mService.incrementOperationCount(UID_RED, 0xFAAD, 10);
 
@@ -494,6 +520,7 @@
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
+        expectNetworkStatsPoll();
 
         mService.incrementOperationCount(UID_RED, 0xF00D, 5);
 
@@ -511,6 +538,7 @@
         expectNetworkState(buildMobile4gState());
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
+        expectNetworkStatsPoll();
 
         replay();
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
@@ -525,6 +553,7 @@
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
+        expectNetworkStatsPoll();
 
         mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
 
@@ -558,6 +587,7 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1024L, 8L, 512L, 4L, 0L));
+        expectNetworkStatsPoll();
 
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
@@ -576,6 +606,7 @@
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0L));
+        expectNetworkStatsPoll();
 
         replay();
         mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
@@ -617,6 +648,7 @@
         expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 128L, 2L, 128L, 2L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L));
+        expectNetworkStatsPoll();
 
         mService.incrementOperationCount(UID_RED, 0xF00D, 1);
 
@@ -637,6 +669,7 @@
                 .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 64L, 1L, 64L, 1L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, TAG_NONE, 32L, 2L, 32L, 2L, 0L)
                 .addValues(TEST_IFACE, UID_RED, SET_FOREGROUND, 0xFAAD, 1L, 1L, 1L, 1L, 0L));
+        expectNetworkStatsPoll();
 
         mService.setUidForeground(UID_RED, true);
         mService.incrementOperationCount(UID_RED, 0xFAAD, 1);
@@ -679,7 +712,7 @@
                 txPackets, operations);
     }
 
-    private Future<?> expectSystemReady() throws Exception {
+    private void expectSystemReady() throws Exception {
         mAlarmManager.remove(isA(PendingIntent.class));
         expectLastCall().anyTimes();
 
@@ -687,8 +720,8 @@
                 eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), isA(PendingIntent.class));
         expectLastCall().atLeastOnce();
 
-        return mServiceContext.nextBroadcastIntent(
-                NetworkStatsService.ACTION_NETWORK_STATS_UPDATED);
+        mNetManager.setGlobalAlert(anyLong());
+        expectLastCall().atLeastOnce();
     }
 
     private void expectNetworkState(NetworkState... state) throws Exception {
@@ -727,6 +760,11 @@
         expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes();
     }
 
+    private void expectNetworkStatsPoll() throws Exception {
+        mNetManager.setGlobalAlert(anyLong());
+        expectLastCall().anyTimes();
+    }
+
     private void assertStatsFilesExist(boolean exist) {
         final File networkFile = new File(mStatsDir, "netstats.bin");
         final File uidFile = new File(mStatsDir, "netstats_uid.bin");
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 6c6f149..c1f6785 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -243,7 +243,7 @@
 
     /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
         [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
-    public static String p2pConnect(WifiP2pConfig config) {
+    public static String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
         if (config == null) return null;
         List<String> args = new ArrayList<String>();
         WpsConfiguration wpsConfig = config.wpsConfig;
@@ -269,15 +269,15 @@
                 break;
         }
 
-        if (config.isPersistent) args.add("persistent");
-        if (config.joinExistingGroup) args.add("join");
+        /* Persist unless there is an explicit request to not do so*/
+        if (config.persist != WifiP2pConfig.Persist.NO) args.add("persistent");
+        if (joinExistingGroup) args.add("join");
 
         int groupOwnerIntent = config.groupOwnerIntent;
         if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
             groupOwnerIntent = 3; //default value
         }
         args.add("go_intent=" + groupOwnerIntent);
-        if (config.channel > 0) args.add("freq=" + config.channel);
 
         String command = "P2P_CONNECT ";
         for (String s : args) command += s + " ";
@@ -300,11 +300,24 @@
 
     /* Invite a peer to a group */
     public static boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
-        if (group == null || deviceAddress == null) return false;
-        return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
-                + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
+        if (deviceAddress == null) return false;
+
+        if (group == null) {
+            return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
+        } else {
+            return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
+                    + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
+        }
     }
 
+    /* Reinvoke a persistent connection */
+    public static boolean p2pReinvoke(int netId, String deviceAddress) {
+        if (deviceAddress == null || netId < 0) return false;
+
+        return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
+    }
+
+
     public static String p2pGetInterfaceAddress(String deviceAddress) {
         if (deviceAddress == null) return null;
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index b77fd76..2d57363 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -51,14 +51,16 @@
      */
     public int groupOwnerIntent = -1;
 
-    public boolean isPersistent;
-
-    public boolean joinExistingGroup;
-
     /**
-     * Channel frequency in MHz
+     * Indicates whether the configuration is saved
      */
-    public int channel;
+    public enum Persist {
+        SYSTEM_DEFAULT,
+        YES,
+        NO
+    }
+
+    public Persist persist = Persist.SYSTEM_DEFAULT;
 
     public WifiP2pConfig() {
         //set defaults
@@ -112,9 +114,7 @@
         sbuf.append("\n address: ").append(deviceAddress);
         sbuf.append("\n wps: ").append(wpsConfig);
         sbuf.append("\n groupOwnerIntent: ").append(groupOwnerIntent);
-        sbuf.append("\n isPersistent: ").append(isPersistent);
-        sbuf.append("\n joinExistingGroup: ").append(joinExistingGroup);
-        sbuf.append("\n channel: ").append(channel);
+        sbuf.append("\n persist: ").append(persist.toString());
         return sbuf.toString();
     }
 
@@ -136,9 +136,7 @@
         dest.writeString(deviceAddress);
         dest.writeParcelable(wpsConfig, flags);
         dest.writeInt(groupOwnerIntent);
-        dest.writeInt(isPersistent ? 1 : 0);
-        dest.writeInt(joinExistingGroup ? 1 : 0);
-        dest.writeInt(channel);
+        dest.writeString(persist.name());
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -150,9 +148,7 @@
                 config.deviceAddress = in.readString();
                 config.wpsConfig = (WpsConfiguration) in.readParcelable(null);
                 config.groupOwnerIntent = in.readInt();
-                config.isPersistent = (in.readInt() == 1);
-                config.joinExistingGroup = (in.readInt() == 1);
-                config.channel = in.readInt();
+                config.persist = Persist.valueOf(in.readString());
                 return config;
             }
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
index 4ec23b8..50f624a 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
@@ -51,8 +51,10 @@
         }
     }
 
-    public void clear() {
+    public boolean clear() {
+        if (mDevices.isEmpty()) return false;
         mDevices.clear();
+        return true;
     }
 
     public void add(WifiP2pDevice device) {
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pStatus.aidl b/wifi/java/android/net/wifi/p2p/WifiP2pInfo.aidl
similarity index 95%
rename from wifi/java/android/net/wifi/p2p/WifiP2pStatus.aidl
rename to wifi/java/android/net/wifi/p2p/WifiP2pInfo.aidl
index 7bab5d3..a347148 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pStatus.aidl
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pInfo.aidl
@@ -16,4 +16,4 @@
 
 package android.net.wifi.p2p;
 
-parcelable WifiP2pStatus;
+parcelable WifiP2pInfo;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java
new file mode 100644
index 0000000..9dc2fbf
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.p2p;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * A class representing connection info on Wi-fi P2p
+ * @hide
+ */
+public class WifiP2pInfo implements Parcelable {
+
+    public boolean groupFormed;
+
+    public boolean isGroupOwner;
+
+    public InetAddress groupOwnerAddress;
+
+    public WifiP2pInfo() {
+    }
+
+    public String toString() {
+        StringBuffer sbuf = new StringBuffer();
+        sbuf.append("groupFormed: ").append(groupFormed)
+            .append("isGroupOwner: ").append(isGroupOwner)
+            .append("groupOwnerAddress: ").append(groupOwnerAddress);
+        return sbuf.toString();
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** copy constructor {@hide} */
+    public WifiP2pInfo(WifiP2pInfo source) {
+        if (source != null) {
+            groupFormed = source.groupFormed;
+            isGroupOwner = source.isGroupOwner;
+            groupOwnerAddress = source.groupOwnerAddress;
+       }
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByte(groupFormed ? (byte)1 : (byte)0);
+        dest.writeByte(isGroupOwner ? (byte)1 : (byte)0);
+
+        if (groupOwnerAddress != null) {
+            dest.writeByte((byte)1);
+            dest.writeByteArray(groupOwnerAddress.getAddress());
+        } else {
+            dest.writeByte((byte)0);
+        }
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public static final Creator<WifiP2pInfo> CREATOR =
+        new Creator<WifiP2pInfo>() {
+            public WifiP2pInfo createFromParcel(Parcel in) {
+                WifiP2pInfo info = new WifiP2pInfo();
+                info.groupFormed = (in.readByte() == 1);
+                info.isGroupOwner = (in.readByte() == 1);
+                if (in.readByte() == 1) {
+                    try {
+                        info.groupOwnerAddress = InetAddress.getByAddress(in.createByteArray());
+                    } catch (UnknownHostException e) {}
+                }
+                return info;
+            }
+
+            public WifiP2pInfo[] newArray(int size) {
+                return new WifiP2pInfo[size];
+            }
+        };
+}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index cc1f062..25daf1c 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -95,6 +95,12 @@
         "android.net.wifi.CONNECTION_STATE_CHANGE";
 
     /**
+     * The lookup key for a {@link android.net.wifi.p2p.WifiP2pInfo} object
+     * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
+     */
+    public static final String EXTRA_WIFI_P2P_INFO = "wifiP2pInfo";
+
+    /**
      * The lookup key for a {@link android.net.NetworkInfo} object associated with the
      * Wi-Fi network. Retrieve with
      * {@link android.content.Intent#getParcelableExtra(String)}.
@@ -145,57 +151,38 @@
     public static final int ENABLE_P2P_FAILED                       = BASE + 2;
     public static final int ENABLE_P2P_SUCCEEDED                    = BASE + 3;
 
-    /* arg1 on ENABLE_P2P_FAILED indicates a reason for failure */
+    public static final int DISABLE_P2P                             = BASE + 4;
+    public static final int DISABLE_P2P_FAILED                      = BASE + 5;
+    public static final int DISABLE_P2P_SUCCEEDED                   = BASE + 6;
+
+    public static final int DISCOVER_PEERS                          = BASE + 7;
+    public static final int DISCOVER_PEERS_FAILED                   = BASE + 8;
+    public static final int DISCOVER_PEERS_SUCCEEDED                = BASE + 9;
+
+    public static final int CONNECT                                 = BASE + 10;
+    public static final int CONNECT_FAILED                          = BASE + 11;
+    public static final int CONNECT_SUCCEEDED                       = BASE + 12;
+
+    public static final int CREATE_GROUP                            = BASE + 13;
+    public static final int CREATE_GROUP_FAILED                     = BASE + 14;
+    public static final int CREATE_GROUP_SUCCEEDED                  = BASE + 15;
+
+    public static final int REMOVE_GROUP                            = BASE + 16;
+    public static final int REMOVE_GROUP_FAILED                     = BASE + 17;
+    public static final int REMOVE_GROUP_SUCCEEDED                  = BASE + 18;
+
+    public static final int REQUEST_PEERS                           = BASE + 19;
+    public static final int RESPONSE_PEERS                          = BASE + 20;
+
+    public static final int REQUEST_CONNECTION_INFO                 = BASE + 21;
+    public static final int RESPONSE_CONNECTION_INFO                = BASE + 22;
+
+    /* arg1 values on response messages from the framework */
     public static final int P2P_UNSUPPORTED     = 1;
 
-    public static final int DISABLE_P2P                             = BASE + 5;
-    public static final int DISABLE_P2P_FAILED                      = BASE + 6;
-    public static final int DISABLE_P2P_SUCCEEDED                   = BASE + 7;
-
-    public static final int START_LISTEN_MODE                       = BASE + 9;
-    public static final int START_LISTEN_FAILED                     = BASE + 10;
-    public static final int START_LISTEN_SUCCEEDED                  = BASE + 11;
-
-    public static final int DISCOVER_PEERS                          = BASE + 13;
-    public static final int DISCOVER_PEERS_FAILED                   = BASE + 14;
-    public static final int DISCOVER_PEERS_SUCCEDED                 = BASE + 15;
-
-    public static final int CANCEL_DISCOVER_PEERS                   = BASE + 17;
-    public static final int CANCEL_DISCOVER_PEERS_FAILED            = BASE + 18;
-    public static final int CANCEL_DISCOVER_PEERS_SUCCEDED          = BASE + 19;
-
-    public static final int CONNECT                                 = BASE + 21;
-    public static final int CONNECT_FAILED                          = BASE + 22;
-    public static final int CONNECT_SUCCEEDED                       = BASE + 23;
-
-    public static final int CANCEL_CONNECT                          = BASE + 25;
-    public static final int CANCEL_CONNECT_FAILED                   = BASE + 26;
-    public static final int CANCEL_CONNECT_SUCCEDED                 = BASE + 27;
-
-    public static final int REJECT                                  = BASE + 28;
-    public static final int REJECT_FAILED                           = BASE + 29;
-    public static final int REJECT_SUCCEEDED                        = BASE + 30;
-
-    public static final int CREATE_GROUP                            = BASE + 31;
-    public static final int CREATE_GROUP_FAILED                     = BASE + 32;
-    public static final int CREATE_GROUP_SUCCEEDED                  = BASE + 33;
-
-    public static final int REMOVE_GROUP                            = BASE + 34;
-    public static final int REMOVE_GROUP_FAILED                     = BASE + 35;
-    public static final int REMOVE_GROUP_SUCCEEDED                  = BASE + 36;
-
-    public static final int REQUEST_SETTINGS                        = BASE + 37;
-    public static final int RESPONSE_SETTINGS                       = BASE + 38;
-
-    public static final int REQUEST_PEERS                           = BASE + 39;
-    public static final int RESPONSE_PEERS                          = BASE + 40;
-
-    public static final int REQUEST_CONNECTION_STATUS               = BASE + 41;
-    public static final int RESPONSE_CONNECTION_STATUS              = BASE + 42;
-
-    public static final int WPS_PBC                                 = BASE + 43;
-    public static final int WPS_PIN                                 = BASE + 44;
-    public static final int WPS_PIN_AVAILABLE                       = BASE + 45;
+    public static final int WPS_PBC                                 = BASE + 23;
+    public static final int WPS_PIN                                 = BASE + 24;
+    public static final int WPS_PIN_AVAILABLE                       = BASE + 25;
 
     /**
      * Create a new WifiP2pManager instance. Applications use
@@ -269,17 +256,6 @@
     }
 
     /**
-     * Set device in listen mode. This will make the device discoverable by
-     * another peer.
-     * A dialog to the user is thrown to request his permission since it can
-     * have a significant impact on power consumption
-     */
-     public void setListenState(Channel c, int timeout) {
-        if (c == null) return;
-        c.mAsyncChannel.sendMessage(START_LISTEN_MODE, timeout);
-     }
-
-    /**
      * Initiates peer discovery
      */
     public void discoverPeers(Channel c) {
@@ -288,22 +264,6 @@
     }
 
     /**
-     * Initiates peer discovery with a 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(Channel c) {
-        if (c == null) return;
-        c.mAsyncChannel.sendMessage(CANCEL_DISCOVER_PEERS);
-    }
-
-    /**
      * Start a p2p connection
      *
      * @param peer Configuration described in a {@link WifiP2pConfig} object.
@@ -314,14 +274,6 @@
     }
 
     /**
-     * Cancel any ongoing negotiation or disconnect from an existing group
-     */
-    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.
      */
@@ -340,15 +292,6 @@
     }
 
     /**
-     * Request current p2p settings. This returns a RESPONSE_SETTINGS on the source
-     * handler.
-     */
-    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.
      */
@@ -365,12 +308,19 @@
     }
 
     /**
-     * Request device connection status. This returns a RESPONSE_CONNECTION_STATUS on
+     * Request device connection info. This returns a RESPONSE_CONNECTION_INFO on
      * the source handler.
      */
-    public void requestConnectionStatus(Channel c) {
+    public void requestConnectionInfo(Channel c) {
         if (c == null) return;
-        c.mAsyncChannel.sendMessage(REQUEST_CONNECTION_STATUS);
+        c.mAsyncChannel.sendMessage(REQUEST_CONNECTION_INFO);
+    }
+
+    /**
+     * Fetch p2p connection status from a RESPONSE_CONNECTION_INFO message
+     */
+    public WifiP2pInfo connectionInfoInResponse(Message msg) {
+        return (WifiP2pInfo) msg.obj;
     }
 
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 4447971..9e0f124 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -93,12 +93,25 @@
     private int mWifiApState = WifiManager.WIFI_AP_STATE_DISABLED;
 
     private P2pStateMachine mP2pStateMachine;
-    private AsyncChannel mReplyChannel = new AsyncChannel();;
+    private AsyncChannel mReplyChannel = new AsyncChannel();
     private AsyncChannel mWifiChannel;
 
-    private static final int GROUP_NEGOTIATION_WAIT_TIME_MS = 60 * 1000;
+    /* Two minutes comes from the wpa_supplicant setting */
+    private static final int GROUP_NEGOTIATION_WAIT_TIME_MS = 120 * 1000;
     private static int mGroupNegotiationTimeoutIndex = 0;
 
+    /**
+     * Delay between restarts upon failure to setup connection with supplicant
+     */
+    private static final int P2P_RESTART_INTERVAL_MSECS = 5000;
+
+    /**
+     * Number of times we attempt to restart p2p
+     */
+    private static final int P2P_RESTART_TRIES = 5;
+
+    private int mP2pRestartCount = 0;
+
     private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE;
 
     /* Message sent to WifiStateMachine to indicate p2p enable is pending */
@@ -115,14 +128,12 @@
     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);
@@ -205,6 +216,7 @@
         private P2pNotSupportedState mP2pNotSupportedState = new P2pNotSupportedState();
         private P2pDisablingState mP2pDisablingState = new P2pDisablingState();
         private P2pDisabledState mP2pDisabledState = new P2pDisabledState();
+        private WaitForUserActionState mWaitForUserActionState = new WaitForUserActionState();
         private WaitForWifiDisableState mWaitForWifiDisableState = new WaitForWifiDisableState();
         private P2pEnablingState mP2pEnablingState = new P2pEnablingState();
         private P2pEnabledState mP2pEnabledState = new P2pEnabledState();
@@ -216,11 +228,9 @@
         private WifiMonitor mWifiMonitor = new WifiMonitor(this);
 
         private WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
+        private WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo();
         private WifiP2pGroup mGroup;
 
-        // Saved enable request message so the state machine can send an appropriate response
-        private Message mSavedEnableRequestMessage;
-
         // Saved WifiP2pConfig from GO negotiation request
         private WifiP2pConfig mSavedGoNegotiationConfig;
 
@@ -237,7 +247,8 @@
                 addState(mP2pNotSupportedState, mDefaultState);
                 addState(mP2pDisablingState, mDefaultState);
                 addState(mP2pDisabledState, mDefaultState);
-                addState(mWaitForWifiDisableState, mDefaultState);
+                    addState(mWaitForUserActionState, mP2pDisabledState);
+                    addState(mWaitForWifiDisableState, mP2pDisabledState);
                 addState(mP2pEnablingState, mDefaultState);
                 addState(mP2pEnabledState, mDefaultState);
                     addState(mInactiveState, mP2pEnabledState);
@@ -251,27 +262,26 @@
             }
         }
 
-    // TODO: Respond to every p2p request with success/failure
     class DefaultState extends State {
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) Slog.d(TAG, getName() + message.toString());
+            if (DBG) logd(getName() + message.toString());
             switch (message.what) {
                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                     if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                        if (DBG) Slog.d(TAG, "Full connection with WifiStateMachine established");
+                        if (DBG) logd("Full connection with WifiStateMachine established");
                         mWifiChannel = (AsyncChannel) message.obj;
                     } else {
-                        Slog.e(TAG, "Full connection failure, error = " + message.arg1);
+                        loge("Full connection failure, error = " + message.arg1);
                         mWifiChannel = null;
                     }
                     break;
 
                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
                     if (message.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
-                        Slog.e(TAG, "Send failed, client connection lost");
+                        loge("Send failed, client connection lost");
                     } else {
-                        Slog.e(TAG, "Client connection lost with reason: " + message.arg1);
+                        loge("Client connection lost with reason: " + message.arg1);
                     }
                     mWifiChannel = null;
                     break;
@@ -286,47 +296,35 @@
                     deferMessage(message);
                     break;
                 case WifiP2pManager.ENABLE_P2P:
-                    mReplyChannel.replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED);
+                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED);
                     break;
                 case WifiP2pManager.DISABLE_P2P:
-                    mReplyChannel.replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED);
-                    break;
-                case WifiP2pManager.START_LISTEN_MODE:
-                    mReplyChannel.replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED);
+                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED);
                     break;
                 case WifiP2pManager.DISCOVER_PEERS:
-                    mReplyChannel.replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED);
-                    break;
-                case WifiP2pManager.CANCEL_DISCOVER_PEERS:
-                    mReplyChannel.replyToMessage(message,
-                            WifiP2pManager.CANCEL_DISCOVER_PEERS_FAILED);
+                    replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED);
                     break;
                 case WifiP2pManager.CONNECT:
-                    mReplyChannel.replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
-                    break;
-                case WifiP2pManager.CANCEL_CONNECT:
-                    mReplyChannel.replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED);
-                    break;
-                case WifiP2pManager.REJECT:
-                    mReplyChannel.replyToMessage(message, WifiP2pManager.REJECT_FAILED);
+                    replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
                     break;
                 case WifiP2pManager.CREATE_GROUP:
-                    mReplyChannel.replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED);
+                    replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED);
                     break;
                 case WifiP2pManager.REMOVE_GROUP:
-                    mReplyChannel.replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED);
+                    replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED);
                     break;
-                // TODO: fix
-                case WifiP2pManager.REQUEST_SETTINGS:
                 case WifiP2pManager.REQUEST_PEERS:
-                case WifiP2pManager.REQUEST_CONNECTION_STATUS:
+                    replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers);
+                    break;
+                case WifiP2pManager.REQUEST_CONNECTION_INFO:
+                    replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO, mWifiP2pInfo);
                     break;
                 // Ignore
                 case WIFI_DISABLE_USER_ACCEPT:
                 case GROUP_NEGOTIATION_TIMED_OUT:
                     break;
                 default:
-                    Slog.e(TAG, "Unhandled message " + message);
+                    loge("Unhandled message " + message);
                     return NOT_HANDLED;
             }
             return HANDLED;
@@ -339,17 +337,33 @@
             switch (message.what) {
                 // Allow Wi-Fi to proceed
                 case WifiStateMachine.WIFI_ENABLE_PENDING:
-                    mReplyChannel.replyToMessage(message, WIFI_ENABLE_PROCEED);
+                    replyToMessage(message, WIFI_ENABLE_PROCEED);
                     break;
                 case WifiP2pManager.ENABLE_P2P:
-                    mReplyChannel.replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
+                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
                     break;
                 case WifiP2pManager.DISABLE_P2P:
-                    mReplyChannel.replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
+                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
                             WifiP2pManager.P2P_UNSUPPORTED);
                     break;
-                default:
+                case WifiP2pManager.DISCOVER_PEERS:
+                    replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
+                            WifiP2pManager.P2P_UNSUPPORTED);
+                    break;
+                case WifiP2pManager.CONNECT:
+                    replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
+                            WifiP2pManager.P2P_UNSUPPORTED);
+                    break;
+                case WifiP2pManager.CREATE_GROUP:
+                    replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
+                            WifiP2pManager.P2P_UNSUPPORTED);
+                    break;
+                case WifiP2pManager.REMOVE_GROUP:
+                    replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
+                            WifiP2pManager.P2P_UNSUPPORTED);
+                    break;
+               default:
                     return NOT_HANDLED;
             }
             return HANDLED;
@@ -359,17 +373,27 @@
     class P2pDisablingState extends State {
         @Override
         public void enter() {
-            if (DBG) Slog.d(TAG, getName());
-            transitionTo(mP2pDisabledState);
+            if (DBG) logd(getName());
+            logd("stopping supplicant");
+            if (!WifiNative.stopSupplicant()) {
+                loge("Failed to stop supplicant, issue kill");
+                WifiNative.killSupplicant();
+            }
         }
 
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) Slog.d(TAG, getName() + message.toString());
+            if (DBG) logd(getName() + message.toString());
             switch (message.what) {
                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
+                    logd("Supplicant connection lost");
+                    WifiNative.closeSupplicantConnection();
                     transitionTo(mP2pDisabledState);
                     break;
+                case WifiP2pManager.ENABLE_P2P:
+                case WifiP2pManager.DISABLE_P2P:
+                    deferMessage(message);
+                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -381,23 +405,22 @@
     class P2pDisabledState extends State {
        @Override
         public void enter() {
-            if (DBG) Slog.d(TAG, getName());
+            if (DBG) logd(getName());
         }
 
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) Slog.d(TAG, getName() + message.toString());
+            if (DBG) logd(getName() + message.toString());
             switch (message.what) {
                 case WifiP2pManager.ENABLE_P2P:
-                    mSavedEnableRequestMessage = Message.obtain(message);
                     OnClickListener listener = new OnClickListener() {
                         @Override
                         public void onClick(DialogInterface dialog, int which) {
                             if (which == DialogInterface.BUTTON_POSITIVE) {
                                 sendMessage(WIFI_DISABLE_USER_ACCEPT);
                             } else {
-                                mReplyChannel.replyToMessage(mSavedEnableRequestMessage,
-                                        WifiP2pManager.ENABLE_P2P_FAILED);
+                                logd("User rejected enabling p2p");
+                                //ignore
                             }
                         }
                     };
@@ -414,17 +437,43 @@
                             .create();
                         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                         dialog.show();
+                        transitionTo(mWaitForUserActionState);
                     } else {
                         mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
                         transitionTo(mWaitForWifiDisableState);
                     }
+                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
                     break;
+                case WifiP2pManager.DISABLE_P2P:
+                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
+                    break;
+                case WifiStateMachine.WIFI_ENABLE_PENDING:
+                    replyToMessage(message, WIFI_ENABLE_PROCEED);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+    }
+
+    class WaitForUserActionState extends State {
+        @Override
+        public void enter() {
+            if (DBG) logd(getName());
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            if (DBG) logd(getName() + message.toString());
+            switch (message.what) {
                 case WIFI_DISABLE_USER_ACCEPT:
                     mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
                     transitionTo(mWaitForWifiDisableState);
                     break;
-                case WifiStateMachine.WIFI_ENABLE_PENDING:
-                    mReplyChannel.replyToMessage(message, WIFI_ENABLE_PROCEED);
+                case WifiP2pManager.ENABLE_P2P:
+                case WifiP2pManager.DISABLE_P2P:
+                    deferMessage(message);
                     break;
                 default:
                     return NOT_HANDLED;
@@ -436,31 +485,42 @@
     class WaitForWifiDisableState extends State {
         @Override
         public void enter() {
-            if (DBG) Slog.d(TAG, getName());
+            if (DBG) logd(getName());
         }
 
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) Slog.d(TAG, getName() + message.toString());
+            if (DBG) logd(getName() + message.toString());
             switch (message.what) {
                 case WifiStateMachine.P2P_ENABLE_PROCEED:
                     try {
                         mNwService.wifiFirmwareReload(mInterface, "P2P");
                     } catch (Exception e) {
-                        Slog.e(TAG, "Failed to reload p2p firmware " + e);
+                        loge("Failed to reload p2p firmware " + e);
                         // continue
                     }
+
+                    //A runtime crash can leave the interface up and
+                    //this affects p2p when supplicant starts up.
+                    //Ensure interface is down before a supplicant start.
+                    try {
+                        mNwService.setInterfaceDown(mInterface);
+                    } catch (Exception e) {
+                        if (DBG) Slog.w(TAG, "Unable to bring down wlan interface: " + e);
+                    }
+
                     if (WifiNative.startSupplicant()) {
-                        Slog.d(TAG, "Wi-fi Direct start successful");
                         mWifiMonitor.startMonitoring();
                         transitionTo(mP2pEnablingState);
                     } else {
                         notifyP2pEnableFailure();
-                        mReplyChannel.replyToMessage(mSavedEnableRequestMessage,
-                                WifiP2pManager.ENABLE_P2P_FAILED);
                         transitionTo(mP2pDisabledState);
                     }
                     break;
+                case WifiP2pManager.ENABLE_P2P:
+                case WifiP2pManager.DISABLE_P2P:
+                    deferMessage(message);
+                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -471,22 +531,32 @@
     class P2pEnablingState extends State {
         @Override
         public void enter() {
-            if (DBG) Slog.d(TAG, getName());
+            if (DBG) logd(getName());
         }
 
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) Slog.d(TAG, getName() + message.toString());
+            if (DBG) logd(getName() + message.toString());
             switch (message.what) {
                 case WifiMonitor.SUP_CONNECTION_EVENT:
-                    mReplyChannel.replyToMessage(mSavedEnableRequestMessage,
-                            WifiP2pManager.ENABLE_P2P_SUCCEEDED);
+                    logd("P2p start successful");
                     transitionTo(mInactiveState);
                     break;
-                case WifiP2pManager.DISABLE_P2P:
-                    //TODO: fix
-                    WifiNative.killSupplicant();
+                case WifiMonitor.SUP_DISCONNECTION_EVENT:
+                    if (++mP2pRestartCount <= P2P_RESTART_TRIES) {
+                        loge("Failed to start p2p, retry");
+                        WifiNative.killSupplicant();
+                        sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
+                    } else {
+                        loge("Failed " + mP2pRestartCount + " times to start p2p, quit ");
+                        mP2pRestartCount = 0;
+                    }
                     transitionTo(mP2pDisabledState);
+                    break;
+                case WifiP2pManager.ENABLE_P2P:
+                case WifiP2pManager.DISABLE_P2P:
+                    deferMessage(message);
+                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -497,27 +567,32 @@
     class P2pEnabledState extends State {
         @Override
         public void enter() {
-            if (DBG) Slog.d(TAG, getName());
+            if (DBG) logd(getName());
             sendP2pStateChangedBroadcast(true);
             mNetworkInfo.setIsAvailable(true);
+            //Start listening for new connections
+            WifiNative.p2pListen();
         }
 
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) Slog.d(TAG, getName() + message.toString());
+            if (DBG) logd(getName() + message.toString());
             switch (message.what) {
+                case WifiP2pManager.ENABLE_P2P:
+                    replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
+                    break;
                 case WifiP2pManager.DISABLE_P2P:
-                    // TODO: use stopSupplicant after control channel fixed
-                    WifiNative.killSupplicant();
+                    if (mPeers.clear()) sendP2pPeersChangedBroadcast();
+                    replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
                     transitionTo(mP2pDisablingState);
                     break;
                 case WifiP2pManager.DISCOVER_PEERS:
                     int timeout = message.arg1;
-                    WifiNative.p2pFlush();
-                    WifiNative.p2pFind(timeout);
-                   break;
-                case WifiP2pManager.REQUEST_PEERS:
-                    mReplyChannel.replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers);
+                    if (WifiNative.p2pFind(timeout)) {
+                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
+                    } else {
+                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED);
+                    }
                     break;
                 case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
                     WifiP2pDevice device = (WifiP2pDevice) message.obj;
@@ -529,23 +604,34 @@
                     if (mPeers.remove(device)) sendP2pPeersChangedBroadcast();
                     break;
                 case WifiP2pManager.CONNECT:
-                    if (DBG) Slog.d(TAG, getName() + " sending connect");
+                    if (DBG) logd(getName() + " sending connect");
                     mSavedConnectConfig = (WifiP2pConfig) message.obj;
-                    String pin = WifiNative.p2pConnect(mSavedConnectConfig);
-                    try {
-                        Integer.parseInt(pin);
-                        notifyWpsPin(pin, mSavedConnectConfig.deviceAddress);
-                    } catch (NumberFormatException ignore) {
-                        // do nothing if p2pConnect did not return a pin
+                    int netId = configuredNetworkId(mSavedConnectConfig.deviceAddress);
+                    if (netId >= 0) {
+                        //TODO: if failure, remove config and do a regular p2pConnect()
+                        WifiNative.p2pReinvoke(netId, mSavedConnectConfig.deviceAddress);
+                    } else {
+                        //TODO: Check if device is a GO and "join"
+                        String pin = WifiNative.p2pConnect(mSavedConnectConfig, false);
+                        try {
+                            Integer.parseInt(pin);
+                            notifyWpsPin(pin, mSavedConnectConfig.deviceAddress);
+                        } catch (NumberFormatException ignore) {
+                            // do nothing if p2pConnect did not return a pin
+                        }
                     }
                     updateDeviceStatus(mSavedConnectConfig.deviceAddress, Status.INVITED);
                     sendP2pPeersChangedBroadcast();
+                    replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
                     transitionTo(mGroupNegotiationState);
                     break;
-                case WifiP2pManager.REJECT:
-                    if (DBG) Slog.d(TAG, getName() + " sending reject");
-                    WifiNative.p2pReject((String) message.obj);
-                    break;
+                case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant died */
+                    loge("Connection lost, restart p2p");
+                    WifiNative.killSupplicant();
+                    WifiNative.closeSupplicantConnection();
+                    if (mPeers.clear()) sendP2pPeersChangedBroadcast();
+                    transitionTo(mP2pDisabledState);
+                    sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
                 default:
                     return NOT_HANDLED;
             }
@@ -561,28 +647,30 @@
 
     class InactiveState extends State {
         @Override public void enter() {
-            if (DBG) Slog.d(TAG, getName());
+            if (DBG) logd(getName());
         }
 
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) Slog.d(TAG, getName() + message.toString());
+            if (DBG) logd(getName() + message.toString());
             switch (message.what) {
                 case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
                     mSavedGoNegotiationConfig = (WifiP2pConfig) message.obj;
                     notifyP2pGoNegotationRequest(mSavedGoNegotiationConfig);
                     break;
                 case WifiP2pManager.CREATE_GROUP:
-                    WifiNative.p2pGroupAdd();
+                    if (WifiNative.p2pGroupAdd()) {
+                        replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
+                    } else {
+                        replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED);
+                    }
                     transitionTo(mGroupNegotiationState);
                     break;
                 case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
                     WifiP2pGroup group = (WifiP2pGroup) message.obj;
                     notifyP2pInvitationReceived(group);
                     break;
-                case WifiP2pManager.REQUEST_PEERS:
-                    return NOT_HANDLED;
-               default:
+                default:
                     return NOT_HANDLED;
             }
             return HANDLED;
@@ -592,31 +680,32 @@
     class GroupNegotiationState extends State {
         @Override
         public void enter() {
-            if (DBG) Slog.d(TAG, getName());
+            if (DBG) logd(getName());
             sendMessageDelayed(obtainMessage(GROUP_NEGOTIATION_TIMED_OUT,
                     ++mGroupNegotiationTimeoutIndex, 0), GROUP_NEGOTIATION_WAIT_TIME_MS);
         }
 
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) Slog.d(TAG, getName() + message.toString());
+            if (DBG) logd(getName() + message.toString());
             switch (message.what) {
                 // We ignore these right now, since we get a GROUP_STARTED notification
                 // afterwards
                 case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
                 case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
-                    if (DBG) Slog.d(TAG, getName() + " go success");
+                    if (DBG) logd(getName() + " go success");
                     break;
                 case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
                 case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
-                    if (DBG) Slog.d(TAG, getName() + " go failure");
+                    if (DBG) logd(getName() + " go failure");
                     updateDeviceStatus(mSavedConnectConfig.deviceAddress, Status.FAILED);
                     mSavedConnectConfig = null;
+                    sendP2pPeersChangedBroadcast();
                     transitionTo(mInactiveState);
                     break;
                 case WifiMonitor.P2P_GROUP_STARTED_EVENT:
                     mGroup = (WifiP2pGroup) message.obj;
-                    if (DBG) Slog.d(TAG, getName() + " group started");
+                    if (DBG) logd(getName() + " group started");
                     if (mGroup.isGroupOwner()) {
                         startDhcpServer(mGroup.getInterface());
                     } else {
@@ -629,14 +718,12 @@
                     }
                     transitionTo(mGroupCreatedState);
                     break;
-                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");
+                        if (DBG) logd("Group negotiation timed out");
                         updateDeviceStatus(mSavedConnectConfig.deviceAddress, Status.FAILED);
                         mSavedConnectConfig = null;
+                        sendP2pPeersChangedBroadcast();
                         transitionTo(mInactiveState);
                     }
                     break;
@@ -650,17 +737,13 @@
     class GroupCreatedState extends State {
         @Override
         public void enter() {
-            if (DBG) Slog.d(TAG, getName());
+            if (DBG) logd(getName());
             mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
-
-            if (mGroup.isGroupOwner()) {
-                sendP2pConnectionChangedBroadcast();
-            }
         }
 
         @Override
         public boolean processMessage(Message message) {
-            if (DBG) Slog.d(TAG, getName() + message.toString());
+            if (DBG) logd(getName() + message.toString());
             switch (message.what) {
                 case WifiMonitor.AP_STA_CONNECTED_EVENT:
                     //After a GO setup, STA connected event comes with interface address
@@ -668,7 +751,7 @@
                     String deviceAddress = getDeviceAddress(interfaceAddress);
                     mGroup.addClient(deviceAddress);
                     updateDeviceStatus(deviceAddress, Status.CONNECTED);
-                    if (DBG) Slog.d(TAG, getName() + " ap sta connected");
+                    if (DBG) logd(getName() + " ap sta connected");
                     sendP2pPeersChangedBroadcast();
                     break;
                 case WifiMonitor.AP_STA_DISCONNECTED_EVENT:
@@ -676,40 +759,37 @@
                     deviceAddress = getDeviceAddress(interfaceAddress);
                     updateDeviceStatus(deviceAddress, Status.AVAILABLE);
                     if (mGroup.removeClient(deviceAddress)) {
-                        if (DBG) Slog.d(TAG, "Removed client " + deviceAddress);
-                        if (mGroup.isClientListEmpty()) {
-                            Slog.d(TAG, "Client list empty, killing p2p connection");
-                            sendMessage(WifiP2pManager.REMOVE_GROUP);
-                        } else {
-                            // Just send a notification
-                            sendP2pPeersChangedBroadcast();
-                        }
+                        if (DBG) logd("Removed client " + deviceAddress);
+                        sendP2pPeersChangedBroadcast();
                     } else {
-                        if (DBG) Slog.d(TAG, "Failed to remove client " + deviceAddress);
+                        if (DBG) logd("Failed to remove client " + deviceAddress);
                         for (WifiP2pDevice c : mGroup.getClientList()) {
-                            if (DBG) Slog.d(TAG,"client " + c.deviceAddress);
+                            if (DBG) logd("client " + c.deviceAddress);
                         }
                     }
-                    if (DBG) Slog.e(TAG, getName() + " ap sta disconnected");
+                    if (DBG) loge(getName() + " ap sta disconnected");
                     break;
                 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());
+                    if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS &&
+                            dhcpInfo != null) {
+                        if (DBG) logd("DhcpInfo: " + dhcpInfo);
+                        setWifiP2pInfoOnGroupFormation(dhcpInfo.serverAddress);
                         sendP2pConnectionChangedBroadcast();
+                    } else {
+                        WifiNative.p2pGroupRemove(mGroup.getInterface());
                     }
                     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");
-                    WifiNative.p2pFlush();
-                    WifiNative.p2pGroupRemove(mGroup.getInterface());
+                    if (DBG) loge(getName() + " remove group");
+                    if (WifiNative.p2pGroupRemove(mGroup.getInterface())) {
+                        replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
+                    } else {
+                        replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED);
+                    }
                     break;
                 case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
-                    if (DBG) Slog.e(TAG, getName() + " group removed");
+                    if (DBG) loge(getName() + " group removed");
                     Collection <WifiP2pDevice> devices = mGroup.getClientList();
                     boolean changed = false;
                     for (WifiP2pDevice d : mPeers.getDeviceList()) {
@@ -722,7 +802,7 @@
                     if (mGroup.isGroupOwner()) {
                         stopDhcpServer();
                     } else {
-                        if (DBG) Slog.d(TAG, "stop DHCP client");
+                        if (DBG) logd("stop DHCP client");
                         mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
                         mDhcpStateMachine.quit();
                         mDhcpStateMachine = null;
@@ -735,33 +815,30 @@
                 case WifiMonitor.P2P_DEVICE_LOST_EVENT:
                     WifiP2pDevice device = (WifiP2pDevice) message.obj;
                     if (device.equals(mGroup.getOwner())) {
-                        Slog.d(TAG, "Lost the group owner, killing p2p connection");
-                        WifiNative.p2pFlush();
+                        logd("Lost the group owner, killing p2p connection");
                         WifiNative.p2pGroupRemove(mGroup.getInterface());
-                    } else if (mGroup.removeClient(device) && mGroup.isClientListEmpty()) {
-                        Slog.d(TAG, "Client list empty, killing p2p connection");
-                        WifiNative.p2pFlush();
-                        WifiNative.p2pGroupRemove(mGroup.getInterface());
+                    } else {
+                        mGroup.removeClient(device);
                     }
                     return NOT_HANDLED; // Do the regular device lost handling
                 case WifiP2pManager.DISABLE_P2P:
                     sendMessage(WifiP2pManager.REMOVE_GROUP);
                     deferMessage(message);
                     break;
-                case WifiP2pManager.DISCOVER_PEERS:
-                    int timeout = message.arg1;
-                    WifiNative.p2pFind(timeout);
-                    break;
                 case WifiP2pManager.CONNECT:
                     WifiP2pConfig config = (WifiP2pConfig) message.obj;
-                    Slog.d(TAG, "Inviting device : " + config.deviceAddress);
-                    WifiNative.p2pInvite(mGroup, config.deviceAddress);
-                    updateDeviceStatus(config.deviceAddress, Status.INVITED);
-                    sendP2pPeersChangedBroadcast();
+                    logd("Inviting device : " + config.deviceAddress);
+                    if (WifiNative.p2pInvite(mGroup, config.deviceAddress)) {
+                        updateDeviceStatus(config.deviceAddress, Status.INVITED);
+                        sendP2pPeersChangedBroadcast();
+                        replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
+                    } else {
+                        replyToMessage(message, WifiP2pManager.CONNECT_FAILED);
+                    }
                     // TODO: figure out updating the status to declined when invitation is rejected
                     break;
                 case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
-                    Slog.d(TAG,"===> INVITATION RESULT EVENT : " + message.obj);
+                    logd("===> INVITATION RESULT EVENT : " + message.obj);
                     break;
                 case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
                     notifyP2pProvDiscPbcRequest((WifiP2pDevice) message.obj);
@@ -782,7 +859,9 @@
         }
 
         public void exit() {
+            setWifiP2pInfoOnGroupTermination();
             mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
+            sendP2pConnectionChangedBroadcast();
         }
     }
 
@@ -806,13 +885,12 @@
     }
 
     private void sendP2pConnectionChangedBroadcast() {
-        if (DBG) Slog.d(TAG, "sending p2p connection changed broadcast");
+        if (DBG) logd("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_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
         intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
-        intent.putExtra(WifiP2pManager.EXTRA_LINK_PROPERTIES,
-                new LinkProperties (mLinkProperties));
         mContext.sendStickyBroadcast(intent);
     }
 
@@ -822,9 +900,6 @@
         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);
@@ -835,23 +910,25 @@
             /* This starts the dnsmasq server */
             mNwService.startTethering(dhcp_range);
         } catch (Exception e) {
-            Slog.e(TAG, "Error configuring interface " + intf + ", :" + e);
+            loge("Error configuring interface " + intf + ", :" + e);
             return;
         }
 
-        mLinkProperties.addDns(NetworkUtils.numericToInetAddress(serverAddress));
-        Slog.d(TAG, "Started Dhcp server on " + intf);
+        logd("Started Dhcp server on " + intf);
+
+        setWifiP2pInfoOnGroupFormation(serverAddress);
+        sendP2pConnectionChangedBroadcast();
     }
 
     private void stopDhcpServer() {
         try {
             mNwService.stopTethering();
         } catch (Exception e) {
-            Slog.e(TAG, "Error stopping Dhcp server" + e);
+            loge("Error stopping Dhcp server" + e);
             return;
         }
 
-        Slog.d(TAG, "Stopped Dhcp server");
+        logd("Stopped Dhcp server");
     }
 
     private void notifyP2pEnableFailure() {
@@ -888,7 +965,7 @@
             .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) logd(getName() + " connect " + pin.getText());
 
                             if (pin.getVisibility() == View.GONE) {
                                 mSavedGoNegotiationConfig.wpsConfig.setup = Setup.PBC;
@@ -903,10 +980,8 @@
             .setNegativeButton(r.getString(R.string.cancel), new OnClickListener() {
                         @Override
                         public void onClick(DialogInterface dialog, int which) {
-                                if (DBG) Slog.d(TAG, getName() + " reject");
-                                sendMessage(WifiP2pManager.REJECT,
-                                        mSavedGoNegotiationConfig.deviceAddress);
-                                mSavedGoNegotiationConfig = null;
+                            if (DBG) logd(getName() + " ignore connect");
+                            mSavedGoNegotiationConfig = null;
                         }
                     })
             .create();
@@ -935,7 +1010,7 @@
             .setView(textEntryView)
             .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
                         public void onClick(DialogInterface dialog, int which) {
-                                if (DBG) Slog.d(TAG, getName() + " wps_pbc");
+                                if (DBG) logd(getName() + " wps_pbc");
                                 sendMessage(WifiP2pManager.WPS_PBC);
                         }
                     })
@@ -961,7 +1036,7 @@
             .setView(textEntryView)
             .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
                     public void onClick(DialogInterface dialog, int which) {
-                        if (DBG) Slog.d(TAG, getName() + " wps_pin");
+                        if (DBG) logd(getName() + " wps_pin");
                         sendMessage(WifiP2pManager.WPS_PIN, pin.getText().toString());
                     }
                     })
@@ -989,8 +1064,7 @@
                         public void onClick(DialogInterface dialog, int which) {
                                 WifiP2pConfig config = new WifiP2pConfig();
                                 config.deviceAddress = mSavedP2pGroup.getOwner().deviceAddress;
-                                config.joinExistingGroup = true;
-                                if (DBG) Slog.d(TAG, getName() + " connect to invited group");
+                                if (DBG) logd(getName() + " connect to invited group");
                                 sendMessage(WifiP2pManager.CONNECT, config);
                                 mSavedP2pGroup = null;
                         }
@@ -1014,6 +1088,23 @@
         }
     }
 
+    //TODO: implement when wpa_supplicant is fixed
+    private int configuredNetworkId(String deviceAddress) {
+        return -1;
+    }
+
+    private void setWifiP2pInfoOnGroupFormation(String serverAddress) {
+        mWifiP2pInfo.groupFormed = true;
+        mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner();
+        mWifiP2pInfo.groupOwnerAddress = NetworkUtils.numericToInetAddress(serverAddress);
+    }
+
+    private void setWifiP2pInfoOnGroupTermination() {
+        mWifiP2pInfo.groupFormed = false;
+        mWifiP2pInfo.isGroupOwner = false;
+        mWifiP2pInfo.groupOwnerAddress = null;
+    }
+
     private String getDeviceAddress(String interfaceAddress) {
         for (WifiP2pDevice d : mPeers.getDeviceList()) {
             if (interfaceAddress.equals(WifiNative.p2pGetInterfaceAddress(d.deviceAddress))) {
@@ -1023,5 +1114,25 @@
         return null;
     }
 
+    //State machine initiated requests can have replyTo set to null indicating
+    //there are no recepients, we ignore those reply actions
+    private void replyToMessage(Message msg, int what) {
+        if (msg.replyTo == null) return;
+        mReplyChannel.replyToMessage(msg, what);
+    }
+
+    private void replyToMessage(Message msg, int what, Object obj) {
+        if (msg.replyTo == null) return;
+        mReplyChannel.replyToMessage(msg, what, obj);
+    }
+
+    private void logd(String s) {
+        Slog.d(TAG, s);
+    }
+
+    private void loge(String s) {
+        Slog.e(TAG, s);
+    }
+
     }
 }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pStatus.java b/wifi/java/android/net/wifi/p2p/WifiP2pStatus.java
deleted file mode 100644
index 1c9b76c..0000000
--- a/wifi/java/android/net/wifi/p2p/WifiP2pStatus.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.p2p;
-
-import android.os.Parcelable;
-import android.os.Parcel;
-
-/**
- * A class representing Wi-fi P2p status
- * @hide
- */
-public class WifiP2pStatus implements Parcelable {
-
-    //Comes from the wpa_supplicant
-    enum p2p_status_code {
-        SUCCESS,
-        FAIL_INFO_CURRENTLY_UNAVAILABLE,
-        FAIL_INCOMPATIBLE_PARAMS,
-        FAIL_LIMIT_REACHED,
-        FAIL_INVALID_PARAMS,
-        FAIL_UNABLE_TO_ACCOMMODATE,
-        FAIL_PREV_PROTOCOL_ERROR,
-        FAIL_NO_COMMON_CHANNELS,
-        FAIL_UNKNOWN_GROUP,
-        FAIL_BOTH_GO_INTENT_15,
-        FAIL_INCOMPATIBLE_PROV_METHOD,
-        FAIL_REJECTED_BY_USER
-    };
-
-    public WifiP2pStatus() {
-    }
-
-    //TODO: add support
-    public String toString() {
-        StringBuffer sbuf = new StringBuffer();
-        return sbuf.toString();
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    public int describeContents() {
-        return 0;
-    }
-
-    /** copy constructor {@hide} */
-    //TODO: implement
-    public WifiP2pStatus(WifiP2pStatus source) {
-        if (source != null) {
-       }
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    // STOPSHIP: implement
-    public void writeToParcel(Parcel dest, int flags) {
-    }
-
-    /** Implement the Parcelable interface {@hide} */
-    public static final Creator<WifiP2pStatus> CREATOR =
-        new Creator<WifiP2pStatus>() {
-            public WifiP2pStatus createFromParcel(Parcel in) {
-                WifiP2pStatus status = new WifiP2pStatus();
-                return status;
-            }
-
-            public WifiP2pStatus[] newArray(int size) {
-                return new WifiP2pStatus[size];
-            }
-        };
-}