Merge "SurfaceTexture can now force the client to request a buffer"
diff --git a/api/current.xml b/api/current.xml
index 58aa1fd..3c78026 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -173149,7 +173149,7 @@
  static="false"
  final="false"
  deprecated="not deprecated"
- visibility=""
+ visibility="public"
 >
 <method name="destroy"
  return="void"
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cb07135..751726a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -394,6 +394,8 @@
         ParcelFileDescriptor fd;
     }
 
+    native private void dumpGraphicsInfo(FileDescriptor fd);
+
     private final class ApplicationThread extends ApplicationThreadNative {
         private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
         private static final String ONE_COUNT_COLUMN = "%17s %8d";
@@ -711,9 +713,14 @@
                 }
             }
         }
-        
+
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (args != null && args.length == 1 && args[0].equals("graphics")) {
+                pw.flush();
+                dumpGraphicsInfo(fd);
+                return;
+            }
             long nativeMax = Debug.getNativeHeapSize() / 1024;
             long nativeAllocated = Debug.getNativeHeapAllocatedSize() / 1024;
             long nativeFree = Debug.getNativeHeapFreeSize() / 1024;
diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java
index 5e9ead0..c8ea825 100644
--- a/core/java/android/hardware/usb/UsbAccessory.java
+++ b/core/java/android/hardware/usb/UsbAccessory.java
@@ -22,7 +22,21 @@
 import android.util.Log;
 
 /**
- * A class representing a USB accessory.
+ * A class representing a USB accessory, which is an external hardware component
+ * that communicates with an android application over USB.
+ * The accessory is the USB host and android the device side of the USB connection.
+ *
+ * <p>When the accessory connects, it reports its manufacturer and model names,
+ * the version of the accessory, and a user visible description of the accessory to the device.
+ * The manufacturer, model and version strings are used by the USB Manager to choose
+ * an appropriate application for the accessory.
+ * The accessory may optionally provide a unique serial number
+ * and a URL to for the accessory's website to the device as well.
+ *
+ * <p>An instance of this class is sent to the application via the
+ * {@link UsbManager#ACTION_USB_ACCESSORY_ATTACHED} Intent.
+ * The application can then call {@link UsbManager#openAccessory} to open a file descriptor
+ * for reading and writing data to and from the accessory.
  */
 public class UsbAccessory implements Parcelable {
 
@@ -63,7 +77,7 @@
     }
 
     /**
-     * Returns the manufacturer of the accessory.
+     * Returns the manufacturer name of the accessory.
      *
      * @return the accessory manufacturer
      */
diff --git a/core/java/android/hardware/usb/UsbConstants.java b/core/java/android/hardware/usb/UsbConstants.java
index 6626c9f..0e8d47c 100644
--- a/core/java/android/hardware/usb/UsbConstants.java
+++ b/core/java/android/hardware/usb/UsbConstants.java
@@ -22,45 +22,162 @@
  */
 public final class UsbConstants {
 
+    /**
+     * Bitmask used for extracting the {@link UsbEndpoint} direction from its address field.
+     * @see UsbEndpoint#getAddress
+     * @see UsbEndpoint#getDirection
+     * @see #USB_DIR_OUT
+     * @see #USB_DIR_IN
+     *
+     */
     public static final int USB_ENDPOINT_DIR_MASK = 0x80;
+    /**
+     * Used to signify direction of data for a {@link UsbEndpoint} is OUT (host to device)
+     * @see UsbEndpoint#getDirection
+     */
     public static final int USB_DIR_OUT = 0;
+    /**
+     * Used to signify direction of data for a {@link UsbEndpoint} is IN (device to host)
+     * @see UsbEndpoint#getDirection
+     */
     public static final int USB_DIR_IN = 0x80;
 
-    public static final int USB_TYPE_MASK = (0x03 << 5);
-    public static final int USB_TYPE_STANDARD = (0x00 << 5);
-    public static final int USB_TYPE_CLASS = (0x01 << 5);
-    public static final int USB_TYPE_VENDOR = (0x02 << 5);
-    public static final int USB_TYPE_RESERVED = (0x03 << 5);
-
+    /**
+     * Bitmask used for extracting the {@link UsbEndpoint} number its address field.
+     * @see UsbEndpoint#getAddress
+     * @see UsbEndpoint#getEndpointNumber
+     */
     public static final int USB_ENDPOINT_NUMBER_MASK = 0x0f;
 
-    // flags for endpoint attributes
+    /**
+     * Bitmask used for extracting the {@link UsbEndpoint} type from its address field.
+     * @see UsbEndpoint#getAddress
+     * @see UsbEndpoint#getType
+     * @see #USB_ENDPOINT_XFER_CONTROL
+     * @see #USB_ENDPOINT_XFER_ISOC
+     * @see #USB_ENDPOINT_XFER_BULK
+     * @see #USB_ENDPOINT_XFER_INT
+     */
     public static final int USB_ENDPOINT_XFERTYPE_MASK = 0x03;
+    /**
+     * Control endpoint type (endpoint zero)
+     * @see UsbEndpoint#getType
+     */
     public static final int USB_ENDPOINT_XFER_CONTROL = 0;
+    /**
+     * Isochronous endpoint type (currently not supported)
+     * @see UsbEndpoint#getType
+     */
     public static final int USB_ENDPOINT_XFER_ISOC = 1;
+    /**
+     * Bulk endpoint type
+     * @see UsbEndpoint#getType
+     */
     public static final int USB_ENDPOINT_XFER_BULK = 2;
+    /**
+     * Interrupt endpoint type
+     * @see UsbEndpoint#getType
+     */
     public static final int USB_ENDPOINT_XFER_INT = 3;
 
-    // USB classes
+
+    /**
+     * Bitmask used for encoding the request type for a control request on endpoint zero.
+     */
+    public static final int USB_TYPE_MASK = (0x03 << 5);
+    /**
+     * Used to specify that an endpoint zero control request is a standard request.
+     */
+    public static final int USB_TYPE_STANDARD = (0x00 << 5);
+    /**
+     * Used to specify that an endpoint zero control request is a class specific request.
+     */
+    public static final int USB_TYPE_CLASS = (0x01 << 5);
+    /**
+     * Used to specify that an endpoint zero control request is a vendor specific request.
+     */
+    public static final int USB_TYPE_VENDOR = (0x02 << 5);
+    /**
+     * Reserved endpoint zero control request type (currently unused).
+     */
+    public static final int USB_TYPE_RESERVED = (0x03 << 5);
+
+
+    /**
+     * USB class indicating that the class is determined on a per-interface basis.
+     */
     public static final int USB_CLASS_PER_INTERFACE = 0;
+    /**
+     * USB class for audio devices.
+     */
     public static final int USB_CLASS_AUDIO = 1;
+    /**
+     * USB class for communication devices.
+     */
     public static final int USB_CLASS_COMM = 2;
+    /**
+     * USB class for human interface devices (for example, mice and keyboards).
+     */
     public static final int USB_CLASS_HID = 3;
+    /**
+     * USB class for physical devices.
+     */
     public static final int USB_CLASS_PHYSICA = 5;
+    /**
+     * USB class for still image devices (digital cameras).
+     */
     public static final int USB_CLASS_STILL_IMAGE = 6;
+    /**
+     * USB class for printers.
+     */
     public static final int USB_CLASS_PRINTER = 7;
+    /**
+     * USB class for mass storage devices.
+     */
     public static final int USB_CLASS_MASS_STORAGE = 8;
+    /**
+     * USB class for USB hubs.
+     */
     public static final int USB_CLASS_HUB = 9;
+    /**
+     * USB class for CDC devices (communications device class).
+     */
     public static final int USB_CLASS_CDC_DATA = 0x0a;
+    /**
+     * USB class for content smart card devices.
+     */
     public static final int USB_CLASS_CSCID = 0x0b;
+    /**
+     * USB class for content security devices.
+     */
     public static final int USB_CLASS_CONTENT_SEC = 0x0d;
+    /**
+     * USB class for video devices.
+     */
     public static final int USB_CLASS_VIDEO = 0x0e;
+    /**
+     * USB class for wireless controller devices.
+     */
     public static final int USB_CLASS_WIRELESS_CONTROLLER = 0xe0;
+    /**
+     * USB class for wireless miscellaneous devices.
+     */
     public static final int USB_CLASS_MISC = 0xef;
+    /**
+     * Application specific USB class.
+     */
     public static final int USB_CLASS_APP_SPEC = 0xfe;
+    /**
+     * Vendor specific USB class.
+     */
     public static final int USB_CLASS_VENDOR_SPEC = 0xff;
 
-    // USB subclasses
-    public static final int USB_INTERFACE_SUBCLASS_BOOT = 1;    // for HID class
+    /**
+     * Boot subclass for HID devices.
+     */
+    public static final int USB_INTERFACE_SUBCLASS_BOOT = 1;
+    /**
+     * Vendor specific USB subclass.
+     */
     public static final int USB_SUBCLASS_VENDOR_SPEC = 0xff;
-}
\ No newline at end of file
+}
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index 9e536a7..af3f7f0 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -24,7 +24,16 @@
 import java.io.FileDescriptor;
 
 /**
- * A class representing a USB device.
+ * This class represents a USB device attached to the android device with the android device
+ * acting as the USB host.
+ * Each device contains one or more {@link UsbInterface}s, each of which contains a number of
+ * {@link UsbEndpoint}s (the channels via which data is transmitted over USB).
+ *
+ * <p> This class contains information (along with {@link UsbInterface} and {@link UsbEndpoint})
+ * that describes the capabilities of the USB device.
+ * To communicate with the device, you open a {@link UsbDeviceConnection} for the device
+ * and use {@link UsbRequest} to send and receive data on an endpoint.
+ * {@link UsbDeviceConnection#controlTransfer} is used for control requests on endpoint zero.
  */
 public class UsbDevice implements Parcelable {
 
@@ -96,8 +105,7 @@
 
     /**
      * Returns the devices's class field.
-     * Some useful constants for USB device classes can be found in
-     * {@link android.hardware.usb.UsbConstants}
+     * Some useful constants for USB device classes can be found in {@link UsbConstants}.
      *
      * @return the devices's class
      */
@@ -115,7 +123,7 @@
     }
 
     /**
-     * Returns the device's subclass field.
+     * Returns the device's protocol field.
      *
      * @return the device's protocol
      */
@@ -124,7 +132,7 @@
     }
 
     /**
-     * Returns the number of {@link android.hardware.usb.UsbInterface}s this device contains.
+     * Returns the number of {@link UsbInterface}s this device contains.
      *
      * @return the number of interfaces
      */
@@ -133,7 +141,7 @@
     }
 
     /**
-     * Returns the {@link android.hardware.usb.UsbInterface} at the given index.
+     * Returns the {@link UsbInterface} at the given index.
      *
      * @return the interface
      */
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 876287c..a153c0b 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -23,7 +23,8 @@
 
 
 /**
- * A class representing a USB device.
+ * This class is used for sending and receiving data and control messages to a USB device.
+ * Instances of this class are created by {@link UsbManager#openDevice}.
  */
 public class UsbDeviceConnection {
 
@@ -48,15 +49,20 @@
 
     /**
      * Releases all system resources related to the device.
+     * Once the object is closed it cannot be used again.
+     * The client must call {@link UsbManager#openDevice} again
+     * to retrieve a new instance to reestablish communication with the device.
      */
     public void close() {
         native_close();
     }
 
     /**
-     * Returns an integer file descriptor for the device, or
+     * Returns the native file descriptor for the device, or
      * -1 if the device is not opened.
-     * This is intended for passing to native code to access the device
+     * This is intended for passing to native code to access the device.
+     *
+     * @return the native file descriptor
      */
     public int getFileDescriptor() {
         return native_get_fd();
@@ -65,7 +71,8 @@
     /**
      * Claims exclusive access to a {@link android.hardware.usb.UsbInterface}.
      * This must be done before sending or receiving data on any
-     * {@link android.hardware.usb.UsbEndpoint}s belonging to the interface
+     * {@link android.hardware.usb.UsbEndpoint}s belonging to the interface.
+     *
      * @param intf the interface to claim
      * @param force true to disconnect kernel driver if necessary
      * @return true if the interface was successfully claimed
diff --git a/core/java/android/hardware/usb/UsbEndpoint.java b/core/java/android/hardware/usb/UsbEndpoint.java
index bc2c2c1..753a447 100644
--- a/core/java/android/hardware/usb/UsbEndpoint.java
+++ b/core/java/android/hardware/usb/UsbEndpoint.java
@@ -21,7 +21,14 @@
 import android.os.Parcelable;
 
 /**
- * A class representing an endpoint on a {@link android.hardware.usb.UsbInterface}.
+ * A class representing an endpoint on a {@link UsbInterface}.
+ * Endpoints are the channels for sending and receiving data over USB.
+ * Typically bulk endpoints are used for sending non-trivial amounts of data.
+ * Interrupt endpoints are used for sending small amounts of data, typically events,
+ * separately from the main data streams.
+ * The endpoint zero is a special endpoint for control messages sent from the host
+ * to device.
+ * Isochronous endpoints are currently unsupported.
  */
 public class UsbEndpoint implements Parcelable {
 
@@ -43,6 +50,10 @@
 
     /**
      * Returns the endpoint's address field.
+     * The address is a bitfield containing both the endpoint number
+     * as well as the data direction of the endpoint.
+     * the endpoint number and direction can also be accessed via
+     * {@link #getEndpointNumber} and {@link #getDirection}.
      *
      * @return the endpoint's address
      */
@@ -61,10 +72,12 @@
 
     /**
      * Returns the endpoint's direction.
-     * Returns {@link android.hardware.usb.UsbConstants#USB_DIR_OUT}
+     * Returns {@link UsbConstants#USB_DIR_OUT}
      * if the direction is host to device, and
-     * {@link android.hardware.usb.UsbConstants#USB_DIR_IN} if the
+     * {@link UsbConstants#USB_DIR_IN} if the
      * direction is device to host.
+     * @see {@link UsbConstants#USB_DIR_IN}
+     * @see {@link UsbConstants#USB_DIR_OUT}
      *
      * @return the endpoint's direction
      */
@@ -85,10 +98,10 @@
      * Returns the endpoint's type.
      * Possible results are:
      * <ul>
-     * <li>{@link android.hardware.usb.UsbConstants#USB_ENDPOINT_XFER_CONTROL} (endpoint zero)
-     * <li>{@link android.hardware.usb.UsbConstants#USB_ENDPOINT_XFER_ISOC} (isochronous endpoint)
-     * <li>{@link android.hardware.usb.UsbConstants#USB_ENDPOINT_XFER_BULK} (bulk endpoint)
-     * <li>{@link android.hardware.usb.UsbConstants#USB_ENDPOINT_XFER_INT} (interrupt endpoint)
+     * <li>{@link UsbConstants#USB_ENDPOINT_XFER_CONTROL} (endpoint zero)
+     * <li>{@link UsbConstants#USB_ENDPOINT_XFER_ISOC} (isochronous endpoint)
+     * <li>{@link UsbConstants#USB_ENDPOINT_XFER_BULK} (bulk endpoint)
+     * <li>{@link UsbConstants#USB_ENDPOINT_XFER_INT} (interrupt endpoint)
      * </ul>
      *
      * @return the endpoint's type
diff --git a/core/java/android/hardware/usb/UsbInterface.java b/core/java/android/hardware/usb/UsbInterface.java
index 2b4c7c0..3b51063 100644
--- a/core/java/android/hardware/usb/UsbInterface.java
+++ b/core/java/android/hardware/usb/UsbInterface.java
@@ -21,7 +21,11 @@
 import android.os.Parcelable;
 
 /**
- * A class representing an interface on a {@link android.hardware.usb.UsbDevice}.
+ * A class representing an interface on a {@link UsbDevice}.
+ * USB devices can have one or more interfaces, each one providing a different
+ * piece of functionality, separate from the other interfaces.
+ * An interface will have one or more {@link UsbEndpoint}s, which are the
+ * channels by which the host transfers data with the device.
  */
 public class UsbInterface implements Parcelable {
 
@@ -46,6 +50,7 @@
 
     /**
      * Returns the interface's ID field.
+     * This is an integer that uniquely identifies the interface on the device.
      *
      * @return the interface's ID
      */
@@ -55,8 +60,7 @@
 
     /**
      * Returns the interface's class field.
-     * Some useful constants for USB classes can be found in
-     * {@link android.hardware.usb.UsbConstants}
+     * Some useful constants for USB classes can be found in {@link UsbConstants}
      *
      * @return the interface's class
      */
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 7bf278a..60b37a1 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -31,7 +31,8 @@
 import java.util.HashMap;
 
 /**
- * This class allows you to access the state of USB, both in host and device mode.
+ * This class allows you to access the state of USB and communicate with USB devices.
+ * Currently only host mode is supported in the public API.
  *
  * <p>You can obtain an instance of this class by calling
  * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java
index 5fe6c8c..2252248 100644
--- a/core/java/android/hardware/usb/UsbRequest.java
+++ b/core/java/android/hardware/usb/UsbRequest.java
@@ -24,8 +24,13 @@
  * A class representing USB request packet.
  * This can be used for both reading and writing data to or from a
  * {@link android.hardware.usb.UsbDeviceConnection}.
- * UsbRequests are sent asynchronously via {@link #queue} and the results
- * are read by {@link android.hardware.usb.UsbDeviceConnection#requestWait}.
+ * UsbRequests can be used to transfer data on bulk and interrupt endpoints.
+ * Requests on bulk endpoints can be sent synchronously via {@link UsbDeviceConnection#bulkTransfer}
+ * or asynchronously via {@link #queue} and {@link UsbDeviceConnection#requestWait}.
+ * Requests on interrupt endpoints are only send and received asynchronously.
+ *
+ * <p>Requests on endpoint zero are not supported by this class;
+ * use {@link UsbDeviceConnection#controlTransfer} for endpoint zero requests instead.
  */
 public class UsbRequest {
 
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index c1e1049..f284f51 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -245,6 +245,13 @@
      */
     private VelocityTracker mVelocityTracker;
 
+    /**
+     * Consistency verifier for debugging purposes.
+     */
+    private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
+            InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+                    new InputEventConsistencyVerifier(this, 0) : null;
+
     private class GestureHandler extends Handler {
         GestureHandler() {
             super();
@@ -443,6 +450,10 @@
      *              else false.
      */
     public boolean onTouchEvent(MotionEvent ev) {
+        if (mInputEventConsistencyVerifier != null) {
+            mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
+        }
+
         final int action = ev.getAction();
         final float y = ev.getY();
         final float x = ev.getX();
diff --git a/core/java/android/view/InputEvent.java b/core/java/android/view/InputEvent.java
index 03189ca..87e7ea7 100755
--- a/core/java/android/view/InputEvent.java
+++ b/core/java/android/view/InputEvent.java
@@ -68,6 +68,14 @@
     public abstract void setSource(int source);
 
     /**
+     * Copies the event.
+     *
+     * @return A deep copy of the event.
+     * @hide
+     */
+    public abstract InputEvent copy();
+
+    /**
      * Recycles the event.
      * This method should only be used by the system since applications do not
      * expect {@link KeyEvent} objects to be recycled, although {@link MotionEvent}
@@ -76,6 +84,28 @@
      */
     public abstract void recycle();
 
+    /**
+     * Gets a private flag that indicates when the system has detected that this input event
+     * may be inconsistent with respect to the sequence of previously delivered input events,
+     * such as when a key up event is sent but the key was not down or when a pointer
+     * move event is sent but the pointer is not down.
+     *
+     * @return True if this event is tainted.
+     * @hide
+     */
+    public abstract boolean isTainted();
+
+    /**
+     * Sets a private flag that indicates when the system has detected that this input event
+     * may be inconsistent with respect to the sequence of previously delivered input events,
+     * such as when a key up event is sent but the key was not down or when a pointer
+     * move event is sent but the pointer is not down.
+     *
+     * @param tainted True if this event is tainted.
+     * @hide
+     */
+    public abstract void setTainted(boolean tainted);
+
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java
new file mode 100644
index 0000000..6618f07
--- /dev/null
+++ b/core/java/android/view/InputEventConsistencyVerifier.java
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2010 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.view;
+
+import android.os.Build;
+import android.util.Log;
+
+/**
+ * Checks whether a sequence of input events is self-consistent.
+ * Logs a description of each problem detected.
+ * <p>
+ * When a problem is detected, the event is tainted.  This mechanism prevents the same
+ * error from being reported multiple times.
+ * </p>
+ *
+ * @hide
+ */
+public final class InputEventConsistencyVerifier {
+    private static final String TAG = "InputEventConsistencyVerifier";
+    private static final boolean IS_ENG_BUILD = "eng".equals(Build.TYPE);
+
+    // The number of recent events to log when a problem is detected.
+    // Can be set to 0 to disable logging recent events but the runtime overhead of
+    // this feature is negligible on current hardware.
+    private static final int RECENT_EVENTS_TO_LOG = 5;
+
+    // The object to which the verifier is attached.
+    private final Object mCaller;
+
+    // Consistency verifier flags.
+    private final int mFlags;
+
+    // The most recently checked event and the nesting level at which it was checked.
+    // This is only set when the verifier is called from a nesting level greater than 0
+    // so that the verifier can detect when it has been asked to verify the same event twice.
+    // It does not make sense to examine the contents of the last event since it may have
+    // been recycled.
+    private InputEvent mLastEvent;
+    private int mLastNestingLevel;
+
+    // Copy of the most recent events.
+    private InputEvent[] mRecentEvents;
+    private int mMostRecentEventIndex;
+
+    // Current event and its type.
+    private InputEvent mCurrentEvent;
+    private String mCurrentEventType;
+
+    // Linked list of key state objects.
+    private KeyState mKeyStateList;
+
+    // Current state of the trackball.
+    private boolean mTrackballDown;
+
+    // Bitfield of pointer ids that are currently down.
+    // Assumes that the largest possible pointer id is 31, which is potentially subject to change.
+    // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
+    private int mTouchEventStreamPointers;
+
+    // The device id and source of the current stream of touch events.
+    private int mTouchEventStreamDeviceId = -1;
+    private int mTouchEventStreamSource;
+
+    // Set to true when we discover that the touch event stream is inconsistent.
+    // Reset on down or cancel.
+    private boolean mTouchEventStreamIsTainted;
+
+    // Set to true if we received hover enter.
+    private boolean mHoverEntered;
+
+    // The current violation message.
+    private StringBuilder mViolationMessage;
+
+    /**
+     * Indicates that the verifier is intended to act on raw device input event streams.
+     * Disables certain checks for invariants that are established by the input dispatcher
+     * itself as it delivers input events, such as key repeating behavior.
+     */
+    public static final int FLAG_RAW_DEVICE_INPUT = 1 << 0;
+
+    /**
+     * Creates an input consistency verifier.
+     * @param caller The object to which the verifier is attached.
+     * @param flags Flags to the verifier, or 0 if none.
+     */
+    public InputEventConsistencyVerifier(Object caller, int flags) {
+        this.mCaller = caller;
+        this.mFlags = flags;
+    }
+
+    /**
+     * Determines whether the instrumentation should be enabled.
+     * @return True if it should be enabled.
+     */
+    public static boolean isInstrumentationEnabled() {
+        return IS_ENG_BUILD;
+    }
+
+    /**
+     * Resets the state of the input event consistency verifier.
+     */
+    public void reset() {
+        mLastEvent = null;
+        mLastNestingLevel = 0;
+        mTrackballDown = false;
+        mTouchEventStreamPointers = 0;
+        mTouchEventStreamIsTainted = false;
+        mHoverEntered = false;
+    }
+
+    /**
+     * Checks an arbitrary input event.
+     * @param event The event.
+     * @param nestingLevel The nesting level: 0 if called from the base class,
+     * or 1 from a subclass.  If the event was already checked by this consistency verifier
+     * at a higher nesting level, it will not be checked again.  Used to handle the situation
+     * where a subclass dispatching method delegates to its superclass's dispatching method
+     * and both dispatching methods call into the consistency verifier.
+     */
+    public void onInputEvent(InputEvent event, int nestingLevel) {
+        if (event instanceof KeyEvent) {
+            final KeyEvent keyEvent = (KeyEvent)event;
+            onKeyEvent(keyEvent, nestingLevel);
+        } else {
+            final MotionEvent motionEvent = (MotionEvent)event;
+            if (motionEvent.isTouchEvent()) {
+                onTouchEvent(motionEvent, nestingLevel);
+            } else if ((motionEvent.getSource() & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+                onTrackballEvent(motionEvent, nestingLevel);
+            } else {
+                onGenericMotionEvent(motionEvent, nestingLevel);
+            }
+        }
+    }
+
+    /**
+     * Checks a key event.
+     * @param event The event.
+     * @param nestingLevel The nesting level: 0 if called from the base class,
+     * or 1 from a subclass.  If the event was already checked by this consistency verifier
+     * at a higher nesting level, it will not be checked again.  Used to handle the situation
+     * where a subclass dispatching method delegates to its superclass's dispatching method
+     * and both dispatching methods call into the consistency verifier.
+     */
+    public void onKeyEvent(KeyEvent event, int nestingLevel) {
+        if (!startEvent(event, nestingLevel, "KeyEvent")) {
+            return;
+        }
+
+        try {
+            ensureMetaStateIsNormalized(event.getMetaState());
+
+            final int action = event.getAction();
+            final int deviceId = event.getDeviceId();
+            final int source = event.getSource();
+            final int keyCode = event.getKeyCode();
+            switch (action) {
+                case KeyEvent.ACTION_DOWN: {
+                    KeyState state = findKeyState(deviceId, source, keyCode, /*remove*/ false);
+                    if (state != null) {
+                        // If the key is already down, ensure it is a repeat.
+                        // We don't perform this check when processing raw device input
+                        // because the input dispatcher itself is responsible for setting
+                        // the key repeat count before it delivers input events.
+                        if ((mFlags & FLAG_RAW_DEVICE_INPUT) == 0
+                                && event.getRepeatCount() == 0) {
+                            problem("ACTION_DOWN but key is already down and this event "
+                                    + "is not a key repeat.");
+                        }
+                    } else {
+                        addKeyState(deviceId, source, keyCode);
+                    }
+                    break;
+                }
+                case KeyEvent.ACTION_UP: {
+                    KeyState state = findKeyState(deviceId, source, keyCode, /*remove*/ true);
+                    if (state == null) {
+                        problem("ACTION_UP but key was not down.");
+                    } else {
+                        state.recycle();
+                    }
+                    break;
+                }
+                case KeyEvent.ACTION_MULTIPLE:
+                    break;
+                default:
+                    problem("Invalid action " + KeyEvent.actionToString(action)
+                            + " for key event.");
+                    break;
+            }
+        } finally {
+            finishEvent(false);
+        }
+    }
+
+    /**
+     * Checks a trackball event.
+     * @param event The event.
+     * @param nestingLevel The nesting level: 0 if called from the base class,
+     * or 1 from a subclass.  If the event was already checked by this consistency verifier
+     * at a higher nesting level, it will not be checked again.  Used to handle the situation
+     * where a subclass dispatching method delegates to its superclass's dispatching method
+     * and both dispatching methods call into the consistency verifier.
+     */
+    public void onTrackballEvent(MotionEvent event, int nestingLevel) {
+        if (!startEvent(event, nestingLevel, "TrackballEvent")) {
+            return;
+        }
+
+        try {
+            ensureMetaStateIsNormalized(event.getMetaState());
+
+            final int action = event.getAction();
+            final int source = event.getSource();
+            if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+                switch (action) {
+                    case MotionEvent.ACTION_DOWN:
+                        if (mTrackballDown) {
+                            problem("ACTION_DOWN but trackball is already down.");
+                        } else {
+                            mTrackballDown = true;
+                        }
+                        ensureHistorySizeIsZeroForThisAction(event);
+                        ensurePointerCountIsOneForThisAction(event);
+                        break;
+                    case MotionEvent.ACTION_UP:
+                        if (!mTrackballDown) {
+                            problem("ACTION_UP but trackball is not down.");
+                        } else {
+                            mTrackballDown = false;
+                        }
+                        ensureHistorySizeIsZeroForThisAction(event);
+                        ensurePointerCountIsOneForThisAction(event);
+                        break;
+                    case MotionEvent.ACTION_MOVE:
+                        ensurePointerCountIsOneForThisAction(event);
+                        break;
+                    default:
+                        problem("Invalid action " + MotionEvent.actionToString(action)
+                                + " for trackball event.");
+                        break;
+                }
+
+                if (mTrackballDown && event.getPressure() <= 0) {
+                    problem("Trackball is down but pressure is not greater than 0.");
+                } else if (!mTrackballDown && event.getPressure() != 0) {
+                    problem("Trackball is up but pressure is not equal to 0.");
+                }
+            } else {
+                problem("Source was not SOURCE_CLASS_TRACKBALL.");
+            }
+        } finally {
+            finishEvent(false);
+        }
+    }
+
+    /**
+     * Checks a touch event.
+     * @param event The event.
+     * @param nestingLevel The nesting level: 0 if called from the base class,
+     * or 1 from a subclass.  If the event was already checked by this consistency verifier
+     * at a higher nesting level, it will not be checked again.  Used to handle the situation
+     * where a subclass dispatching method delegates to its superclass's dispatching method
+     * and both dispatching methods call into the consistency verifier.
+     */
+    public void onTouchEvent(MotionEvent event, int nestingLevel) {
+        if (!startEvent(event, nestingLevel, "TouchEvent")) {
+            return;
+        }
+
+        final int action = event.getAction();
+        final boolean newStream = action == MotionEvent.ACTION_DOWN
+                || action == MotionEvent.ACTION_CANCEL;
+        if (mTouchEventStreamIsTainted) {
+            if (newStream) {
+                mTouchEventStreamIsTainted = false;
+            } else {
+                finishEvent(true);
+                return;
+            }
+        }
+
+        try {
+            ensureMetaStateIsNormalized(event.getMetaState());
+
+            final int deviceId = event.getDeviceId();
+            final int source = event.getSource();
+
+            if (!newStream && mTouchEventStreamDeviceId != -1
+                    && (mTouchEventStreamDeviceId != deviceId
+                            || mTouchEventStreamSource != source)) {
+                problem("Touch event stream contains events from multiple sources: "
+                        + "previous device id " + mTouchEventStreamDeviceId
+                        + ", previous source " + Integer.toHexString(mTouchEventStreamSource)
+                        + ", new device id " + deviceId
+                        + ", new source " + Integer.toHexString(source));
+            }
+            mTouchEventStreamDeviceId = deviceId;
+            mTouchEventStreamSource = source;
+
+            final int pointerCount = event.getPointerCount();
+            if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+                switch (action) {
+                    case MotionEvent.ACTION_DOWN:
+                        if (mTouchEventStreamPointers != 0) {
+                            problem("ACTION_DOWN but pointers are already down.  "
+                                    + "Probably missing ACTION_UP from previous gesture.");
+                        }
+                        ensureHistorySizeIsZeroForThisAction(event);
+                        ensurePointerCountIsOneForThisAction(event);
+                        mTouchEventStreamPointers = 1 << event.getPointerId(0);
+                        break;
+                    case MotionEvent.ACTION_UP:
+                        ensureHistorySizeIsZeroForThisAction(event);
+                        ensurePointerCountIsOneForThisAction(event);
+                        mTouchEventStreamPointers = 0;
+                        mTouchEventStreamIsTainted = false;
+                        break;
+                    case MotionEvent.ACTION_MOVE: {
+                        final int expectedPointerCount =
+                                Integer.bitCount(mTouchEventStreamPointers);
+                        if (pointerCount != expectedPointerCount) {
+                            problem("ACTION_MOVE contained " + pointerCount
+                                    + " pointers but there are currently "
+                                    + expectedPointerCount + " pointers down.");
+                            mTouchEventStreamIsTainted = true;
+                        }
+                        break;
+                    }
+                    case MotionEvent.ACTION_CANCEL:
+                        mTouchEventStreamPointers = 0;
+                        mTouchEventStreamIsTainted = false;
+                        break;
+                    case MotionEvent.ACTION_OUTSIDE:
+                        if (mTouchEventStreamPointers != 0) {
+                            problem("ACTION_OUTSIDE but pointers are still down.");
+                        }
+                        ensureHistorySizeIsZeroForThisAction(event);
+                        ensurePointerCountIsOneForThisAction(event);
+                        mTouchEventStreamIsTainted = false;
+                        break;
+                    default: {
+                        final int actionMasked = event.getActionMasked();
+                        final int actionIndex = event.getActionIndex();
+                        if (actionMasked == MotionEvent.ACTION_POINTER_DOWN) {
+                            if (mTouchEventStreamPointers == 0) {
+                                problem("ACTION_POINTER_DOWN but no other pointers were down.");
+                                mTouchEventStreamIsTainted = true;
+                            }
+                            if (actionIndex < 0 || actionIndex >= pointerCount) {
+                                problem("ACTION_POINTER_DOWN index is " + actionIndex
+                                        + " but the pointer count is " + pointerCount + ".");
+                                mTouchEventStreamIsTainted = true;
+                            } else {
+                                final int id = event.getPointerId(actionIndex);
+                                final int idBit = 1 << id;
+                                if ((mTouchEventStreamPointers & idBit) != 0) {
+                                    problem("ACTION_POINTER_DOWN specified pointer id " + id
+                                            + " which is already down.");
+                                    mTouchEventStreamIsTainted = true;
+                                } else {
+                                    mTouchEventStreamPointers |= idBit;
+                                }
+                            }
+                            ensureHistorySizeIsZeroForThisAction(event);
+                        } else if (actionMasked == MotionEvent.ACTION_POINTER_UP) {
+                            if (actionIndex < 0 || actionIndex >= pointerCount) {
+                                problem("ACTION_POINTER_UP index is " + actionIndex
+                                        + " but the pointer count is " + pointerCount + ".");
+                                mTouchEventStreamIsTainted = true;
+                            } else {
+                                final int id = event.getPointerId(actionIndex);
+                                final int idBit = 1 << id;
+                                if ((mTouchEventStreamPointers & idBit) == 0) {
+                                    problem("ACTION_POINTER_UP specified pointer id " + id
+                                            + " which is not currently down.");
+                                    mTouchEventStreamIsTainted = true;
+                                } else {
+                                    mTouchEventStreamPointers &= ~idBit;
+                                }
+                            }
+                            ensureHistorySizeIsZeroForThisAction(event);
+                        } else {
+                            problem("Invalid action " + MotionEvent.actionToString(action)
+                                    + " for touch event.");
+                        }
+                        break;
+                    }
+                }
+            } else {
+                problem("Source was not SOURCE_CLASS_POINTER.");
+            }
+        } finally {
+            finishEvent(false);
+        }
+    }
+
+    /**
+     * Checks a generic motion event.
+     * @param event The event.
+     * @param nestingLevel The nesting level: 0 if called from the base class,
+     * or 1 from a subclass.  If the event was already checked by this consistency verifier
+     * at a higher nesting level, it will not be checked again.  Used to handle the situation
+     * where a subclass dispatching method delegates to its superclass's dispatching method
+     * and both dispatching methods call into the consistency verifier.
+     */
+    public void onGenericMotionEvent(MotionEvent event, int nestingLevel) {
+        if (!startEvent(event, nestingLevel, "GenericMotionEvent")) {
+            return;
+        }
+
+        try {
+            ensureMetaStateIsNormalized(event.getMetaState());
+
+            final int action = event.getAction();
+            final int source = event.getSource();
+            if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+                switch (action) {
+                    case MotionEvent.ACTION_HOVER_ENTER:
+                        ensurePointerCountIsOneForThisAction(event);
+                        mHoverEntered = true;
+                        break;
+                    case MotionEvent.ACTION_HOVER_MOVE:
+                        ensurePointerCountIsOneForThisAction(event);
+                        break;
+                    case MotionEvent.ACTION_HOVER_EXIT:
+                        ensurePointerCountIsOneForThisAction(event);
+                        if (!mHoverEntered) {
+                            problem("ACTION_HOVER_EXIT without prior ACTION_HOVER_ENTER");
+                        }
+                        mHoverEntered = false;
+                        break;
+                    case MotionEvent.ACTION_SCROLL:
+                        ensureHistorySizeIsZeroForThisAction(event);
+                        ensurePointerCountIsOneForThisAction(event);
+                        break;
+                    default:
+                        problem("Invalid action for generic pointer event.");
+                        break;
+                }
+            } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+                switch (action) {
+                    case MotionEvent.ACTION_MOVE:
+                        ensurePointerCountIsOneForThisAction(event);
+                        break;
+                    default:
+                        problem("Invalid action for generic joystick event.");
+                        break;
+                }
+            }
+        } finally {
+            finishEvent(false);
+        }
+    }
+
+    private void ensureMetaStateIsNormalized(int metaState) {
+        final int normalizedMetaState = KeyEvent.normalizeMetaState(metaState);
+        if (normalizedMetaState != metaState) {
+            problem(String.format("Metastate not normalized.  Was 0x%08x but expected 0x%08x.",
+                    metaState, normalizedMetaState));
+        }
+    }
+
+    private void ensurePointerCountIsOneForThisAction(MotionEvent event) {
+        final int pointerCount = event.getPointerCount();
+        if (pointerCount != 1) {
+            problem("Pointer count is " + pointerCount + " but it should always be 1 for "
+                    + MotionEvent.actionToString(event.getAction()));
+        }
+    }
+
+    private void ensureHistorySizeIsZeroForThisAction(MotionEvent event) {
+        final int historySize = event.getHistorySize();
+        if (historySize != 0) {
+            problem("History size is " + historySize + " but it should always be 0 for "
+                    + MotionEvent.actionToString(event.getAction()));
+        }
+    }
+
+    private boolean startEvent(InputEvent event, int nestingLevel, String eventType) {
+        // Ignore the event if it is already tainted.
+        if (event.isTainted()) {
+            return false;
+        }
+
+        // Ignore the event if we already checked it at a higher nesting level.
+        if (event == mLastEvent && nestingLevel < mLastNestingLevel) {
+            return false;
+        }
+
+        if (nestingLevel > 0) {
+            mLastEvent = event;
+            mLastNestingLevel = nestingLevel;
+        } else {
+            mLastEvent = null;
+            mLastNestingLevel = 0;
+        }
+
+        mCurrentEvent = event;
+        mCurrentEventType = eventType;
+        return true;
+    }
+
+    private void finishEvent(boolean tainted) {
+        if (mViolationMessage != null && mViolationMessage.length() != 0) {
+            mViolationMessage.append("\n  in ").append(mCaller);
+            mViolationMessage.append("\n  ").append(mCurrentEvent);
+
+            if (RECENT_EVENTS_TO_LOG != 0 && mRecentEvents != null) {
+                mViolationMessage.append("\n  -- recent events --");
+                for (int i = 0; i < RECENT_EVENTS_TO_LOG; i++) {
+                    final int index = (mMostRecentEventIndex + RECENT_EVENTS_TO_LOG - i)
+                            % RECENT_EVENTS_TO_LOG;
+                    final InputEvent event = mRecentEvents[index];
+                    if (event == null) {
+                        break;
+                    }
+                    mViolationMessage.append("\n  ").append(i + 1).append(": ").append(event);
+                }
+            }
+
+            Log.d(TAG, mViolationMessage.toString());
+            mViolationMessage.setLength(0);
+            tainted = true;
+        }
+
+        if (tainted) {
+            // Taint the event so that we do not generate additional violations from it
+            // further downstream.
+            mCurrentEvent.setTainted(true);
+        }
+
+        if (RECENT_EVENTS_TO_LOG != 0) {
+            if (mRecentEvents == null) {
+                mRecentEvents = new InputEvent[RECENT_EVENTS_TO_LOG];
+            }
+            final int index = (mMostRecentEventIndex + 1) % RECENT_EVENTS_TO_LOG;
+            mMostRecentEventIndex = index;
+            if (mRecentEvents[index] != null) {
+                mRecentEvents[index].recycle();
+            }
+            mRecentEvents[index] = mCurrentEvent.copy();
+        }
+
+        mCurrentEvent = null;
+        mCurrentEventType = null;
+    }
+
+    private void problem(String message) {
+        if (mViolationMessage == null) {
+            mViolationMessage = new StringBuilder();
+        }
+        if (mViolationMessage.length() == 0) {
+            mViolationMessage.append(mCurrentEventType).append(": ");
+        } else {
+            mViolationMessage.append("\n  ");
+        }
+        mViolationMessage.append(message);
+    }
+
+    private KeyState findKeyState(int deviceId, int source, int keyCode, boolean remove) {
+        KeyState last = null;
+        KeyState state = mKeyStateList;
+        while (state != null) {
+            if (state.deviceId == deviceId && state.source == source
+                    && state.keyCode == keyCode) {
+                if (remove) {
+                    if (last != null) {
+                        last.next = state.next;
+                    } else {
+                        mKeyStateList = state.next;
+                    }
+                    state.next = null;
+                }
+                return state;
+            }
+            last = state;
+            state = state.next;
+        }
+        return null;
+    }
+
+    private void addKeyState(int deviceId, int source, int keyCode) {
+        KeyState state = KeyState.obtain(deviceId, source, keyCode);
+        state.next = mKeyStateList;
+        mKeyStateList = state;
+    }
+
+    private static final class KeyState {
+        private static Object mRecycledListLock = new Object();
+        private static KeyState mRecycledList;
+
+        public KeyState next;
+        public int deviceId;
+        public int source;
+        public int keyCode;
+
+        private KeyState() {
+        }
+
+        public static KeyState obtain(int deviceId, int source, int keyCode) {
+            KeyState state;
+            synchronized (mRecycledListLock) {
+                state = mRecycledList;
+                if (state != null) {
+                    mRecycledList = state.next;
+                } else {
+                    state = new KeyState();
+                }
+            }
+            state.deviceId = deviceId;
+            state.source = source;
+            state.keyCode = keyCode;
+            return state;
+        }
+
+        public void recycle() {
+            synchronized (mRecycledListLock) {
+                next = mRecycledList;
+                mRecycledList = next;
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 8070c6a..4320160 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1170,7 +1170,18 @@
      * @hide
      */
     public static final int FLAG_START_TRACKING = 0x40000000;
-    
+
+    /**
+     * Private flag that indicates when the system has detected that this key event
+     * may be inconsistent with respect to the sequence of previously delivered key events,
+     * such as when a key up event is sent but the key was not down.
+     *
+     * @hide
+     * @see #isTainted
+     * @see #setTainted
+     */
+    public static final int FLAG_TAINTED = 0x80000000;
+
     /**
      * Returns the maximum keycode.
      */
@@ -1535,6 +1546,33 @@
     }
 
     /**
+     * Obtains a (potentially recycled) copy of another key event.
+     *
+     * @hide
+     */
+    public static KeyEvent obtain(KeyEvent other) {
+        KeyEvent ev = obtain();
+        ev.mDownTime = other.mDownTime;
+        ev.mEventTime = other.mEventTime;
+        ev.mAction = other.mAction;
+        ev.mKeyCode = other.mKeyCode;
+        ev.mRepeatCount = other.mRepeatCount;
+        ev.mMetaState = other.mMetaState;
+        ev.mDeviceId = other.mDeviceId;
+        ev.mScanCode = other.mScanCode;
+        ev.mFlags = other.mFlags;
+        ev.mSource = other.mSource;
+        ev.mCharacters = other.mCharacters;
+        return ev;
+    }
+
+    /** @hide */
+    @Override
+    public KeyEvent copy() {
+        return obtain(this);
+    }
+
+    /**
      * Recycles a key event.
      * Key events should only be recycled if they are owned by the system since user
      * code expects them to be essentially immutable, "tracking" notwithstanding.
@@ -1635,7 +1673,19 @@
         event.mFlags = flags;
         return event;
     }
-    
+
+    /** @hide */
+    @Override
+    public final boolean isTainted() {
+        return (mFlags & FLAG_TAINTED) != 0;
+    }
+
+    /** @hide */
+    @Override
+    public final void setTainted(boolean tainted) {
+        mFlags = tainted ? mFlags | FLAG_TAINTED : mFlags & ~FLAG_TAINTED;
+    }
+
     /**
      * Don't use in new code, instead explicitly check
      * {@link #getAction()}.
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 3c34479..7611b08 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -308,6 +308,17 @@
     public static final int FLAG_WINDOW_IS_OBSCURED = 0x1;
 
     /**
+     * Private flag that indicates when the system has detected that this motion event
+     * may be inconsistent with respect to the sequence of previously delivered motion events,
+     * such as when a pointer move event is sent but the pointer is not down.
+     *
+     * @hide
+     * @see #isTainted
+     * @see #setTainted
+     */
+    public static final int FLAG_TAINTED = 0x80000000;
+
+    /**
      * Flag indicating the motion event intersected the top edge of the screen.
      */
     public static final int EDGE_TOP = 0x00000001;
@@ -1054,6 +1065,7 @@
     private static native void nativeSetAction(int nativePtr, int action);
     private static native boolean nativeIsTouchEvent(int nativePtr);
     private static native int nativeGetFlags(int nativePtr);
+    private static native void nativeSetFlags(int nativePtr, int flags);
     private static native int nativeGetEdgeFlags(int nativePtr);
     private static native void nativeSetEdgeFlags(int nativePtr, int action);
     private static native int nativeGetMetaState(int nativePtr);
@@ -1290,6 +1302,12 @@
         return ev;
     }
 
+    /** @hide */
+    @Override
+    public MotionEvent copy() {
+        return obtain(this);
+    }
+
     /**
      * Recycle the MotionEvent, to be re-used by a later caller.  After calling
      * this function you must not ever touch the event again.
@@ -1403,6 +1421,20 @@
         return nativeGetFlags(mNativePtr);
     }
 
+    /** @hide */
+    @Override
+    public final boolean isTainted() {
+        final int flags = getFlags();
+        return (flags & FLAG_TAINTED) != 0;
+    }
+
+    /** @hide */
+    @Override
+    public final void setTainted(boolean tainted) {
+        final int flags = getFlags();
+        nativeSetFlags(mNativePtr, tainted ? flags | FLAG_TAINTED : flags & ~FLAG_TAINTED);
+    }
+
     /**
      * Returns the time (in ms) when the user originally pressed down to start
      * a stream of position events.
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index d638e70..456857a 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -163,6 +163,13 @@
     private int mActiveId1;
     private boolean mActive0MostRecent;
 
+    /**
+     * Consistency verifier for debugging purposes.
+     */
+    private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
+            InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+                    new InputEventConsistencyVerifier(this, 0) : null;
+
     public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
         ViewConfiguration config = ViewConfiguration.get(context);
         mContext = context;
@@ -171,6 +178,10 @@
     }
 
     public boolean onTouchEvent(MotionEvent event) {
+        if (mInputEventConsistencyVerifier != null) {
+            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
+        }
+
         final int action = event.getActionMasked();
         boolean handled = true;
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0ef56cc..e329e97d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2384,6 +2384,14 @@
     Rect mLocalDirtyRect;
 
     /**
+     * Consistency verifier for debugging purposes.
+     * @hide
+     */
+    protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
+            InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+                    new InputEventConsistencyVerifier(this, 0) : null;
+
+    /**
      * Simple constructor to use when creating a view from code.
      *
      * @param context The Context the view is running in, through which it can
@@ -4590,13 +4598,16 @@
      * @return True if the event was handled, false otherwise.
      */
     public boolean dispatchKeyEvent(KeyEvent event) {
-        // If any attached key listener a first crack at the event.
+        if (mInputEventConsistencyVerifier != null) {
+            mInputEventConsistencyVerifier.onKeyEvent(event, 0);
+        }
 
         //noinspection SimplifiableIfStatement,deprecation
         if (android.util.Config.LOGV) {
             captureViewInfo("captureViewKeyEvent", this);
         }
 
+        // Give any attached key listener a first crack at the event.
         //noinspection SimplifiableIfStatement
         if (mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                 && mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
@@ -4625,6 +4636,10 @@
      * @return True if the event was handled by the view, false otherwise.
      */
     public boolean dispatchTouchEvent(MotionEvent event) {
+        if (mInputEventConsistencyVerifier != null) {
+            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
+        }
+
         if (!onFilterTouchEventForSecurity(event)) {
             return false;
         }
@@ -4662,6 +4677,10 @@
      * @return True if the event was handled by the view, false otherwise.
      */
     public boolean dispatchTrackballEvent(MotionEvent event) {
+        if (mInputEventConsistencyVerifier != null) {
+            mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
+        }
+
         //Log.i("view", "view=" + this + ", " + event.toString());
         return onTrackballEvent(event);
     }
@@ -4679,6 +4698,10 @@
      * @return True if the event was handled by the view, false otherwise.
      */
     public boolean dispatchGenericMotionEvent(MotionEvent event) {
+        if (mInputEventConsistencyVerifier != null) {
+            mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
+        }
+
         final int source = event.getSource();
         if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
             final int action = event.getAction();
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f7f2685..0d4f3d0 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1126,6 +1126,10 @@
      */
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
+        if (mInputEventConsistencyVerifier != null) {
+            mInputEventConsistencyVerifier.onKeyEvent(event, 1);
+        }
+
         if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
             return super.dispatchKeyEvent(event);
         } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
@@ -1152,6 +1156,10 @@
      */
     @Override
     public boolean dispatchTrackballEvent(MotionEvent event) {
+        if (mInputEventConsistencyVerifier != null) {
+            mInputEventConsistencyVerifier.onTrackballEvent(event, 1);
+        }
+
         if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
             return super.dispatchTrackballEvent(event);
         } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
@@ -1332,6 +1340,10 @@
      */
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (mInputEventConsistencyVerifier != null) {
+            mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
+        }
+
         if (!onFilterTouchEventForSecurity(ev)) {
             return false;
         }
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 3c386b4..2f9d501 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -250,6 +250,13 @@
 
     private final int mDensity;
 
+    /**
+     * Consistency verifier for debugging purposes.
+     */
+    protected final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
+            InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+                    new InputEventConsistencyVerifier(this, 0) : null;
+
     public static IWindowSession getWindowSession(Looper mainLooper) {
         synchronized (mStaticInit) {
             if (!mInitialized) {
@@ -2316,6 +2323,14 @@
     }
 
     private void deliverPointerEvent(MotionEvent event, boolean sendDone) {
+        if (mInputEventConsistencyVerifier != null) {
+            if (event.isTouchEvent()) {
+                mInputEventConsistencyVerifier.onTouchEvent(event, 0);
+            } else {
+                mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
+            }
+        }
+
         // If there is no view, then the event will not be handled.
         if (mView == null || !mAdded) {
             finishMotionEvent(event, sendDone, false);
@@ -2422,6 +2437,10 @@
     private void deliverTrackballEvent(MotionEvent event, boolean sendDone) {
         if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
 
+        if (mInputEventConsistencyVerifier != null) {
+            mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
+        }
+
         // If there is no view, then the event will not be handled.
         if (mView == null || !mAdded) {
             finishMotionEvent(event, sendDone, false);
@@ -2550,6 +2569,10 @@
     }
 
     private void deliverGenericMotionEvent(MotionEvent event, boolean sendDone) {
+        if (mInputEventConsistencyVerifier != null) {
+            mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
+        }
+
         final int source = event.getSource();
         final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
 
@@ -2785,6 +2808,10 @@
     }
 
     private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
+        if (mInputEventConsistencyVerifier != null) {
+            mInputEventConsistencyVerifier.onKeyEvent(event, 0);
+        }
+
         // If there is no view, then the event will not be handled.
         if (mView == null || !mAdded) {
             finishKeyEvent(event, sendDone, false);
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index e4eb692..25c0a13 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -165,6 +165,7 @@
 extern int register_android_backup_BackupDataOutput(JNIEnv *env);
 extern int register_android_backup_FileBackupHelperBase(JNIEnv *env);
 extern int register_android_backup_BackupHelperDispatcher(JNIEnv *env);
+extern int register_android_app_ActivityThread(JNIEnv *env);
 extern int register_android_app_NativeActivity(JNIEnv *env);
 extern int register_android_view_InputChannel(JNIEnv* env);
 extern int register_android_view_InputQueue(JNIEnv* env);
@@ -1298,6 +1299,7 @@
     REG_JNI(register_android_backup_FileBackupHelperBase),
     REG_JNI(register_android_backup_BackupHelperDispatcher),
     
+    REG_JNI(register_android_app_ActivityThread),
     REG_JNI(register_android_app_NativeActivity),
     REG_JNI(register_android_view_InputChannel),
     REG_JNI(register_android_view_InputQueue),
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index a4931ac..454c3e3 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -592,6 +592,19 @@
 }
 
 // ----------------------------------------------------------------------------
+// Logging
+// ----------------------------------------------------------------------------
+
+jfieldID gFileDescriptorField;
+
+static void
+android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor)
+{
+    int fd = env->GetIntField(javaFileDescriptor, gFileDescriptorField);
+    android::uirenderer::DisplayList::outputLogBuffer(fd);
+}
+
+// ----------------------------------------------------------------------------
 // JNI Glue
 // ----------------------------------------------------------------------------
 
@@ -690,6 +703,12 @@
 #endif
 };
 
+static JNINativeMethod gActivityThreadMethods[] = {
+    { "dumpGraphicsInfo",        "(Ljava/io/FileDescriptor;)V",
+                                               (void*) android_app_ActivityThread_dumpGraphics }
+};
+
+
 #ifdef USE_OPENGL_RENDERER
     #define FIND_CLASS(var, className) \
             var = env->FindClass(className); \
@@ -711,4 +730,19 @@
     return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
+const char* const kActivityThreadPathName = "android/app/ActivityThread";
+
+int register_android_app_ActivityThread(JNIEnv* env)
+{
+    jclass gFileDescriptorClass = env->FindClass("java/io/FileDescriptor");
+    LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
+    gFileDescriptorField = env->GetFieldID(gFileDescriptorClass, "descriptor", "I");
+    LOG_FATAL_IF(gFileDescriptorField == NULL,
+                 "Unable to find descriptor field in java.io.FileDescriptor");
+
+    return AndroidRuntime::registerNativeMethods(
+            env, kActivityThreadPathName,
+            gActivityThreadMethods, NELEM(gActivityThreadMethods));
+}
+
 };
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 4ce471e..2ede7ec 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -430,6 +430,12 @@
     return event->getFlags();
 }
 
+static void android_view_MotionEvent_nativeSetFlags(JNIEnv* env, jclass clazz,
+        jint nativePtr, jint flags) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->setFlags(flags);
+}
+
 static jint android_view_MotionEvent_nativeGetEdgeFlags(JNIEnv* env, jclass clazz,
         jint nativePtr) {
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -656,6 +662,9 @@
     { "nativeGetFlags",
             "(I)I",
             (void*)android_view_MotionEvent_nativeGetFlags },
+    { "nativeSetFlags",
+            "(II)V",
+            (void*)android_view_MotionEvent_nativeSetFlags },
     { "nativeGetEdgeFlags",
             "(I)I",
             (void*)android_view_MotionEvent_nativeGetEdgeFlags },
diff --git a/docs/html/guide/market/billing/billing_integrate.jd b/docs/html/guide/market/billing/billing_integrate.jd
index 56e471e..7532337 100755
--- a/docs/html/guide/market/billing/billing_integrate.jd
+++ b/docs/html/guide/market/billing/billing_integrate.jd
@@ -121,7 +121,6 @@
 
 <p>The in-app billing sample application is available as a downloadable component of the Android SDK. To download the sample application component, launch the Android SDK and AVD Manager and then select the "Google Market Billing package" component (see figure 1), and click <strong>Install Selected</strong> to begin the download.</p>
 
-<div style="margin-bottom:2em;">
 
 <img src="{@docRoot}images/billing_package.png" height="325" id="figure1" />
 <p class="img-caption">
@@ -664,8 +663,6 @@
 
 <p class="caution"><strong>Important</strong>: To keep your public key safe from malicious users and hackers, do not embed your public key as an entire literal string. Instead, construct the string at runtime from pieces or use bit manipulation (for example, XOR with some other string) to hide the actual key. The key itself is not secret information, but you do not want to make it easy for a hacker or malicious user to replace the public key with another key.</p>
 
-<div style="margin-bottom:2em;">
-
 <img src="{@docRoot}images/billing_public_key.png" height="510" id="figure2" />
 <p class="img-caption">
   <strong>Figure 2.</strong> The Licensing and In-app Billing panel of your account's Edit Profile page lets you see your public key.
diff --git a/graphics/java/android/renderscript/BaseObj.java b/graphics/java/android/renderscript/BaseObj.java
index 8ce1d9a..a17e735 100644
--- a/graphics/java/android/renderscript/BaseObj.java
+++ b/graphics/java/android/renderscript/BaseObj.java
@@ -24,7 +24,7 @@
  * disconecting the object from the native allocation for early cleanup.
  *
  **/
-class BaseObj {
+public class BaseObj {
     BaseObj(int id, RenderScript rs) {
         rs.validate();
         mRS = rs;
diff --git a/include/ui/Input.h b/include/ui/Input.h
index b22986d..0dc29c8 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -37,10 +37,16 @@
  * Additional private constants not defined in ndk/ui/input.h.
  */
 enum {
-    /*
-     * Private control to determine when an app is tracking a key sequence.
-     */
-    AKEY_EVENT_FLAG_START_TRACKING = 0x40000000
+    /* Private control to determine when an app is tracking a key sequence. */
+    AKEY_EVENT_FLAG_START_TRACKING = 0x40000000,
+
+    /* Key event is inconsistent with previously sent key events. */
+    AKEY_EVENT_FLAG_TAINTED = 0x80000000,
+};
+
+enum {
+    /* Motion event is inconsistent with previously sent motion events. */
+    AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
 };
 
 enum {
@@ -328,6 +334,8 @@
 
     inline int32_t getFlags() const { return mFlags; }
 
+    inline void setFlags(int32_t flags) { mFlags = flags; }
+
     inline int32_t getEdgeFlags() const { return mEdgeFlags; }
 
     inline void setEdgeFlags(int32_t edgeFlags) { mEdgeFlags = edgeFlags; }
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index b465fee..a98e4cd 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -9,6 +9,7 @@
 		FontRenderer.cpp \
 		GammaFontRenderer.cpp \
 		Caches.cpp \
+		DisplayListLogBuffer.cpp \
 		DisplayListRenderer.cpp \
 		FboCache.cpp \
 		GradientCache.cpp \
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 4f5edd5..cd48429 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "OpenGLRenderer"
 
 #include <utils/Log.h>
+#include <utils/String8.h>
 
 #include "Caches.h"
 #include "Properties.h"
@@ -69,30 +70,43 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void Caches::dumpMemoryUsage() {
-    LOGD("Current memory usage / total memory usage (bytes):");
-    LOGD("  TextureCache         %8d / %8d", textureCache.getSize(), textureCache.getMaxSize());
-    LOGD("  LayerCache           %8d / %8d", layerCache.getSize(), layerCache.getMaxSize());
-    LOGD("  GradientCache        %8d / %8d", gradientCache.getSize(), gradientCache.getMaxSize());
-    LOGD("  PathCache            %8d / %8d", pathCache.getSize(), pathCache.getMaxSize());
-    LOGD("  CircleShapeCache     %8d / %8d",
+    String8 stringLog;
+    dumpMemoryUsage(stringLog);
+    LOGD("%s", stringLog.string());
+    delete stringLog;
+}
+
+void Caches::dumpMemoryUsage(String8 &log) {
+    log.appendFormat("Current memory usage / total memory usage (bytes):\n");
+    log.appendFormat("  TextureCache         %8d / %8d\n",
+            textureCache.getSize(), textureCache.getMaxSize());
+    log.appendFormat("  LayerCache           %8d / %8d\n",
+            layerCache.getSize(), layerCache.getMaxSize());
+    log.appendFormat("  GradientCache        %8d / %8d\n",
+            gradientCache.getSize(), gradientCache.getMaxSize());
+    log.appendFormat("  PathCache            %8d / %8d\n",
+            pathCache.getSize(), pathCache.getMaxSize());
+    log.appendFormat("  CircleShapeCache     %8d / %8d\n",
             circleShapeCache.getSize(), circleShapeCache.getMaxSize());
-    LOGD("  OvalShapeCache       %8d / %8d",
+    log.appendFormat("  OvalShapeCache       %8d / %8d\n",
             ovalShapeCache.getSize(), ovalShapeCache.getMaxSize());
-    LOGD("  RoundRectShapeCache  %8d / %8d",
+    log.appendFormat("  RoundRectShapeCache  %8d / %8d\n",
             roundRectShapeCache.getSize(), roundRectShapeCache.getMaxSize());
-    LOGD("  RectShapeCache       %8d / %8d",
+    log.appendFormat("  RectShapeCache       %8d / %8d\n",
             rectShapeCache.getSize(), rectShapeCache.getMaxSize());
-    LOGD("  ArcShapeCache        %8d / %8d",
+    log.appendFormat("  ArcShapeCache        %8d / %8d\n",
             arcShapeCache.getSize(), arcShapeCache.getMaxSize());
-    LOGD("  TextDropShadowCache  %8d / %8d", dropShadowCache.getSize(),
+    log.appendFormat("  TextDropShadowCache  %8d / %8d\n", dropShadowCache.getSize(),
             dropShadowCache.getMaxSize());
     for (uint32_t i = 0; i < fontRenderer.getFontRendererCount(); i++) {
         const uint32_t size = fontRenderer.getFontRendererSize(i);
-        LOGD("  FontRenderer %d       %8d / %8d", i, size, size);
+        log.appendFormat("  FontRenderer %d       %8d / %8d\n", i, size, size);
     }
-    LOGD("Other:");
-    LOGD("  FboCache             %8d / %8d", fboCache.getSize(), fboCache.getMaxSize());
-    LOGD("  PatchCache           %8d / %8d", patchCache.getSize(), patchCache.getMaxSize());
+    log.appendFormat("Other:");
+    log.appendFormat("  FboCache             %8d / %8d\n",
+            fboCache.getSize(), fboCache.getMaxSize());
+    log.appendFormat("  PatchCache           %8d / %8d\n",
+            patchCache.getSize(), patchCache.getMaxSize());
 
     uint32_t total = 0;
     total += textureCache.getSize();
@@ -109,9 +123,8 @@
         total += fontRenderer.getFontRendererSize(i);
     }
 
-    LOGD("Total memory usage:");
-    LOGD("  %d bytes, %.2f MB", total, total / 1024.0f / 1024.0f);
-    LOGD("\n");
+    log.appendFormat("Total memory usage:\n");
+    log.appendFormat("  %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 0a9335f..7d02cf8 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -140,6 +140,7 @@
      * Displays the memory usage of each cache and the total sum.
      */
     void dumpMemoryUsage();
+    void dumpMemoryUsage(String8& log);
 
     bool blend;
     GLenum lastSrcMode;
diff --git a/libs/hwui/DisplayListLogBuffer.cpp b/libs/hwui/DisplayListLogBuffer.cpp
new file mode 100644
index 0000000..f204644
--- /dev/null
+++ b/libs/hwui/DisplayListLogBuffer.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+#include "DisplayListLogBuffer.h"
+
+// BUFFER_SIZE size must be one more than a multiple of COMMAND_SIZE to ensure
+// that mStart always points at the next command, not just the next item
+#define COMMAND_SIZE 2
+#define NUM_COMMANDS 50
+#define BUFFER_SIZE ((NUM_COMMANDS * COMMAND_SIZE) + 1)
+
+/**
+ * DisplayListLogBuffer is a utility class which logs the most recent display
+ * list operations in a circular buffer. The log is process-wide, because we
+ * only care about the most recent operations, not the operations on a per-window
+ * basis for a given activity. The purpose of the log is to provide more debugging
+ * information in a bug report, by telling us not just where a process hung (which
+ * generally is just reported as a stack trace at the Java level) or crashed, but
+ * also what happened immediately before that hang or crash. This may help track down
+ * problems in the native rendering code or driver interaction related to the display
+ * list operations that led up to the hang or crash.
+ *
+ * The log is implemented as a circular buffer for both space and performance
+ * reasons - we only care about the last several operations to give us context
+ * leading up to the problem, and we don't want to constantly copy data around or do
+ * additional mallocs to keep the most recent operations logged. Only numbers are
+ * logged to make the operation fast. If and when the log is output, we process this
+ * data into meaningful strings.
+ *
+ * There is an assumption about the format of the command (currently 2 ints: the
+ * opcode and the nesting level). If the type of information logged changes (for example,
+ * we may want to save a timestamp), then the size of the buffer and the way the
+ * information is recorded in writeCommand() should change to suit.
+ */
+
+namespace android {
+
+#ifdef USE_OPENGL_RENDERER
+using namespace uirenderer;
+ANDROID_SINGLETON_STATIC_INSTANCE(DisplayListLogBuffer);
+#endif
+
+namespace uirenderer {
+
+
+DisplayListLogBuffer::DisplayListLogBuffer() {
+    mBufferFirst = (int*) malloc(BUFFER_SIZE * sizeof(int));
+    mStart = mBufferFirst;
+    mBufferLast = mBufferFirst + BUFFER_SIZE - 1;
+    mEnd = mStart;
+}
+
+DisplayListLogBuffer::~DisplayListLogBuffer() {
+    free(mBufferFirst);
+}
+
+/**
+ * Called from DisplayListRenderer to output the current buffer into the
+ * specified FILE. This only happens in a dumpsys/bugreport operation.
+ */
+void DisplayListLogBuffer::outputCommands(FILE *file, const char* opNames[])
+{
+    int *tmpBufferPtr = mStart;
+    while (true) {
+        if (tmpBufferPtr == mEnd) {
+            break;
+        }
+        int level = *tmpBufferPtr++;
+        if (tmpBufferPtr > mBufferLast) {
+            tmpBufferPtr = mBufferFirst;
+        }
+        int op = *tmpBufferPtr++;
+        if (tmpBufferPtr > mBufferLast) {
+            tmpBufferPtr = mBufferFirst;
+        }
+        uint32_t count = (level + 1) * 2;
+        char indent[count + 1];
+        for (uint32_t i = 0; i < count; i++) {
+            indent[i] = ' ';
+        }
+        indent[count] = '\0';
+        fprintf(file, "%s%s\n", indent, opNames[op]);
+    }
+}
+
+void DisplayListLogBuffer::writeCommand(int level, int op) {
+    writeInt(level);
+    writeInt(op);
+}
+
+/**
+ * Store the given value in the buffer and increment/wrap the mEnd
+ * and mStart values as appropriate.
+ */
+void DisplayListLogBuffer::writeInt(int value) {
+    *((int*)mEnd) = value;
+    if (mEnd == mBufferLast) {
+        mEnd = mBufferFirst;
+    } else {
+        mEnd++;
+    }
+    if (mEnd == mStart) {
+        mStart++;
+        if (mStart > mBufferLast) {
+            mStart = mBufferFirst;
+        }
+    }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/DisplayListLogBuffer.h b/libs/hwui/DisplayListLogBuffer.h
new file mode 100644
index 0000000..bf16f29
--- /dev/null
+++ b/libs/hwui/DisplayListLogBuffer.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H
+#define ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H
+
+#include <utils/Singleton.h>
+#include <stdio.h>
+
+namespace android {
+namespace uirenderer {
+
+class DisplayListLogBuffer: public Singleton<DisplayListLogBuffer> {
+    DisplayListLogBuffer();
+    ~DisplayListLogBuffer();
+
+    friend class Singleton<DisplayListLogBuffer>;
+
+public:
+    void writeCommand(int level, int op);
+    void writeInt(int value);
+    void outputCommands(FILE *file, const char* opNames[]);
+
+    bool isEmpty() {
+        return (mStart == mEnd);
+    }
+
+private:
+    int *mBufferFirst; // where the memory starts
+    int* mStart;       // where the current command stream starts
+    int* mEnd;         // where the current commands end
+    int* mBufferLast;  // where the buffer memory ends
+
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index c7459d1..34dda9a 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -16,11 +16,16 @@
 
 #define LOG_TAG "OpenGLRenderer"
 
+
+#include "DisplayListLogBuffer.h"
 #include "DisplayListRenderer.h"
+#include <utils/String8.h>
+#include "Caches.h"
 
 namespace android {
 namespace uirenderer {
 
+
 ///////////////////////////////////////////////////////////////////////////////
 // Display list
 ///////////////////////////////////////////////////////////////////////////////
@@ -64,6 +69,20 @@
     "DrawGLFunction"
 };
 
+void DisplayList::outputLogBuffer(int fd) {
+    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+    if (logBuffer.isEmpty()) {
+        return;
+    }
+    String8 cachesLog;
+    Caches::getInstance().dumpMemoryUsage(cachesLog);
+    FILE *file = fdopen(fd, "a");
+    fprintf(file, "\nCaches:\n%s", cachesLog.string());
+    fprintf(file, "\nRecent DisplayList operations\n");
+    logBuffer.outputCommands(file, OP_NAMES);
+    fflush(file);
+}
+
 DisplayList::DisplayList(const DisplayListRenderer& recorder) {
     initFromDisplayListRenderer(recorder);
 }
@@ -173,9 +192,11 @@
     DISPLAY_LIST_LOGD("%sStart display list (%p)", (char*) indent + 2, this);
 #endif
 
+    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
     int saveCount = renderer.getSaveCount() - 1;
     while (!mReader.eof()) {
         int op = mReader.readInt();
+        logBuffer.writeCommand(level, op);
 
         switch (op) {
             case DrawGLFunction: {
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index da57e4a..b782103 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -26,6 +26,7 @@
 #include <SkTDArray.h>
 #include <SkTSearch.h>
 
+#include "DisplayListLogBuffer.h"
 #include "OpenGLRenderer.h"
 #include "utils/Functor.h"
 
@@ -106,6 +107,8 @@
 
     bool replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level = 0);
 
+    static void outputLogBuffer(int fd);
+
 private:
     void init();
 
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index af37c9e..47bb8c9 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -61,7 +61,9 @@
     }
 
     /**
-     * Closes all resources related to the MtpDevice object
+     * Closes all resources related to the MtpDevice object.
+     * After this is called, the object can not be used until {@link #open} is called again
+     * with a new {@link android.hardware.usb.UsbDeviceConnection}.
      */
     public void close() {
         native_close();
@@ -78,6 +80,8 @@
 
     /**
      * Returns the name of the USB device
+     * This returns the same value as {@link android.hardware.usb.UsbDevice#getDeviceName}
+     * for the device's {@link android.hardware.usb.UsbDevice}
      *
      * @return the device name
      */
@@ -86,7 +90,9 @@
     }
 
     /**
-     * Returns the ID of the USB device
+     * Returns the USB ID of the USB device.
+     * This returns the same value as {@link android.hardware.usb.UsbDevice#getDeviceId}
+     * for the device's {@link android.hardware.usb.UsbDevice}
      *
      * @return the device ID
      */
@@ -100,7 +106,7 @@
     }
 
     /**
-     * Returns the {@link android.mtp.MtpDeviceInfo} for this device
+     * Returns the {@link MtpDeviceInfo} for this device
      *
      * @return the device info
      */
@@ -110,8 +116,9 @@
 
     /**
      * Returns the list of IDs for all storage units on this device
+     * Information about each storage unit can be accessed via {@link #getStorageInfo}.
      *
-     * @return the storage IDs
+     * @return the list of storage IDs
      */
     public int[] getStorageIds() {
         return native_get_storage_ids();
@@ -120,6 +127,7 @@
     /**
      * Returns the list of object handles for all objects on the given storage unit,
      * with the given format and parent.
+     * Information about each object can be accessed via {@link #getObjectInfo}.
      *
      * @param storageId the storage unit to query
      * @param format the format of the object to return, or zero for all formats
@@ -132,10 +140,12 @@
 
     /**
      * Returns the data for an object as a byte array.
+     * This call may block for an arbitrary amount of time depending on the size
+     * of the data and speed of the devices.
      *
      * @param objectHandle handle of the object to read
      * @param objectSize the size of the object (this should match
-     *      {@link android.mtp.MtpObjectInfo#getCompressedSize}
+     *      {@link MtpObjectInfo#getCompressedSize}
      * @return the object's data, or null if reading fails
      */
     public byte[] getObject(int objectHandle, int objectSize) {
@@ -144,6 +154,10 @@
 
     /**
      * Returns the thumbnail data for an object as a byte array.
+     * The size and format of the thumbnail data can be determined via
+     * {@link MtpObjectInfo#getThumbCompressedSize} and
+     * {@link MtpObjectInfo#getThumbFormat}.
+     * For typical devices the format is JPEG.
      *
      * @param objectHandle handle of the object to read
      * @return the object's thumbnail, or null if reading fails
@@ -153,7 +167,7 @@
     }
 
     /**
-     * Retrieves the {@link android.mtp.MtpStorageInfo} for a storage unit.
+     * Retrieves the {@link MtpStorageInfo} for a storage unit.
      *
      * @param storageId the ID of the storage unit
      * @return the MtpStorageInfo
@@ -163,7 +177,7 @@
     }
 
     /**
-     * Retrieves the {@link android.mtp.MtpObjectInfo} for an object.
+     * Retrieves the {@link MtpObjectInfo} for an object.
      *
      * @param objectHandle the handle of the object
      * @return the MtpObjectInfo
@@ -173,7 +187,9 @@
     }
 
     /**
-     * Deletes an object on the device.
+     * Deletes an object on the device.  This call may block, since
+     * deleting a directory containing many files may take a long time
+     * on some devices.
      *
      * @param objectHandle handle of the object to delete
      * @return true if the deletion succeeds
@@ -204,6 +220,8 @@
 
     /**
      * Copies the data for an object to a file in external storage.
+     * This call may block for an arbitrary amount of time depending on the size
+     * of the data and speed of the devices.
      *
      * @param objectHandle handle of the object to read
      * @param destPath path to destination for the file transfer.
diff --git a/media/java/android/mtp/MtpStorageInfo.java b/media/java/android/mtp/MtpStorageInfo.java
index 09736a8..d1b86fc 100644
--- a/media/java/android/mtp/MtpStorageInfo.java
+++ b/media/java/android/mtp/MtpStorageInfo.java
@@ -34,7 +34,8 @@
     }
 
     /**
-     * Returns the storage ID for the storage unit
+     * Returns the storage ID for the storage unit.
+     * The storage ID uniquely identifies the storage unit on the MTP device.
      *
      * @return the storage ID
      */
@@ -61,7 +62,9 @@
     }
 
    /**
-     * Returns the description string for the storage unit
+     * Returns the description string for the storage unit.
+     * This is typically displayed to the user in the user interface on the
+     * MTP host.
      *
      * @return the storage unit description
      */
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 6d074e9..0f9cbec 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -317,8 +317,7 @@
         if (surfaceTexture != NULL) {
             sp<ISurfaceTexture> native_surfaceTexture(
                     getSurfaceTexture(env, surfaceTexture));
-            LOGV("%s: texture=%p (id=%d)", prefix,
-                 native_surfaceTexture.get(), native_surfaceTexture->getIdentity());
+            LOGV("%s: texture=%p", prefix, native_surfaceTexture.get());
             mp->setVideoSurfaceTexture(native_surfaceTexture);
         }
     }
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 6b0b1b9..f25d498 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -55,7 +55,6 @@
 #include <cutils/properties.h>
 
 #define USE_SURFACE_ALLOC 1
-#define FRAME_DROP_FREQ 0
 
 namespace android {
 
@@ -1440,7 +1439,6 @@
 
     if (mFlags & FIRST_FRAME) {
         mFlags &= ~FIRST_FRAME;
-        mSinceLastDropped = 0;
         mTimeSourceDeltaUs = ts->getRealTimeUs() - timeUs;
     }
 
@@ -1487,17 +1485,13 @@
 
         if (latenessUs > 40000) {
             // We're more than 40ms late.
-            LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
-            if ( mSinceLastDropped > FRAME_DROP_FREQ)
-            {
-                LOGV("we're late by %lld us (%.2f secs) dropping one after %d frames", latenessUs, latenessUs / 1E6, mSinceLastDropped);
-                mSinceLastDropped = 0;
-                mVideoBuffer->release();
-                mVideoBuffer = NULL;
+            LOGV("we're late by %lld us (%.2f secs), dropping frame",
+                 latenessUs, latenessUs / 1E6);
+            mVideoBuffer->release();
+            mVideoBuffer = NULL;
 
-                postVideoEvent_l();
-                return;
-            }
+            postVideoEvent_l();
+            return;
         }
 
         if (latenessUs < -10000) {
@@ -1515,7 +1509,6 @@
     }
 
     if (mVideoRenderer != NULL) {
-        mSinceLastDropped++;
         mVideoRenderer->render(mVideoBuffer);
     }
 
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index a9b7ae8..7fd7724 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -163,7 +163,6 @@
 
     uint32_t mFlags;
     uint32_t mExtractorFlags;
-    uint32_t mSinceLastDropped;
 
     int64_t mTimeSourceDeltaUs;
     int64_t mVideoTimeUs;
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index b3e29b9..64266b8 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -291,6 +291,7 @@
     Mutex::Autolock autoLock(mExtractor->mLock);
 
     mCluster = mExtractor->mSegment->GetFirst();
+    mBlockEntry = NULL;
     mBlockEntryIndex = 0;
 
     do {
@@ -302,11 +303,13 @@
     Mutex::Autolock autoLock(mExtractor->mLock);
 
     mCluster = mExtractor->mSegment->FindCluster(seekTimeUs * 1000ll);
+    mBlockEntry = NULL;
     mBlockEntryIndex = 0;
 
-    while (!eos() && block()->GetTrackNumber() != mTrackNum) {
+    do {
         advance_l();
     }
+    while (!eos() && block()->GetTrackNumber() != mTrackNum);
 
     while (!eos() && !mBlockEntry->GetBlock()->IsKey()) {
         advance_l();
diff --git a/native/android/native_window.cpp b/native/android/native_window.cpp
index ae1993d..8d42edb 100644
--- a/native/android/native_window.cpp
+++ b/native/android/native_window.cpp
@@ -68,8 +68,7 @@
 
 int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width,
         int32_t height, int32_t format) {
-    native_window_set_buffers_geometry(window, width, height, format);
-    return 0;
+    return native_window_set_buffers_geometry(window, width, height, format);
 }
 
 int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer,
diff --git a/native/include/android/native_window.h b/native/include/android/native_window.h
index f3d7550..337fa96 100644
--- a/native/include/android/native_window.h
+++ b/native/include/android/native_window.h
@@ -95,6 +95,9 @@
  *
  * For all of these parameters, if 0 is supplied then the window's base
  * value will come back in force.
+ *
+ * width and height must be either both zero or both non-zero.
+ *
  */
 int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, int32_t height, int32_t format);
 
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 12ac052..3997702 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -543,18 +543,8 @@
      */
     public NetworkInfo getActiveNetworkInfo() {
         enforceAccessPermission();
-        for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
-            if (mNetAttributes[type] == null || !mNetAttributes[type].isDefault()) {
-                continue;
-            }
-            NetworkStateTracker t = mNetTrackers[type];
-            NetworkInfo info = t.getNetworkInfo();
-            if (info.isConnected()) {
-                if (DBG && type != mActiveDefaultNetwork) {
-                    loge("connected default network is not mActiveDefaultNetwork!");
-                }
-                return info;
-            }
+        if (mActiveDefaultNetwork != -1) {
+            return mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
         }
         return null;
     }
@@ -1353,6 +1343,19 @@
                 handleApplyDefaultProxy(netType);
                 addDefaultRoute(mNetTrackers[netType]);
             } else {
+                // many radios add a default route even when we don't want one.
+                // remove the default route unless we need it for our active network
+                if (mActiveDefaultNetwork != -1) {
+                    LinkProperties defaultLinkProperties =
+                            mNetTrackers[mActiveDefaultNetwork].getLinkProperties();
+                    LinkProperties newLinkProperties =
+                            mNetTrackers[netType].getLinkProperties();
+                    String defaultIface = defaultLinkProperties.getInterfaceName();
+                    if (defaultIface != null &&
+                            !defaultIface.equals(newLinkProperties.getInterfaceName())) {
+                        removeDefaultRoute(mNetTrackers[netType]);
+                    }
+                }
                 addPrivateDnsRoutes(mNetTrackers[netType]);
             }
         } else {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 50fffd0..5f471fe 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1276,6 +1276,7 @@
             
             ServiceManager.addService("activity", m);
             ServiceManager.addService("meminfo", new MemBinder(m));
+            ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
             if (MONITOR_CPU_USAGE) {
                 ServiceManager.addService("cpuinfo", new CpuBinder(m));
             }
@@ -1429,6 +1430,46 @@
         }
     }
 
+    static class GraphicsBinder extends Binder {
+        ActivityManagerService mActivityManagerService;
+        GraphicsBinder(ActivityManagerService activityManagerService) {
+            mActivityManagerService = activityManagerService;
+        }
+
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            ActivityManagerService service = mActivityManagerService;
+            ArrayList<ProcessRecord> procs;
+            synchronized (mActivityManagerService) {
+                if (args != null && args.length > 0
+                        && args[0].charAt(0) != '-') {
+                    procs = new ArrayList<ProcessRecord>();
+                    int pid = -1;
+                    try {
+                        pid = Integer.parseInt(args[0]);
+                    } catch (NumberFormatException e) {
+
+                    }
+                    for (int i=service.mLruProcesses.size()-1; i>=0; i--) {
+                        ProcessRecord proc = service.mLruProcesses.get(i);
+                        if (proc.pid == pid) {
+                            procs.add(proc);
+                        } else if (proc.processName.equals(args[0])) {
+                            procs.add(proc);
+                        }
+                    }
+                    if (procs.size() <= 0) {
+                        pw.println("No process found for: " + args[0]);
+                        return;
+                    }
+                } else {
+                    procs = new ArrayList<ProcessRecord>(service.mLruProcesses);
+                }
+            }
+            dumpGraphicsHardwareUsage(fd, pw, procs);
+        }
+    }
+
     static class CpuBinder extends Binder {
         ActivityManagerService mActivityManagerService;
         CpuBinder(ActivityManagerService activityManagerService) {
@@ -8471,6 +8512,28 @@
         }
     }
 
+    static final void dumpGraphicsHardwareUsage(FileDescriptor fd,
+            PrintWriter pw, List list) {
+        String args[] = {"graphics"};
+        pw.println("-------------------------------------------------------------------------------");
+        pw.println("DUMP OF GRAPHICS ACCELERATION INFO:");
+        for (int i = list.size() - 1 ; i >= 0 ; i--) {
+            ProcessRecord r = (ProcessRecord)list.get(i);
+            if (r.thread != null) {
+                pw.println("\n** Graphics info for pid " + r.pid + " [" + r.processName + "] **");
+                pw.flush();
+                try {
+                    r.thread.asBinder().dump(fd, args);
+                } catch (RemoteException e) {
+                    pw.println("Got RemoteException!");
+                    pw.flush();
+                }
+            }
+        }
+        pw.println("\n");
+        pw.flush();
+    }
+
     static final void dumpApplicationMemoryUsage(FileDescriptor fd,
             PrintWriter pw, List list, String prefix, String[] args) {
         final boolean isCheckinRequest = scanArgs(args, "--checkin");
diff --git a/services/java/com/android/server/wm/InputFilter.java b/services/java/com/android/server/wm/InputFilter.java
index 78b87fe..7e1ab07 100644
--- a/services/java/com/android/server/wm/InputFilter.java
+++ b/services/java/com/android/server/wm/InputFilter.java
@@ -20,6 +20,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.view.InputEvent;
+import android.view.InputEventConsistencyVerifier;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.WindowManagerPolicy;
@@ -100,6 +101,16 @@
     private final H mH;
     private Host mHost;
 
+    // Consistency verifiers for debugging purposes.
+    private final InputEventConsistencyVerifier mInboundInputEventConsistencyVerifier =
+            InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+                    new InputEventConsistencyVerifier(this,
+                            InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT) : null;
+    private final InputEventConsistencyVerifier mOutboundInputEventConsistencyVerifier =
+            InputEventConsistencyVerifier.isInstrumentationEnabled() ?
+                    new InputEventConsistencyVerifier(this,
+                            InputEventConsistencyVerifier.FLAG_RAW_DEVICE_INPUT) : null;
+
     /**
      * Creates the input filter.
      *
@@ -152,6 +163,9 @@
             throw new IllegalStateException("Cannot send input event because the input filter " +
                     "is not installed.");
         }
+        if (mOutboundInputEventConsistencyVerifier != null) {
+            mOutboundInputEventConsistencyVerifier.onInputEvent(event, 0);
+        }
         mHost.sendInputEvent(event, policyFlags);
     }
 
@@ -201,6 +215,12 @@
             switch (msg.what) {
                 case MSG_INSTALL:
                     mHost = (Host)msg.obj;
+                    if (mInboundInputEventConsistencyVerifier != null) {
+                        mInboundInputEventConsistencyVerifier.reset();
+                    }
+                    if (mOutboundInputEventConsistencyVerifier != null) {
+                        mOutboundInputEventConsistencyVerifier.reset();
+                    }
                     onInstalled();
                     break;
 
@@ -215,6 +235,9 @@
                 case MSG_INPUT_EVENT: {
                     final InputEvent event = (InputEvent)msg.obj;
                     try {
+                        if (mInboundInputEventConsistencyVerifier != null) {
+                            mInboundInputEventConsistencyVerifier.onInputEvent(event, 0);
+                        }
                         onInputEvent(event, msg.arg1);
                     } finally {
                         event.recycle();
diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
index 1b5bad9..5501361 100644
--- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -68,6 +68,9 @@
     protected RegistrantList mPsRestrictEnabledRegistrants = new RegistrantList();
     protected RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList();
 
+    /* Radio power off pending flag and tag counter */
+    private boolean mPendingRadioPowerOffAfterDataOff = false;
+    private int mPendingRadioPowerOffAfterDataOffTag = 0;
 
     protected  static final boolean DBG = true;
 
@@ -77,8 +80,6 @@
     /** Waiting period before recheck gprs and voice registration. */
     public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
 
-    public static final int DATA_STATE_POLL_SLEEP_MS = 100;
-
     /** GSM events */
     protected static final int EVENT_RADIO_STATE_CHANGED               = 1;
     protected static final int EVENT_NETWORK_STATE_CHANGED             = 2;
@@ -262,7 +263,29 @@
         }
     }
 
-    public abstract void handleMessage(Message msg);
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case EVENT_SET_RADIO_POWER_OFF:
+                synchronized(this) {
+                    if (mPendingRadioPowerOffAfterDataOff &&
+                            (msg.arg1 == mPendingRadioPowerOffAfterDataOffTag)) {
+                        if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
+                        hangupAndPowerOff();
+                        mPendingRadioPowerOffAfterDataOffTag += 1;
+                        mPendingRadioPowerOffAfterDataOff = false;
+                    } else {
+                        log("EVENT_SET_RADIO_OFF is stale arg1=" + msg.arg1 +
+                                "!= tag=" + mPendingRadioPowerOffAfterDataOffTag);
+                    }
+                }
+                break;
+
+            default:
+                log("Unhandled message with number: " + msg.what);
+                break;
+        }
+    }
 
     protected abstract Phone getPhone();
     protected abstract void handlePollStateResult(int what, AsyncResult ar);
@@ -370,7 +393,52 @@
      *
      * Hang up the existing voice calls to decrease call drop rate.
      */
-    public abstract void powerOffRadioSafely();
+    public void powerOffRadioSafely(DataConnectionTracker dcTracker) {
+        synchronized (this) {
+            if (!mPendingRadioPowerOffAfterDataOff) {
+                if (dcTracker.isAnyActiveDataConnections()) {
+                    dcTracker.cleanUpAllConnections(null);
+                    Message msg = Message.obtain(this);
+                    msg.what = EVENT_SET_RADIO_POWER_OFF;
+                    msg.arg1 = ++mPendingRadioPowerOffAfterDataOffTag;
+                    if (sendMessageDelayed(msg, 30000)) {
+                        if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
+                        mPendingRadioPowerOffAfterDataOff = true;
+                    } else {
+                        log("Cannot send delayed Msg, turn off radio right away.");
+                        hangupAndPowerOff();
+                    }
+                } else {
+                    dcTracker.cleanUpAllConnections(null);
+                    if (DBG) log("Data disconnected, turn off radio right away.");
+                    hangupAndPowerOff();
+                }
+            }
+        }
+    }
+
+    /**
+     * process the pending request to turn radio off after data is disconnected
+     *
+     * return true if there is pending request to process; false otherwise.
+     */
+    public boolean processPendingRadioPowerOffAfterDataOff() {
+        synchronized(this) {
+            if (mPendingRadioPowerOffAfterDataOff) {
+                if (DBG) log("Process pending request to turn radio off.");
+                mPendingRadioPowerOffAfterDataOffTag += 1;
+                hangupAndPowerOff();
+                mPendingRadioPowerOffAfterDataOff = false;
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Hang up all voice call and turn off radio. Implemented by derived class.
+     */
+    protected abstract void hangupAndPowerOff();
 
     /** Cancel a pending (if any) pollState() operation */
     protected void cancelPollState() {
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index c4fab66..4927006 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -75,7 +75,7 @@
     /** 'true' if device supports both LTE and CDMA mode of operation.
      *  Availability: Set only on devices supporting LTE and CDMA.
      */
-    static final String PROPERTY_NETWORK_LTE_ON_CDMA = "ro.telephony.lte_on_cdma";
+    static final String PROPERTY_NETWORK_LTE_ON_CDMA = "telephony.lte_on_cdma";
 
     static final String CURRENT_ACTIVE_PHONE = "gsm.current.phone-type";
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 3d10c9c..05361cd 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -907,6 +907,11 @@
     }
 
     @Override
+    public boolean isAnyActiveDataConnections() {
+        return (mState != State.IDLE);
+    }
+
+    @Override
     protected void log(String s) {
         Log.d(LOG_TAG, "[CdmaDataConnectionTracker] " + s);
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 4c6cd17..07f9e38 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -139,8 +139,6 @@
     private boolean isEriTextLoaded = false;
     private boolean isSubscriptionFromRuim = false;
 
-    private boolean mPendingRadioPowerOffAfterDataOff = false;
-
     /* Used only for debugging purposes. */
     private String mRegistrationDeniedReason;
 
@@ -485,18 +483,8 @@
             }
             break;
 
-        case EVENT_SET_RADIO_POWER_OFF:
-            synchronized(this) {
-                if (mPendingRadioPowerOffAfterDataOff) {
-                    if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
-                    hangupAndPowerOff();
-                    mPendingRadioPowerOffAfterDataOff = false;
-                }
-            }
-            break;
-
         default:
-            Log.e(LOG_TAG, "Unhandled message with number: " + msg.what);
+            super.handleMessage(msg);
         break;
         }
     }
@@ -513,35 +501,10 @@
             DataConnectionTracker dcTracker = phone.mDataConnection;
 
             // If it's on and available and we want it off gracefully
-            powerOffRadioSafely();
+            powerOffRadioSafely(dcTracker);
         } // Otherwise, we're in the desired state
     }
 
-    // TODO: Consider moving this method to DataConnectionTracker
-    @Override
-    public void powerOffRadioSafely() {
-        DataConnectionTracker dcTracker = phone.mDataConnection;
-
-        synchronized (this) {
-            if (!mPendingRadioPowerOffAfterDataOff) {
-                if (dcTracker.isAnyActiveDataConnections()) {
-                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
-                    if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) {
-                        if (DBG) log("Wait upto 30s for data to disconnect, then turn off radio.");
-                        mPendingRadioPowerOffAfterDataOff = true;
-                    } else {
-                        Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away.");
-                        hangupAndPowerOff();
-                    }
-                } else {
-                    dcTracker.cleanUpAllConnections(Phone.REASON_RADIO_TURNED_OFF);
-                    if (DBG) log("Data disconnected, turn off radio right away.");
-                    hangupAndPowerOff();
-                }
-            }
-        }
-    }
-
     @Override
     protected void updateSpnDisplay() {
         // TODO RUIM SPN is not implemented, EF_SPN has to be read and Display Condition
@@ -1658,24 +1621,6 @@
     }
 
     /**
-     * process the pending request to turn radio off after data is disconnected
-     *
-     * return true if there is pending request to process; false otherwise.
-     */
-    public boolean processPendingRadioPowerOffAfterDataOff() {
-        synchronized(this) {
-            if (mPendingRadioPowerOffAfterDataOff) {
-                if (DBG) log("Process pending request to turn radio off.");
-                removeMessages(EVENT_SET_RADIO_POWER_OFF);
-                hangupAndPowerOff();
-                mPendingRadioPowerOffAfterDataOff = false;
-                return true;
-            }
-            return false;
-        }
-    }
-
-    /**
      * Returns OTASP_UNKNOWN, OTASP_NEEDED or OTASP_NOT_NEEDED
      */
     int getOtasp() {
@@ -1701,7 +1646,8 @@
         Log.d(LOG_TAG, "[CdmaServiceStateTracker] " + s);
     }
 
-    private void hangupAndPowerOff() {
+    @Override
+    protected void hangupAndPowerOff() {
         // hang up all active voice calls
         phone.mCT.ringingCall.hangupIfAlive();
         phone.mCT.backgroundCall.hangupIfAlive();
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 4d94b27..7dad459 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -1073,7 +1073,7 @@
     protected void restartRadio() {
         log("************TURN OFF RADIO**************");
         cleanUpAllConnections(true, Phone.REASON_RADIO_TURNED_OFF);
-        mPhone.getServiceStateTracker().powerOffRadioSafely();
+        mPhone.getServiceStateTracker().powerOffRadioSafely(this);
         /* Note: no need to call setRadioPower(true).  Assuming the desired
          * radio power state is still ON (as tracked by ServiceStateTracker),
          * ServiceStateTracker will call setRadioPower when it receives the
@@ -1491,14 +1491,25 @@
         }
 
         mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
+
+        apnContext.setState(State.IDLE);
+        apnContext.setApnSetting(null);
+
+        // if all data connection are gone, check whether Airplane mode request was
+        // pending.
+        if (!isConnected()) {
+            if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
+                // Radio will be turned off. No need to retry data setup
+                return;
+            }
+        }
+
         // Check if APN disabled.
         if (apnContext.getPendingAction() == ApnContext.PENDING_ACTION_APN_DISABLE) {
            mApnContexts.remove(apnContext.getApnType());
            return;
         }
 
-        apnContext.setState(State.IDLE);
-        apnContext.setApnSetting(null);
         if (TextUtils.equals(apnContext.getApnType(), Phone.APN_TYPE_DEFAULT)
             && retryAfterDisconnected(apnContext.getReason())) {
             SystemProperties.set("gsm.defaultpdpcontext.active", "false");
@@ -1894,6 +1905,11 @@
     }
 
     @Override
+    public boolean isAnyActiveDataConnections() {
+        return isConnected();
+    }
+
+    @Override
     protected void log(String s) {
         Log.d(LOG_TAG, "[GsmDataConnectionTracker] " + s);
     }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 3f521a0..ef3eed8 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -164,8 +164,6 @@
     static final int PS_NOTIFICATION = 888;  // Id to update and cancel PS restricted
     static final int CS_NOTIFICATION = 999;  // Id to update and cancel CS restricted
 
-    static final int MAX_NUM_DATA_STATE_READS = 15;
-
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -459,7 +457,7 @@
                 break;
 
             default:
-                Log.e(LOG_TAG, "Unhandled message with number: " + msg.what);
+                super.handleMessage(msg);
             break;
         }
     }
@@ -471,23 +469,13 @@
             cm.setRadioPower(true, null);
         } else if (!mDesiredPowerState && cm.getRadioState().isOn()) {
             // If it's on and available and we want it off gracefully
-            powerOffRadioSafely();
+            DataConnectionTracker dcTracker = phone.mDataConnection;
+            powerOffRadioSafely(dcTracker);
         } // Otherwise, we're in the desired state
     }
 
     @Override
-    public void powerOffRadioSafely() {
-        // Cleanup all connections
-        DataConnectionTracker dcTracker = phone.mDataConnection;
-        Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_ALL_CONNECTIONS);
-        dcTracker.sendMessage(msg);
-
-        // poll data state up to 15 times, with a 100ms delay
-        // totaling 1.5 sec. Normal data disable action will finish in 100ms.
-        for (int i = 0; i < MAX_NUM_DATA_STATE_READS; i++) {
-            SystemClock.sleep(DATA_STATE_POLL_SLEEP_MS);
-        }
-
+    protected void hangupAndPowerOff() {
         // hang up all active voice calls
         if (phone.isInCall()) {
             phone.mCT.ringingCall.hangupIfAlive();