Merge "New media button API." into jb-mr2-dev
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index f7933d3..c62bf32 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -338,8 +338,8 @@
      */
     public final AppWidgetHostView createView(Context context, int appWidgetId,
             AppWidgetProviderInfo appWidget) {
-        final int userId = context.getUserId();
-        AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget);
+        final int userId = mContext.getUserId();
+        AppWidgetHostView view = onCreateView(mContext, appWidgetId, appWidget);
         view.setUserId(userId);
         view.setOnClickHandler(mOnClickHandler);
         view.setAppWidget(appWidgetId, appWidget);
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index e522754..eedc372 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -121,7 +121,7 @@
 
     public Collection<String> getAllInterfaceNames() {
         Collection interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1);
-        interfaceNames.add(new String(mIfaceName));
+        if (mIfaceName != null) interfaceNames.add(new String(mIfaceName));
         for (LinkProperties stacked: mStackedLinks.values()) {
             interfaceNames.addAll(stacked.getAllInterfaceNames());
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 19283be..f9ad8c0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4411,6 +4411,14 @@
        public static final String DATA_ROAMING = "data_roaming";
 
        /**
+        * The value passed to a Mobile DataConnection via bringUp which defines the
+        * number of retries to preform when setting up the initial connection. The default
+        * value defined in DataConnectionTrackerBase#DEFAULT_MDC_INITIAL_RETRY is currently 1.
+        * @hide
+        */
+       public static final String MDC_INITIAL_MAX_RETRY = "mdc_initial_max_retry";
+
+       /**
         * Whether user has enabled development settings.
         */
        public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index c369ebe..2ec9a7d 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -399,12 +399,13 @@
     ///////////////////////////////////////////////////////////////////////////
     
     void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) {
+        layer.setLayerPaint(paint);
+
         final GLES20Layer glLayer = (GLES20Layer) layer;
-        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawLayer(mRenderer, glLayer.getLayer(), x, y, nativePaint);
+        nDrawLayer(mRenderer, glLayer.getLayer(), x, y);
     }
 
-    private static native void nDrawLayer(int renderer, int layer, float x, float y, int paint);
+    private static native void nDrawLayer(int renderer, int layer, float x, float y);
 
     void interrupt() {
         nInterrupt(mRenderer);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 925a5e76..3ce4c13 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -107,6 +107,7 @@
     private static final boolean DEBUG_IMF = false || LOCAL_LOGV;
     private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV;
     private static final boolean DEBUG_FPS = false;
+    private static final boolean DEBUG_INPUT_PROCESSING = false || LOCAL_LOGV;
 
     private static final boolean USE_RENDER_THREAD = false;
 
@@ -231,24 +232,38 @@
     int mClientWindowLayoutFlags;
     boolean mLastOverscanRequested;
 
-    /** @hide */
+     /** Event was not handled and is finished.
+      * @hide */
     public static final int EVENT_NOT_HANDLED = 0;
-    /** @hide */
+     /** Event was handled and is finished.
+      * @hide */
     public static final int EVENT_HANDLED = 1;
-    /** @hide */
-    public static final int EVENT_IN_PROGRESS = 2;
+    /** Event is waiting on the IME.
+     * @hide */
+    public static final int EVENT_PENDING_IME = 2;
+    /** Event requires post-IME dispatch.
+     * @hide */
+    public static final int EVENT_POST_IME = 3;
 
     // Pool of queued input events.
     private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
     private QueuedInputEvent mQueuedInputEventPool;
     private int mQueuedInputEventPoolSize;
 
-    // Input event queue.
-    QueuedInputEvent mFirstPendingInputEvent;
+    /* Input event queue.
+     * Pending input events are input events waiting to be handled by the application. Current
+     * input events are input events which are being handled but are waiting on some action by the
+     * IME, even if they themselves may not need to be handled by the IME.
+     */
+    QueuedInputEvent mPendingInputEventHead;
+    QueuedInputEvent mPendingInputEventTail;
     int mPendingInputEventCount;
-    QueuedInputEvent mCurrentInputEvent;
+    QueuedInputEvent mCurrentInputEventHead;
+    QueuedInputEvent mCurrentInputEventTail;
+    int mCurrentInputEventCount;
     boolean mProcessInputEventsScheduled;
     String mPendingInputEventQueueLengthCounterName = "pq";
+    String mCurrentInputEventQueueLengthCounterName = "cq";
 
     boolean mWindowAttributesChanged = false;
     int mWindowAttributesChangesFlag = 0;
@@ -646,6 +661,7 @@
                 }
 
                 mPendingInputEventQueueLengthCounterName = "pq:" + attrs.getTitle();
+                mCurrentInputEventQueueLengthCounterName = "cq:" + attrs.getTitle();
             }
         }
     }
@@ -3441,17 +3457,13 @@
                     if (DEBUG_IMF)
                         Log.v(TAG, "Sending trackball event to IME: seq="
                                 + seq + " event=" + event);
-                    int result = imm.dispatchTrackballEvent(mView.getContext(), seq, event,
+                    return imm.dispatchTrackballEvent(mView.getContext(), seq, event,
                             mInputMethodCallback);
-                    if (result != EVENT_NOT_HANDLED) {
-                        return result;
-                    }
                 }
             }
         }
 
-        // Not dispatching to IME, continue with post IME actions.
-        return deliverTrackballEventPostIme(q);
+        return EVENT_POST_IME;
     }
 
     private int deliverTrackballEventPostIme(QueuedInputEvent q) {
@@ -3599,17 +3611,13 @@
                     if (DEBUG_IMF)
                         Log.v(TAG, "Sending generic motion event to IME: seq="
                                 + seq + " event=" + event);
-                    int result = imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
+                    return imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
                             mInputMethodCallback);
-                    if (result != EVENT_NOT_HANDLED) {
-                        return result;
-                    }
                 }
             }
         }
 
-        // Not dispatching to IME, continue with post IME actions.
-        return deliverGenericMotionEventPostIme(q);
+        return EVENT_POST_IME;
     }
 
     private int deliverGenericMotionEventPostIme(QueuedInputEvent q) {
@@ -3814,17 +3822,13 @@
                     final int seq = event.getSequenceNumber();
                     if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
                             + seq + " event=" + event);
-                    int result = imm.dispatchKeyEvent(mView.getContext(), seq, event,
+                    return imm.dispatchKeyEvent(mView.getContext(), seq, event,
                             mInputMethodCallback);
-                    if (result != EVENT_NOT_HANDLED) {
-                        return result;
-                    }
                 }
             }
         }
 
-        // Not dispatching to IME, continue with post IME actions.
-        return deliverKeyEventPostIme(q);
+        return EVENT_POST_IME;
     }
 
     private int deliverKeyEventPostIme(QueuedInputEvent q) {
@@ -4425,14 +4429,13 @@
         // in response to touch events and we want to ensure that the injected keys
         // are processed in the order they were received and we cannot trust that
         // the time stamp of injected events are monotonic.
-        QueuedInputEvent last = mFirstPendingInputEvent;
+        QueuedInputEvent last = mPendingInputEventTail;
         if (last == null) {
-            mFirstPendingInputEvent = q;
+            mPendingInputEventHead = q;
+            mPendingInputEventTail = q;
         } else {
-            while (last.mNext != null) {
-                last = last.mNext;
-            }
             last.mNext = q;
+            mPendingInputEventTail = q;
         }
         mPendingInputEventCount += 1;
         Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
@@ -4455,19 +4458,44 @@
     }
 
     void doProcessInputEvents() {
-        while (mCurrentInputEvent == null && mFirstPendingInputEvent != null) {
-            QueuedInputEvent q = mFirstPendingInputEvent;
-            mFirstPendingInputEvent = q.mNext;
+        // Handle all of the available pending input events. Currently this will immediately
+        // process all of the events it can until it encounters one that must go through the IME.
+        // After that it will continue adding events to the current input queue but will wait for a
+        // response from the IME, regardless of whether that particular event needs it or not, in
+        // order to guarantee ordering consistency. This could be slightly improved by only
+        // queueing events whose source has previously encountered something that needs to be
+        // handled by the IME, and otherwise handling them immediately since we only need to
+        // guarantee ordering within a given source.
+        while (mPendingInputEventHead != null) {
+            QueuedInputEvent q = mPendingInputEventHead;
+            mPendingInputEventHead = q.mNext;
+            if (mPendingInputEventHead == null) {
+                mPendingInputEventTail = null;
+            }
             q.mNext = null;
-            mCurrentInputEvent = q;
 
             mPendingInputEventCount -= 1;
             Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                     mPendingInputEventCount);
 
-            final int result = deliverInputEvent(q);
-            if (result != EVENT_IN_PROGRESS) {
-                finishCurrentInputEvent(result == EVENT_HANDLED);
+            int result = deliverInputEvent(q);
+
+            if (result == EVENT_HANDLED || result == EVENT_NOT_HANDLED) {
+                finishInputEvent(q, result == EVENT_HANDLED);
+            } else if (result == EVENT_PENDING_IME) {
+                enqueueCurrentInputEvent(q);
+            } else {
+                q.mFlags |= QueuedInputEvent.FLAG_DELIVER_POST_IME;
+                // If the IME decided not to handle this event, and we have no events already being
+                // handled by the IME, go ahead and handle this one and then continue to the next
+                // input event. Otherwise, queue it up and handle it after whatever in front of it
+                // in the queue has been handled.
+                if (mCurrentInputEventHead == null) {
+                    result = deliverInputEventPostIme(q);
+                    finishInputEvent(q, result == EVENT_HANDLED);
+                } else {
+                    enqueueCurrentInputEvent(q);
+                }
             }
         }
 
@@ -4479,9 +4507,36 @@
         }
     }
 
+    private void enqueueCurrentInputEvent(QueuedInputEvent q) {
+        if (mCurrentInputEventHead == null) {
+            mCurrentInputEventHead = q;
+            mCurrentInputEventTail = q;
+        } else {
+            mCurrentInputEventTail.mNext = q;
+            mCurrentInputEventTail = q;
+        }
+        mCurrentInputEventCount += 1;
+        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mCurrentInputEventQueueLengthCounterName,
+                mCurrentInputEventCount);
+    }
+
+    private QueuedInputEvent dequeueCurrentInputEvent() {
+        QueuedInputEvent q = mCurrentInputEventHead;
+        mCurrentInputEventHead = q.mNext;
+        if (mCurrentInputEventHead == null) {
+            mCurrentInputEventTail = null;
+        }
+        q.mNext = null;
+        mCurrentInputEventCount -= 1;
+        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mCurrentInputEventQueueLengthCounterName,
+                mCurrentInputEventCount);
+        return q;
+    }
+
     void handleImeFinishedEvent(int seq, boolean handled) {
-        final QueuedInputEvent q = mCurrentInputEvent;
+        QueuedInputEvent q = mCurrentInputEventHead;
         if (q != null && q.mEvent.getSequenceNumber() == seq) {
+            dequeueCurrentInputEvent();
             if (DEBUG_IMF) {
                 Log.v(TAG, "IME finished event: seq=" + seq
                         + " handled=" + handled + " event=" + q);
@@ -4500,22 +4555,26 @@
                     }
                 }
             }
-            finishCurrentInputEvent(handled);
 
-            // Immediately start processing the next input event.
-            doProcessInputEvents();
+            finishInputEvent(q, handled);
+
+            // Flush all of the input events that are no longer waiting on the IME
+            while (mCurrentInputEventHead != null && (mCurrentInputEventHead.mFlags &
+                        QueuedInputEvent.FLAG_DELIVER_POST_IME) != 0) {
+                q = dequeueCurrentInputEvent();
+                final int result = deliverInputEventPostIme(q);
+                finishInputEvent(q, result == EVENT_HANDLED);
+            }
         } else {
             if (DEBUG_IMF) {
                 Log.v(TAG, "IME finished event: seq=" + seq
                         + " handled=" + handled + ", event not found!");
             }
         }
+
     }
 
-    private void finishCurrentInputEvent(boolean handled) {
-        final QueuedInputEvent q = mCurrentInputEvent;
-        mCurrentInputEvent = null;
-
+    private void finishInputEvent(QueuedInputEvent q, boolean handled) {
         if (q.mReceiver != null) {
             q.mReceiver.finishInputEvent(q.mEvent, handled);
         } else {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index dba2354..e602eb7 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1577,13 +1577,13 @@
                     final long startTime = SystemClock.uptimeMillis();
                     enqueuePendingEventLocked(startTime, seq, mCurId, callback);
                     mCurMethod.dispatchKeyEvent(seq, key, mInputMethodCallback);
-                    return ViewRootImpl.EVENT_IN_PROGRESS;
+                    return ViewRootImpl.EVENT_PENDING_IME;
                 } catch (RemoteException e) {
                     Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
                 }
             }
         }
-        return ViewRootImpl.EVENT_NOT_HANDLED;
+        return ViewRootImpl.EVENT_POST_IME;
     }
 
     /**
@@ -1600,13 +1600,13 @@
                     final long startTime = SystemClock.uptimeMillis();
                     enqueuePendingEventLocked(startTime, seq, mCurId, callback);
                     mCurMethod.dispatchTrackballEvent(seq, motion, mInputMethodCallback);
-                    return ViewRootImpl.EVENT_IN_PROGRESS;
+                    return ViewRootImpl.EVENT_PENDING_IME;
                 } catch (RemoteException e) {
                     Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
                 }
             }
         }
-        return ViewRootImpl.EVENT_NOT_HANDLED;
+        return ViewRootImpl.EVENT_POST_IME;
     }
 
     /**
@@ -1623,13 +1623,13 @@
                     final long startTime = SystemClock.uptimeMillis();
                     enqueuePendingEventLocked(startTime, seq, mCurId, callback);
                     mCurMethod.dispatchGenericMotionEvent(seq, motion, mInputMethodCallback);
-                    return ViewRootImpl.EVENT_IN_PROGRESS;
+                    return ViewRootImpl.EVENT_PENDING_IME;
                 } catch (RemoteException e) {
                     Log.w(TAG, "IME died: " + mCurId + " dropping generic motion: " + motion, e);
                 }
             }
         }
-        return ViewRootImpl.EVENT_NOT_HANDLED;
+        return ViewRootImpl.EVENT_POST_IME;
     }
 
     void finishedEvent(int seq, boolean handled) {
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index d44df0c..9137d3c 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -79,6 +79,10 @@
      * @return true if they're equal, false otherwise
      */
     public static boolean equals(byte[] array1, byte[] array2, int length) {
+        if (length < 0) {
+            throw new IllegalArgumentException();
+        }
+
         if (array1 == array2) {
             return true;
         }
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index 93f6cf6..fa35308 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.util;
 
+import android.util.Xml;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -24,6 +25,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.net.ProtocolException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -32,11 +34,8 @@
 import java.util.Map;
 import java.util.Set;
 
-import android.util.Xml;
-
 /** {@hide} */
-public class XmlUtils
-{
+public class XmlUtils {
 
     public static void skipCurrentTag(XmlPullParser parser)
             throws XmlPullParserException, IOException {
@@ -900,4 +899,42 @@
             }
         }
     }
+
+    public static int readIntAttribute(XmlPullParser in, String name) throws IOException {
+        final String value = in.getAttributeValue(null, name);
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            throw new ProtocolException("problem parsing " + name + "=" + value + " as int");
+        }
+    }
+
+    public static void writeIntAttribute(XmlSerializer out, String name, int value)
+            throws IOException {
+        out.attribute(null, name, Integer.toString(value));
+    }
+
+    public static long readLongAttribute(XmlPullParser in, String name) throws IOException {
+        final String value = in.getAttributeValue(null, name);
+        try {
+            return Long.parseLong(value);
+        } catch (NumberFormatException e) {
+            throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
+        }
+    }
+
+    public static void writeLongAttribute(XmlSerializer out, String name, long value)
+            throws IOException {
+        out.attribute(null, name, Long.toString(value));
+    }
+
+    public static boolean readBooleanAttribute(XmlPullParser in, String name) {
+        final String value = in.getAttributeValue(null, name);
+        return Boolean.parseBoolean(value);
+    }
+
+    public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value)
+            throws IOException {
+        out.attribute(null, name, Boolean.toString(value));
+    }
 }
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 3a23aa1..b87fe27 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -868,8 +868,7 @@
 }
 
 static void android_view_GLES20Canvas_drawLayer(JNIEnv* env, jobject clazz,
-        OpenGLRenderer* renderer, Layer* layer, jfloat x, jfloat y, SkPaint* paint) {
-    // TODO: don't pass the paint from java
+        OpenGLRenderer* renderer, Layer* layer, jfloat x, jfloat y) {
     renderer->drawLayer(layer, x, y);
 }
 
@@ -1051,7 +1050,7 @@
     { "nClearLayerTexture",      "(I)V",       (void*) android_view_GLES20Canvas_clearLayerTexture },
     { "nDestroyLayer",           "(I)V",       (void*) android_view_GLES20Canvas_destroyLayer },
     { "nDestroyLayerDeferred",   "(I)V",       (void*) android_view_GLES20Canvas_destroyLayerDeferred },
-    { "nDrawLayer",              "(IIFFI)V",   (void*) android_view_GLES20Canvas_drawLayer },
+    { "nDrawLayer",              "(IIFF)V",    (void*) android_view_GLES20Canvas_drawLayer },
     { "nCopyLayer",              "(II)Z",      (void*) android_view_GLES20Canvas_copyLayer },
     { "nClearLayerUpdates",      "(I)V",       (void*) android_view_GLES20Canvas_clearLayerUpdates },
     { "nPushLayerUpdate",        "(II)V",      (void*) android_view_GLES20Canvas_pushLayerUpdate },
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 7907224..020c1e9 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -368,13 +368,18 @@
     status_t status = DrawGlInfo::kStatusDone;
 
     if (isEmpty()) return status; // nothing to flush
+    renderer.restoreToCount(1);
 
     DEFER_LOGD("--flushing");
     renderer.eventMark("Flush");
 
+    // save and restore (with draw modifiers) so that reordering doesn't affect final state
     DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers();
-    renderer.restoreToCount(1);
+    renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+
     status |= replayBatchList(mBatches, renderer, dirty);
+
+    renderer.restoreToCount(1);
     renderer.setDrawModifiers(restoreDrawModifiers);
 
     DEFER_LOGD("--flush complete, returning %x", status);
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 3ae652a..a82f421 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -63,13 +63,13 @@
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static com.android.internal.util.ArrayUtils.appendInt;
 import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readLongAttribute;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.readBooleanAttribute;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.readIntAttribute;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.readLongAttribute;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.writeBooleanAttribute;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.writeIntAttribute;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.writeLongAttribute;
 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;
@@ -149,7 +149,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.net.ProtocolException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -2088,44 +2087,4 @@
         }
         fout.print("]");
     }
-
-    public static class XmlUtils {
-        public static int readIntAttribute(XmlPullParser in, String name) throws IOException {
-            final String value = in.getAttributeValue(null, name);
-            try {
-                return Integer.parseInt(value);
-            } catch (NumberFormatException e) {
-                throw new ProtocolException("problem parsing " + name + "=" + value + " as int");
-            }
-        }
-
-        public static void writeIntAttribute(XmlSerializer out, String name, int value)
-                throws IOException {
-            out.attribute(null, name, Integer.toString(value));
-        }
-
-        public static long readLongAttribute(XmlPullParser in, String name) throws IOException {
-            final String value = in.getAttributeValue(null, name);
-            try {
-                return Long.parseLong(value);
-            } catch (NumberFormatException e) {
-                throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
-            }
-        }
-
-        public static void writeLongAttribute(XmlSerializer out, String name, long value)
-                throws IOException {
-            out.attribute(null, name, Long.toString(value));
-        }
-
-        public static boolean readBooleanAttribute(XmlPullParser in, String name) {
-            final String value = in.getAttributeValue(null, name);
-            return Boolean.parseBoolean(value);
-        }
-
-        public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value)
-                throws IOException {
-            out.attribute(null, name, Boolean.toString(value));
-        }
-    }
 }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 56f4de5..b46fb2f 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -76,6 +76,7 @@
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
@@ -282,6 +283,8 @@
     private static final String SYSTEM_SECURE = "ro.secure";
     private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
 
+    private static final int MAX_SCREENSHOT_RETRIES = 3;
+
     final private KeyguardDisableHandler mKeyguardDisableHandler;
 
     private final boolean mHeadless;
@@ -5277,134 +5280,191 @@
             throw new SecurityException("Requires READ_FRAME_BUFFER permission");
         }
 
-        Bitmap rawss;
+        Bitmap rawss = null;
 
         int maxLayer = 0;
         final Rect frame = new Rect();
 
-        float scale;
+        float scale = 0;
         int dw, dh;
-        int rot;
+        int rot = Surface.ROTATION_0;
 
-        synchronized(mWindowMap) {
-            long ident = Binder.clearCallingIdentity();
+        boolean screenshotReady;
+        int minLayer;
+        if (appToken == null) {
+            screenshotReady = true;
+            minLayer = 0;
+        } else {
+            screenshotReady = false;
+            minLayer = Integer.MAX_VALUE;
+        }
 
-            final DisplayContent displayContent = getDisplayContentLocked(displayId);
-            if (displayContent == null) {
-                return null;
+        int retryCount = 0;
+        WindowState appWin = null;
+
+        do {
+            if (retryCount++ > 0) {
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                }
             }
-            final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-            dw = displayInfo.logicalWidth;
-            dh = displayInfo.logicalHeight;
-
-            int aboveAppLayer = mPolicy.windowTypeToLayerLw(TYPE_APPLICATION)
-                    * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
-            aboveAppLayer += TYPE_LAYER_MULTIPLIER;
-
-            boolean isImeTarget = mInputMethodTarget != null
-                    && mInputMethodTarget.mAppToken != null
-                    && mInputMethodTarget.mAppToken.appToken != null
-                    && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
-
-            // Figure out the part of the screen that is actually the app.
-            boolean including = false;
-            final WindowList windows = displayContent.getWindowList();
-            for (int i = windows.size() - 1; i >= 0; i--) {
-                WindowState ws = windows.get(i);
-                if (!ws.mHasSurface) {
-                    continue;
+            synchronized(mWindowMap) {
+                final DisplayContent displayContent = getDisplayContentLocked(displayId);
+                if (displayContent == null) {
+                    return null;
                 }
-                if (ws.mLayer >= aboveAppLayer) {
-                    continue;
-                }
-                // When we will skip windows: when we are not including
-                // ones behind a window we didn't skip, and we are actually
-                // taking a screenshot of a specific app.
-                if (!including && appToken != null) {
-                    // Also, we can possibly skip this window if it is not
-                    // an IME target or the application for the screenshot
-                    // is not the current IME target.
-                    if (!ws.mIsImWindow || !isImeTarget) {
-                        // And finally, this window is of no interest if it
-                        // is not associated with the screenshot app.
-                        if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
-                            continue;
+                final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+                dw = displayInfo.logicalWidth;
+                dh = displayInfo.logicalHeight;
+
+                int aboveAppLayer = mPolicy.windowTypeToLayerLw(TYPE_APPLICATION)
+                        * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
+                aboveAppLayer += TYPE_LAYER_MULTIPLIER;
+
+                boolean isImeTarget = mInputMethodTarget != null
+                        && mInputMethodTarget.mAppToken != null
+                        && mInputMethodTarget.mAppToken.appToken != null
+                        && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
+
+                // Figure out the part of the screen that is actually the app.
+                boolean including = false;
+                appWin = null;
+                final WindowList windows = displayContent.getWindowList();
+                for (int i = windows.size() - 1; i >= 0; i--) {
+                    WindowState ws = windows.get(i);
+                    if (!ws.mHasSurface) {
+                        continue;
+                    }
+                    if (ws.mLayer >= aboveAppLayer) {
+                        continue;
+                    }
+                    // When we will skip windows: when we are not including
+                    // ones behind a window we didn't skip, and we are actually
+                    // taking a screenshot of a specific app.
+                    if (!including && appToken != null) {
+                        // Also, we can possibly skip this window if it is not
+                        // an IME target or the application for the screenshot
+                        // is not the current IME target.
+                        if (!ws.mIsImWindow || !isImeTarget) {
+                            // And finally, this window is of no interest if it
+                            // is not associated with the screenshot app.
+                            if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
+                                continue;
+                            }
+                            appWin = ws;
+                        }
+                    }
+
+                    // We keep on including windows until we go past a full-screen
+                    // window.
+                    boolean fullscreen = ws.isFullscreen(dw, dh);
+                    including = !ws.mIsImWindow && !fullscreen;
+
+                    final WindowStateAnimator winAnim = ws.mWinAnimator;
+                    if (maxLayer < winAnim.mSurfaceLayer) {
+                        maxLayer = winAnim.mSurfaceLayer;
+                    }
+
+                    // Don't include wallpaper in bounds calculation
+                    if (!ws.mIsWallpaper) {
+                        final Rect wf = ws.mFrame;
+                        final Rect cr = ws.mContentInsets;
+                        int left = wf.left + cr.left;
+                        int top = wf.top + cr.top;
+                        int right = wf.right - cr.right;
+                        int bottom = wf.bottom - cr.bottom;
+                        frame.union(left, top, right, bottom);
+                    }
+
+                    if (ws.mAppToken != null && ws.mAppToken.token == appToken) {
+                        if (minLayer > ws.mWinAnimator.mSurfaceLayer) {
+                            minLayer = ws.mWinAnimator.mSurfaceLayer;
+                        }
+                        if (ws.isDisplayedLw()) {
+                            screenshotReady = true;
+                        }
+                        if (fullscreen) {
+                            // No point in continuing down through windows.
+                            break;
                         }
                     }
                 }
 
-                // We keep on including windows until we go past a full-screen
-                // window.
-                including = !ws.mIsImWindow && !ws.isFullscreen(dw, dh);
-
-                if (maxLayer < ws.mWinAnimator.mSurfaceLayer) {
-                    maxLayer = ws.mWinAnimator.mSurfaceLayer;
+                if (appToken != null && appWin == null) {
+                    // Can't find a window to snapshot.
+                    if (DEBUG_SCREENSHOT) Slog.i(TAG,
+                            "Screenshot: Couldn't find a surface matching " + appToken);
+                    return null;
+                }
+                if (!screenshotReady) {
+                    // Delay and hope that window gets drawn.
+                    if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot: No image ready for " + appToken
+                            + ", " + appWin + " drawState=" + appWin.mWinAnimator.mDrawState);
+                    continue;
                 }
 
-                // Don't include wallpaper in bounds calculation
-                if (!ws.mIsWallpaper) {
-                    final Rect wf = ws.mFrame;
-                    final Rect cr = ws.mContentInsets;
-                    int left = wf.left + cr.left;
-                    int top = wf.top + cr.top;
-                    int right = wf.right - cr.right;
-                    int bottom = wf.bottom - cr.bottom;
-                    frame.union(left, top, right, bottom);
+                // Constrain frame to the screen size.
+                frame.intersect(0, 0, dw, dh);
+
+                if (frame.isEmpty() || maxLayer == 0) {
+                    if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken
+                            + ": returning null frame=" + frame.toShortString() + " maxLayer="
+                            + maxLayer);
+                    return null;
                 }
-            }
-            Binder.restoreCallingIdentity(ident);
 
-            // Constrain frame to the screen size.
-            frame.intersect(0, 0, dw, dh);
+                // The screenshot API does not apply the current screen rotation.
+                rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
+                int fw = frame.width();
+                int fh = frame.height();
 
-            if (frame.isEmpty() || maxLayer == 0) {
-                return null;
-            }
-
-            // The screenshot API does not apply the current screen rotation.
-            rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
-            int fw = frame.width();
-            int fh = frame.height();
-
-            // Constrain thumbnail to smaller of screen width or height. Assumes aspect
-            // of thumbnail is the same as the screen (in landscape) or square.
-            float targetWidthScale = width / (float) fw;
-            float targetHeightScale = height / (float) fh;
-            if (dw <= dh) {
-                scale = targetWidthScale;
-                // If aspect of thumbnail is the same as the screen (in landscape),
-                // select the slightly larger value so we fill the entire bitmap
-                if (targetHeightScale > scale && (int) (targetHeightScale * fw) == width) {
-                    scale = targetHeightScale;
-                }
-            } else {
-                scale = targetHeightScale;
-                // If aspect of thumbnail is the same as the screen (in landscape),
-                // select the slightly larger value so we fill the entire bitmap
-                if (targetWidthScale > scale && (int) (targetWidthScale * fh) == height) {
+                // Constrain thumbnail to smaller of screen width or height. Assumes aspect
+                // of thumbnail is the same as the screen (in landscape) or square.
+                float targetWidthScale = width / (float) fw;
+                float targetHeightScale = height / (float) fh;
+                if (dw <= dh) {
                     scale = targetWidthScale;
+                    // If aspect of thumbnail is the same as the screen (in landscape),
+                    // select the slightly larger value so we fill the entire bitmap
+                    if (targetHeightScale > scale && (int) (targetHeightScale * fw) == width) {
+                        scale = targetHeightScale;
+                    }
+                } else {
+                    scale = targetHeightScale;
+                    // If aspect of thumbnail is the same as the screen (in landscape),
+                    // select the slightly larger value so we fill the entire bitmap
+                    if (targetWidthScale > scale && (int) (targetWidthScale * fh) == height) {
+                        scale = targetWidthScale;
+                    }
                 }
-            }
 
-            // The screen shot will contain the entire screen.
-            dw = (int)(dw*scale);
-            dh = (int)(dh*scale);
-            if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
-                int tmp = dw;
-                dw = dh;
-                dh = tmp;
-                rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
-            }
-            if (DEBUG_SCREENSHOT) {
-                Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from 0 to " + maxLayer);
-                for (int i = 0; i < windows.size(); i++) {
-                    WindowState win = windows.get(i);
-                    Slog.i(TAG, win + ": " + win.mLayer
-                            + " animLayer=" + win.mWinAnimator.mAnimLayer
-                            + " surfaceLayer=" + win.mWinAnimator.mSurfaceLayer);
+                // The screen shot will contain the entire screen.
+                dw = (int)(dw*scale);
+                dh = (int)(dh*scale);
+                if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
+                    int tmp = dw;
+                    dw = dh;
+                    dh = tmp;
+                    rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
                 }
+                if (DEBUG_SCREENSHOT) {
+                    Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
+                            + maxLayer + " appToken=" + appToken);
+                    for (int i = 0; i < windows.size(); i++) {
+                        WindowState win = windows.get(i);
+                        Slog.i(TAG, win + ": " + win.mLayer
+                                + " animLayer=" + win.mWinAnimator.mAnimLayer
+                                + " surfaceLayer=" + win.mWinAnimator.mSurfaceLayer);
+                    }
+                }
+                rawss = SurfaceControl.screenshot(dw, dh, minLayer, maxLayer);
             }
-            rawss = SurfaceControl.screenshot(dw, dh, 0, maxLayer);
+        } while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES);
+        if (DEBUG_SCREENSHOT && retryCount > MAX_SCREENSHOT_RETRIES) {
+            Slog.i(TAG, "Screenshot max retries " + retryCount + " of " + appToken + " appWin="
+                    + (appWin == null ? "null" : (appWin + " drawState="
+                            + appWin.mWinAnimator.mDrawState)));
         }
 
         if (rawss == null) {
@@ -5421,6 +5481,23 @@
         canvas.drawBitmap(rawss, matrix, null);
         canvas.setBitmap(null);
 
+        if (DEBUG_SCREENSHOT) {
+            // TEST IF IT's ALL BLACK
+            int[] buffer = new int[bm.getWidth() * bm.getHeight()];
+            bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
+            boolean allBlack = true;
+            for (int i = 0; i < buffer.length; i++) {
+                if (buffer[i] != Color.BLACK) {
+                    allBlack = false;
+                    break;
+                }
+            }
+            if (allBlack) {
+                Slog.i(TAG, "Screenshot " + appWin + " was all black! mSurfaceLayer=" +
+                        (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null"));
+            }
+        }
+
         rawss.recycle();
         return bm;
     }
@@ -7408,6 +7485,7 @@
         performLayoutAndPlaceSurfacesLocked();
     }
 
+    @Override
     public void setOverscan(int displayId, int left, int top, int right, int bottom) {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 80c985f..cacafd4 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -30,10 +30,11 @@
      * DISCONNECTING: Connection.disconnect() has been called, but PDP
      *                context is not yet deactivated
      * FAILED: data connection fail for all apns settings
+     * RETRYING: data connection failed but we're going to retry.
      *
      * getDataConnectionState() maps State to DataState
      *      FAILED or IDLE : DISCONNECTED
-     *      CONNECTING or SCANNING: CONNECTING
+     *      RETRYING or CONNECTING or SCANNING: CONNECTING
      *      CONNECTED : CONNECTED or DISCONNECTING
      */
     public enum State {
@@ -42,7 +43,8 @@
         SCANNING,
         CONNECTED,
         DISCONNECTING,
-        FAILED
+        FAILED,
+        RETRYING
     }
 
     public enum Activity {
@@ -89,6 +91,8 @@
     public static final int CMD_SET_DEPENDENCY_MET = BASE + 31;
     public static final int CMD_SET_POLICY_DATA_ENABLE = BASE + 32;
     public static final int EVENT_ICC_CHANGED = BASE + 33;
+    public static final int EVENT_DISCONNECT_DC_RETRYING = BASE + 34;
+    public static final int EVENT_DATA_SETUP_COMPLETE_ERROR = BASE + 35;
 
     /***** Constants *****/
 
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index dffb617..62f6aff 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -15,6 +15,8 @@
  */
 package com.android.tests.applaunch;
 
+import android.accounts.Account;
+import android.accounts.AccountManager;
 import android.app.ActivityManager;
 import android.app.ActivityManager.ProcessErrorStateInfo;
 import android.app.ActivityManagerNative;
@@ -33,9 +35,11 @@
 import android.util.Log;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * This test is intended to measure the time it takes for the apps to start.
@@ -52,6 +56,9 @@
     private static final String TAG = AppLaunch.class.getSimpleName();
     private static final String KEY_APPS = "apps";
     private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations";
+    // optional parameter: comma separated list of required account types before proceeding
+    // with the app launch
+    private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
     private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 7500; //7.5s to allow app to idle
     private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches
     private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 2000; //2s between launching apps
@@ -63,6 +70,7 @@
     private IActivityManager mAm;
     private int mLaunchIterations = 10;
     private Bundle mResult = new Bundle();
+    private Set<String> mRequiredAccounts;
 
     public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException {
         InstrumentationTestRunner instrumentation =
@@ -72,6 +80,7 @@
 
         createMappings();
         parseArgs(args);
+        checkAccountSignIn();
 
         // do initial app launch, without force stopping
         for (String app : mNameToResultKey.keySet()) {
@@ -140,6 +149,13 @@
             mNameToResultKey.put(parts[0], parts[1]);
             mNameToLaunchTime.put(parts[0], 0L);
         }
+        String requiredAccounts = args.getString(KEY_REQUIRED_ACCOUNTS);
+        if (requiredAccounts != null) {
+            mRequiredAccounts = new HashSet<String>();
+            for (String accountType : requiredAccounts.split(",")) {
+                mRequiredAccounts.add(accountType);
+            }
+        }
     }
 
     private void createMappings() {
@@ -204,6 +220,37 @@
         return result.thisTime;
     }
 
+    private void checkAccountSignIn() {
+        // ensure that the device has the required account types before starting test
+        // e.g. device must have a valid Google account sign in to measure a meaningful launch time
+        // for Gmail
+        if (mRequiredAccounts == null || mRequiredAccounts.isEmpty()) {
+            return;
+        }
+        final AccountManager am =
+                (AccountManager) getInstrumentation().getTargetContext().getSystemService(
+                        Context.ACCOUNT_SERVICE);
+        Account[] accounts = am.getAccounts();
+        // use set here in case device has multiple accounts of the same type
+        Set<String> foundAccounts = new HashSet<String>();
+        for (Account account : accounts) {
+            if (mRequiredAccounts.contains(account.type)) {
+                foundAccounts.add(account.type);
+            }
+        }
+        // check if account type matches, if not, fail test with message on what account types
+        // are missing
+        if (mRequiredAccounts.size() != foundAccounts.size()) {
+            mRequiredAccounts.removeAll(foundAccounts);
+            StringBuilder sb = new StringBuilder("Device missing these accounts:");
+            for (String account : mRequiredAccounts) {
+                sb.append(' ');
+                sb.append(account);
+            }
+            fail(sb.toString());
+        }
+    }
+
     private void closeApp(String appName, boolean forceStopApp) {
         Intent homeIntent = new Intent(Intent.ACTION_MAIN);
         homeIntent.addCategory(Intent.CATEGORY_HOME);