Merge "Tracking merge of dalvik-dev to master"
diff --git a/api/current.xml b/api/current.xml
index 740c697..0a57d3e 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 74155b1..2a11c87 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);
@@ -1299,6 +1300,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..9a727a7 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -592,6 +592,21 @@
 }
 
 // ----------------------------------------------------------------------------
+// Logging
+// ----------------------------------------------------------------------------
+
+jfieldID gFileDescriptorField;
+
+static void
+android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor)
+{
+#ifdef USE_OPENGL_RENDERER
+    int fd = env->GetIntField(javaFileDescriptor, gFileDescriptorField);
+    android::uirenderer::DisplayList::outputLogBuffer(fd);
+#endif // USE_OPENGL_RENDERER
+}
+
+// ----------------------------------------------------------------------------
 // JNI Glue
 // ----------------------------------------------------------------------------
 
@@ -690,6 +705,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 +732,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/index.jd b/docs/html/index.jd
index 7fcd7b6..78f71ac 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -11,10 +11,14 @@
                             </div><!-- end homeTitle -->
                             <div id="announcement-block">
                             <!-- total max width is 520px -->
-                                  <img src="{@docRoot}assets/images/home/GDC2011.png" alt="Android at GDC 2011" width="203px" style="padding-left:22px;padding-bottom:28px;padding-top:22px;"/>
+                                  <img src="{@docRoot}assets/images/home/IO-logo-2011.png"
+alt="Android at Google IO 2011" width="200px"
+style="padding-left:22px;padding-bottom:15px;padding-top:15px;"/>
                                   <div id="announcement" style="width:275px">
-    <p>Thanks to everyone who visited us at the <a href="http://www.gdconf.com/">Game Developers Conference</a> in San Francisco. We're looking forward to seeing your games running on Android!</p>
-    <p><a href="http://android-developers.blogspot.com/2011/02/heading-for-gdc.html">Learn more &raquo;</a></p>
+    <p>Google I/O is a two-day developer event that will take place May 10-11 at Moscone Center, San
+Francisco. The agenda includes several sessions about Android, presented by Android engineers and
+other team members.</p><p><a href="http://www.google.com/events/io/2011/sessions.html">Learn
+more &raquo;</a></p>
                                 </div> <!-- end annoucement -->
                             </div> <!-- end annoucement-block -->
                         </div><!-- end topAnnouncement -->
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index 73d7fc1..5d7b651 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -52,7 +52,7 @@
 <div class="dashboard-panel">
 
 <img alt="" height="250" width="460"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:3.0,4.8,29.0,61.3,0.7,1.0,0.2&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.0&chco=c4df9b,6fad0c" />
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:2.7,3.5,27.2,63.9,0.8,1.7,0.2&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.0&chco=c4df9b,6fad0c" />
 
 <table>
 <tr>
@@ -60,16 +60,16 @@
   <th>API Level</th>
   <th>Distribution</th>
 </tr>
-<tr><td>Android 1.5</td><td>3</td><td>3.0%</td></tr> 
-<tr><td>Android 1.6</td><td>4</td><td>4.8%</td></tr> 
-<tr><td>Android 2.1</td><td>7</td><td>29.0%</td></tr> 
-<tr><td>Android 2.2</td><td>8</td><td>61.3%</td></tr> 
-<tr><td>Android 2.3</td><td>9</td><td>0.7%</td></tr> 
-<tr><td>Android 2.3.3</td><td>10</td><td>1.0%</td></tr> 
+<tr><td>Android 1.5</td><td>3</td><td>2.7%</td></tr> 
+<tr><td>Android 1.6</td><td>4</td><td>3.5%</td></tr> 
+<tr><td>Android 2.1</td><td>7</td><td>27.2%</td></tr> 
+<tr><td>Android 2.2</td><td>8</td><td>63.9%</td></tr> 
+<tr><td>Android 2.3</td><td>9</td><td>0.8%</td></tr> 
+<tr><td>Android 2.3.3</td><td>10</td><td>1.7%</td></tr> 
 <tr><td>Android 3.0</td><td>11</td><td>0.2%</td></tr> 
 </table>
 
-<p><em>Data collected during two weeks ending on March 15, 2011</em></p>
+<p><em>Data collected during two weeks ending on April 1, 2011</em></p>
 <!--
 <p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p>
 -->
@@ -98,9 +98,9 @@
 <div class="dashboard-panel">
 
 <img alt="" height="250" width="660" style="padding:5px;background:#fff"
-src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C09/15%7C10/01%7C10/15%7C11/01%7C11/15%7C12/01%7C12/15%7C01/01%7C01/15%7C02/01%7C02/15%7C03/01%7C03/15%7C1%3A%7C2010%7C%7C%7C%7C%7C%7C%7C2011%7C%7C%7C%7C%7C2011%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.9,99.9,99.9,100.0,99.9,99.8,99.7,100.0,99.9,99.9,99.9,100.0,99.8|89.2,90.2,91.1,92.0,92.7,93.4,94.1,95.2,95.6,96.0,96.3,96.7,96.8|72.1,73.8,75.3,77.4,79.6,82.2,84.4,87.2,88.3,89.7,90.5,91.5,92.0|32.1,33.4,34.5,37.1,40.5,44.3,47.7,51.8,54.3,58.3,59.7,61.5,63.0|0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.4,0.6,0.7,0.8,1.1,1.7|0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0&chm=tAndroid 1.5,7caa36,0,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid 1.6,689326,1,0,15,,t::-5|b,b4db77,1,2,0|tAndroid 2.1,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|tAndroid 2.2,3f5e0e,3,0,15,,t::-5|b,96dd28,3,4,0|b,83c916,4,5,0|B,6fad0c,5,6,0&chg=7,25&chdl=Android 1.5|Android 1.6|Android 2.1|Android 2.2|Android 2.3|Android 2.3.3&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08" />
+src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C10/01%7C10/15%7C11/01%7C11/15%7C12/01%7C12/15%7C01/01%7C01/15%7C02/01%7C02/15%7C03/01%7C03/15%7C04/01%7C1%3A%7C2010%7C%7C%7C%7C%7C%7C2011%7C%7C%7C%7C%7C%7C2011%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.9,99.9,100.0,99.9,99.8,99.7,100.0,99.9,99.9,99.9,100.0,99.8,99.7|90.2,91.1,92.0,92.7,93.4,94.1,95.2,95.6,96.0,96.3,96.7,96.8,97.0|73.8,75.3,77.4,79.6,82.2,84.4,87.2,88.3,89.7,90.5,91.5,92.0,93.5|33.4,34.5,37.1,40.5,44.3,47.7,51.8,54.3,58.3,59.7,61.5,63.0,66.3|0.0,0.0,0.0,0.0,0.0,0.0,0.4,0.6,0.7,0.8,1.1,1.7,2.5|0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,1.7&chm=b,c3df9b,0,1,0|tAndroid 1.6,689326,1,0,15,,t::-5|b,b4db77,1,2,0|tAndroid 2.1,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|tAndroid 2.2,3f5e0e,3,0,15,,t::-5|b,96dd28,3,4,0|b,83c916,4,5,0|B,6fad0c,5,6,0&chg=7,25&chdl=Android 1.5|Android 1.6|Android 2.1|Android 2.2|Android 2.3|Android 2.3.3&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08" />
 
-<p><em>Last historical dataset collected during two weeks ending on March 15, 2011</em></p>
+<p><em>Last historical dataset collected during two weeks ending on April 1, 2011</em></p>
 
 
 </div><!-- end dashboard-panel -->
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/gui/ISurfaceTexture.h b/include/gui/ISurfaceTexture.h
index 6ed3c6f..d2d3bb8 100644
--- a/include/gui/ISurfaceTexture.h
+++ b/include/gui/ISurfaceTexture.h
@@ -36,6 +36,8 @@
 public:
     DECLARE_META_INTERFACE(SurfaceTexture);
 
+    enum { BUFFER_NEEDS_REALLOCATION = 1 };
+
     // requestBuffer requests a new buffer for the given index. The server (i.e.
     // the ISurfaceTexture implementation) assigns the newly created buffer to
     // the given slot index, and the client is expected to mirror the
@@ -56,6 +58,8 @@
     // should call requestBuffer to assign a new buffer to that slot. The client
     // is expected to either call cancelBuffer on the dequeued slot or to fill
     // in the contents of its associated buffer contents and call queueBuffer.
+    // If dequeueBuffer return BUFFER_NEEDS_REALLOCATION, the client is
+    // expected to call requestBuffer immediately.
     virtual status_t dequeueBuffer(int *slot) = 0;
 
     // queueBuffer indicates that the client has finished filling in the
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index afa64d3..585d288 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -121,6 +121,12 @@
     // buffers before the client is done with them.
     sp<IBinder> getAllocator();
 
+    // setDefaultBufferSize is used to set the size of buffers returned by
+    // requestBuffers when a with and height of zero is requested.
+    // A call to setDefaultBufferSize() may trigger requestBuffers() to
+    // be called from the client.
+    status_t setDefaultBufferSize(uint32_t w, uint32_t h);
+
 private:
 
     // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for
@@ -158,6 +164,23 @@
     // for a slot when requestBuffer is called with that slot's index.
     BufferSlot mSlots[NUM_BUFFER_SLOTS];
 
+    // mDefaultWidth holds the default width of allocated buffers. It is used
+    // in requestBuffers() if a width and height of zero is specified.
+    uint32_t mDefaultWidth;
+
+    // mDefaultHeight holds the default height of allocated buffers. It is used
+    // in requestBuffers() if a width and height of zero is specified.
+    uint32_t mDefaultHeight;
+
+    // mPixelFormat holds the pixel format of allocated buffers. It is used
+    // in requestBuffers() if a format of zero is specified.
+    uint32_t mPixelFormat;
+
+    // mUseDefaultSize indicates whether or not the default size should be used
+    // that is, if the last requestBuffer has been called with both width
+    // and height null.
+    bool mUseDefaultSize;
+
     // mBufferCount is the number of buffer slots that the client and server
     // must maintain. It defaults to MIN_BUFFER_SLOTS and can be changed by
     // calling setBufferCount.
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/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index cdaca47..f4e2a67 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -76,6 +76,10 @@
 static void mtxMul(float out[16], const float a[16], const float b[16]);
 
 SurfaceTexture::SurfaceTexture(GLuint tex) :
+    mDefaultWidth(1),
+    mDefaultHeight(1),
+    mPixelFormat(PIXEL_FORMAT_RGBA_8888),
+    mUseDefaultSize(true),
     mBufferCount(MIN_BUFFER_SLOTS),
     mCurrentTexture(INVALID_BUFFER_SLOT),
     mCurrentTransform(0),
@@ -115,6 +119,16 @@
     return OK;
 }
 
+status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
+{
+    Mutex::Autolock lock(mMutex);
+    if ((w != mDefaultWidth) || (h != mDefaultHeight)) {
+        mDefaultWidth = w;
+        mDefaultHeight = h;
+    }
+    return OK;
+}
+
 sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf,
         uint32_t w, uint32_t h, uint32_t format, uint32_t usage) {
     LOGV("SurfaceTexture::requestBuffer");
@@ -124,12 +138,34 @@
                 mBufferCount, buf);
         return 0;
     }
+    if ((w && !h) || (!w & h)) {
+        LOGE("requestBuffer: invalid size: w=%u, h=%u: %d", w, h, buf);
+        return 0;
+    }
+
+    const bool useDefaultSize = !w && !h;
+    if (useDefaultSize) {
+        // use the default size
+        w = mDefaultWidth;
+        h = mDefaultHeight;
+    }
+
+    const bool updateFormat = (format != 0);
+    if (!updateFormat) {
+        // keep the current (or default) format
+        format = mPixelFormat;
+    }
+
     usage |= GraphicBuffer::USAGE_HW_TEXTURE;
     sp<GraphicBuffer> graphicBuffer(
             mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage));
     if (graphicBuffer == 0) {
         LOGE("requestBuffer: SurfaceComposer::createGraphicBuffer failed");
     } else {
+        mUseDefaultSize = useDefaultSize;
+        if (updateFormat) {
+            mPixelFormat = format;
+        }
         mSlots[buf].mGraphicBuffer = graphicBuffer;
         if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
             eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage);
@@ -155,7 +191,18 @@
     if (found == INVALID_BUFFER_SLOT) {
         return -EBUSY;
     }
+
     *buf = found;
+
+    const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
+    if (buffer == NULL) {
+        return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+    }
+    if ((mUseDefaultSize) &&
+        ((uint32_t(buffer->width) != mDefaultWidth) ||
+         (uint32_t(buffer->height) != mDefaultHeight))) {
+        return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
+    }
     return OK;
 }
 
@@ -312,10 +359,10 @@
         } else {
             tx = 0.0f;
         }
-        if (mCurrentCrop.right < buf->getWidth()) {
+        if (mCurrentCrop.right < int32_t(buf->getWidth())) {
             xshrink++;
         }
-        if (mCurrentCrop.bottom < buf->getHeight()) {
+        if (mCurrentCrop.bottom < int32_t(buf->getHeight())) {
             ty = (float(buf->getHeight() - mCurrentCrop.bottom) + 1.0f) /
                     float(buf->getHeight());
             yshrink++;
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index a4812d0..29fc4d3 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -25,8 +25,8 @@
 
 SurfaceTextureClient::SurfaceTextureClient(
         const sp<ISurfaceTexture>& surfaceTexture):
-        mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(1),
-        mReqHeight(1), mReqFormat(DEFAULT_FORMAT), mReqUsage(0),
+        mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(0),
+        mReqHeight(0), mReqFormat(DEFAULT_FORMAT), mReqUsage(0),
         mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mMutex() {
     // Initialize the ANativeWindow function pointers.
     ANativeWindow::setSwapInterval  = setSwapInterval;
@@ -100,7 +100,8 @@
         return err;
     }
     sp<GraphicBuffer>& gbuf(mSlots[buf]);
-    if (gbuf == 0 || gbuf->getWidth() != mReqWidth ||
+    if (err == ISurfaceTexture::BUFFER_NEEDS_REALLOCATION ||
+        gbuf == 0 || gbuf->getWidth() != mReqWidth ||
         gbuf->getHeight() != mReqHeight ||
         uint32_t(gbuf->getPixelFormat()) != mReqFormat ||
         (gbuf->getUsage() & mReqUsage) != mReqUsage) {
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 94b05bc..348171d 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -100,4 +100,151 @@
     eglTerminate(dpy);
 }
 
+TEST_F(SurfaceTextureClientTest, BufferGeometryInvalidSizesFail) {
+    sp<ANativeWindow> anw(mSTC);
+
+    EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), -1,  0,  0));
+    EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(),  0, -1,  0));
+    EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(),  0,  0, -1));
+    EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), -1, -1,  0));
+    EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(),  0,  8,  0));
+    EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(),  8,  0,  0));
+}
+
+TEST_F(SurfaceTextureClientTest, DefaultGeometryValues) {
+    sp<ANativeWindow> anw(mSTC);
+    android_native_buffer_t* buf;
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(1, buf->width);
+    EXPECT_EQ(1, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometryCanBeSet) {
+    sp<ANativeWindow> anw(mSTC);
+    android_native_buffer_t* buf;
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, PIXEL_FORMAT_RGB_565));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(16, buf->width);
+    EXPECT_EQ(8, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometryDefaultSizeSetFormat) {
+    sp<ANativeWindow> anw(mSTC);
+    android_native_buffer_t* buf;
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, PIXEL_FORMAT_RGB_565));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(1, buf->width);
+    EXPECT_EQ(1, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometrySetSizeDefaultFormat) {
+    sp<ANativeWindow> anw(mSTC);
+    android_native_buffer_t* buf;
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, 0));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(16, buf->width);
+    EXPECT_EQ(8, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeUnset) {
+    sp<ANativeWindow> anw(mSTC);
+    android_native_buffer_t* buf;
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, 0));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(16, buf->width);
+    EXPECT_EQ(8, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, 0));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(1, buf->width);
+    EXPECT_EQ(1, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeChangedWithoutFormat) {
+    sp<ANativeWindow> anw(mSTC);
+    android_native_buffer_t* buf;
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, PIXEL_FORMAT_RGB_565));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(1, buf->width);
+    EXPECT_EQ(1, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, 0));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(16, buf->width);
+    EXPECT_EQ(8, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSize) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf;
+    EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf));
+    EXPECT_EQ(16, buf->width);
+    EXPECT_EQ(8, buf->height);
+    EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf));
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeAfterDequeue) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[2];
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    EXPECT_NE(buf[0], buf[1]);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
+    EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    EXPECT_NE(buf[0], buf[1]);
+    EXPECT_EQ(16, buf[0]->width);
+    EXPECT_EQ(16, buf[1]->width);
+    EXPECT_EQ(8, buf[0]->height);
+    EXPECT_EQ(8, buf[1]->height);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
+}
+
+TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeVsGeometry) {
+    sp<ANativeWindow> anw(mSTC);
+    sp<SurfaceTexture> st(mST);
+    android_native_buffer_t* buf[2];
+    EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    EXPECT_NE(buf[0], buf[1]);
+    EXPECT_EQ(16, buf[0]->width);
+    EXPECT_EQ(16, buf[1]->width);
+    EXPECT_EQ(8, buf[0]->height);
+    EXPECT_EQ(8, buf[1]->height);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
+    EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 12, 24, 0));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+    ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1]));
+    EXPECT_NE(buf[0], buf[1]);
+    EXPECT_EQ(12, buf[0]->width);
+    EXPECT_EQ(12, buf[1]->width);
+    EXPECT_EQ(24, buf[0]->height);
+    EXPECT_EQ(24, buf[1]->height);
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0]));
+    ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1]));
+}
+
 }
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/libs/ui/Input.cpp b/libs/ui/Input.cpp
index 440ec00..bbe579e 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -724,7 +724,7 @@
             LOGD("  %d: position (%0.3f, %0.3f), vx=%0.3f, vy=%0.3f, speed=%0.3f",
                     id, positions[index].x, positions[index].y, vx, vy, sqrtf(vx * vx + vy * vy));
         } else {
-            assert(vx == 0 && vy == 0);
+            LOG_ASSERT(vx == 0 && vy == 0);
             LOGD("  %d: position (%0.3f, %0.3f), velocity not available",
                     id, positions[index].x, positions[index].y);
         }
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/input/EventHub.cpp b/services/input/EventHub.cpp
index 7625adb..9a9d9e5 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -450,7 +450,7 @@
 size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
     // Note that we only allow one caller to getEvents(), so don't need
     // to do locking here...  only when adding/removing devices.
-    assert(bufferSize >= 1);
+    LOG_ASSERT(bufferSize >= 1);
 
     if (!mOpened) {
         mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 8363e8b..253d070 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -369,7 +369,7 @@
 
     // Now we have an event to dispatch.
     // All events are eventually dequeued and processed this way, even if we intend to drop them.
-    assert(mPendingEvent != NULL);
+    LOG_ASSERT(mPendingEvent != NULL);
     bool done = false;
     DropReason dropReason = DROP_REASON_NOT_DROPPED;
     if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {
@@ -431,7 +431,7 @@
     }
 
     default:
-        assert(false);
+        LOG_ASSERT(false);
         break;
     }
 
@@ -558,7 +558,7 @@
         reason = "inbound event was dropped because it is stale";
         break;
     default:
-        assert(false);
+        LOG_ASSERT(false);
         return;
     }
 
@@ -962,7 +962,7 @@
             toString(resumeWithAppendedMotionSample));
 #endif
 
-    assert(eventEntry->dispatchInProgress); // should already have been set to true
+    LOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
 
     pokeUserActivityLocked(eventEntry);
 
@@ -1596,9 +1596,10 @@
 
         InputTarget& target = mCurrentInputTargets.editTop();
         target.inputChannel = mMonitoringChannels[i];
-        target.flags = 0;
+        target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
         target.xOffset = 0;
         target.yOffset = 0;
+        target.pointerIds.clear();
     }
 }
 
@@ -1711,7 +1712,7 @@
 
     // Make sure we are never called for streaming when splitting across multiple windows.
     bool isSplit = inputTarget->flags & InputTarget::FLAG_SPLIT;
-    assert(! (resumeWithAppendedMotionSample && isSplit));
+    LOG_ASSERT(! (resumeWithAppendedMotionSample && isSplit));
 
     // Skip this event if the connection status is not normal.
     // We don't want to enqueue additional outbound events if the connection is broken.
@@ -1725,7 +1726,7 @@
 
     // Split a motion event if needed.
     if (isSplit) {
-        assert(eventEntry->type == EventEntry::TYPE_MOTION);
+        LOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION);
 
         MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);
         if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {
@@ -1832,7 +1833,7 @@
             resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_IS);
 
     // If the outbound queue was previously empty, start the dispatch cycle going.
-    if (wasEmpty) {
+    if (wasEmpty && !connection->outboundQueue.isEmpty()) {
         activateConnectionLocked(connection.get());
         startDispatchCycleLocked(currentTime, connection);
     }
@@ -1880,11 +1881,11 @@
             connection->getInputChannelName());
 #endif
 
-    assert(connection->status == Connection::STATUS_NORMAL);
-    assert(! connection->outboundQueue.isEmpty());
+    LOG_ASSERT(connection->status == Connection::STATUS_NORMAL);
+    LOG_ASSERT(! connection->outboundQueue.isEmpty());
 
     DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next;
-    assert(! dispatchEntry->inProgress);
+    LOG_ASSERT(! dispatchEntry->inProgress);
 
     // Mark the dispatch entry as in progress.
     dispatchEntry->inProgress = true;
@@ -2005,7 +2006,7 @@
     }
 
     default: {
-        assert(false);
+        LOG_ASSERT(false);
     }
     }
 
@@ -2242,7 +2243,7 @@
 
 InputDispatcher::MotionEntry*
 InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) {
-    assert(pointerIds.value != 0);
+    LOG_ASSERT(pointerIds.value != 0);
 
     uint32_t splitPointerIndexMap[MAX_POINTERS];
     int32_t splitPointerIds[MAX_POINTERS];
@@ -3510,7 +3511,7 @@
                         return;
                     }
 
-                    assert(connection->outboundQueue.headSentinel.next == dispatchEntry);
+                    LOG_ASSERT(connection->outboundQueue.headSentinel.next == dispatchEntry);
 
                     // Latch the fallback keycode for this key on an initial down.
                     // The fallback keycode cannot change at any other point in the lifecycle.
@@ -3523,7 +3524,7 @@
                         connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode);
                     }
 
-                    assert(fallbackKeyCode != -1);
+                    LOG_ASSERT(fallbackKeyCode != -1);
 
                     // Cancel the fallback key if the policy decides not to send it anymore.
                     // We will continue to dispatch the key to the policy but we will no
@@ -3761,7 +3762,7 @@
     if (injectionState->refCount == 0) {
         mInjectionStatePool.free(injectionState);
     } else {
-        assert(injectionState->refCount > 0);
+        LOG_ASSERT(injectionState->refCount > 0);
     }
 }
 
@@ -3777,7 +3778,7 @@
         releaseMotionEntry(static_cast<MotionEntry*>(entry));
         break;
     default:
-        assert(false);
+        LOG_ASSERT(false);
         break;
     }
 }
@@ -3789,7 +3790,7 @@
         releaseEventEntryInjectionState(entry);
         mConfigurationChangeEntryPool.free(entry);
     } else {
-        assert(entry->refCount > 0);
+        LOG_ASSERT(entry->refCount > 0);
     }
 }
 
@@ -3799,7 +3800,7 @@
         releaseEventEntryInjectionState(entry);
         mKeyEntryPool.free(entry);
     } else {
-        assert(entry->refCount > 0);
+        LOG_ASSERT(entry->refCount > 0);
     }
 }
 
@@ -3814,7 +3815,7 @@
         }
         mMotionEntryPool.free(entry);
     } else {
-        assert(entry->refCount > 0);
+        LOG_ASSERT(entry->refCount > 0);
     }
 }
 
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 82cf62f..6db445e 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -293,7 +293,7 @@
                 handleConfigurationChanged(rawEvent->when);
                 break;
             default:
-                assert(false); // can't happen
+                LOG_ASSERT(false); // can't happen
                 break;
             }
         }
@@ -1378,7 +1378,7 @@
         dump.append(INDENT4 "Mode: navigation\n");
         break;
     default:
-        assert(false);
+        LOG_ASSERT(false);
     }
 
     dump.appendFormat(INDENT4 "OrientationAware: %s\n",
@@ -1801,7 +1801,7 @@
         mPointerSource = AINPUT_SOURCE_MOUSE;
         break;
     default:
-        assert(false);
+        LOG_ASSERT(false);
     }
 
     // Configure absolute axis information.
@@ -1874,7 +1874,7 @@
         dump.append(INDENT4 "DeviceType: pointer\n");
         break;
     default:
-        assert(false);
+        LOG_ASSERT(false);
     }
 
     dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n",
@@ -2509,7 +2509,7 @@
         dump.append(INDENT4 "touch.touchSize.calibration: pressure\n");
         break;
     default:
-        assert(false);
+        LOG_ASSERT(false);
     }
 
     // Tool Size
@@ -2527,7 +2527,7 @@
         dump.append(INDENT4 "touch.toolSize.calibration: area\n");
         break;
     default:
-        assert(false);
+        LOG_ASSERT(false);
     }
 
     if (mCalibration.haveToolSizeLinearScale) {
@@ -2567,7 +2567,7 @@
         dump.append(INDENT4 "touch.pressure.calibration: amplitude\n");
         break;
     default:
-        assert(false);
+        LOG_ASSERT(false);
     }
 
     switch (mCalibration.pressureSource) {
@@ -2580,7 +2580,7 @@
     case Calibration::PRESSURE_SOURCE_DEFAULT:
         break;
     default:
-        assert(false);
+        LOG_ASSERT(false);
     }
 
     if (mCalibration.havePressureScale) {
@@ -2597,7 +2597,7 @@
         dump.append(INDENT4 "touch.size.calibration: normalized\n");
         break;
     default:
-        assert(false);
+        LOG_ASSERT(false);
     }
 
     // Orientation
@@ -2612,7 +2612,7 @@
         dump.append(INDENT4 "touch.orientation.calibration: vector\n");
         break;
     default:
-        assert(false);
+        LOG_ASSERT(false);
     }
 }
 
@@ -2901,7 +2901,7 @@
         // Although applications receive new locations as part of individual pointer up
         // events, they do not generally handle them except when presented in a move event.
         if (moveNeeded) {
-            assert(moveIdBits.value == dispatchedIdBits.value);
+            LOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);
             dispatchMotion(when, policyFlags, mTouchSource,
                     AMOTION_EVENT_ACTION_MOVE, 0, metaState, 0,
                     mCurrentTouchCoords, mCurrentTouch.idToIndex, dispatchedIdBits, -1,
@@ -3550,7 +3550,7 @@
         // Case 4. Exactly one finger down, button is not pressed. (HOVER)
         // The pointer follows the active touch point.
         // Emit HOVER_MOVE events at the pointer location.
-        assert(activeTouchId >= 0);
+        LOG_ASSERT(activeTouchId >= 0);
 
 #if DEBUG_GESTURES
         LOGD("Gestures: HOVER");
@@ -3600,7 +3600,7 @@
         // Fix the centroid of the figure when the gesture actually starts.
         // We do not recalculate the centroid at any other time during the gesture because
         // it would affect the relationship of the touch points relative to the pointer location.
-        assert(activeTouchId >= 0);
+        LOG_ASSERT(activeTouchId >= 0);
 
         uint32_t currentTouchPointerCount = mCurrentTouch.pointerCount;
         if (currentTouchPointerCount > MAX_POINTERS) {
@@ -3712,7 +3712,7 @@
                     "activeGestureId=%d, currentTouchPointerCount=%d",
                     activeTouchId, mPointerGesture.activeGestureId, currentTouchPointerCount);
 #endif
-            assert(mPointerGesture.activeGestureId >= 0);
+            LOG_ASSERT(mPointerGesture.activeGestureId >= 0);
 
             float x = (mCurrentTouch.pointers[0].x + mCurrentTouch.pointers[1].x
                     - mPointerGesture.initialCentroidX * 2) * 0.5f
@@ -3736,7 +3736,7 @@
                     "activeGestureId=%d, currentTouchPointerCount=%d",
                     activeTouchId, mPointerGesture.activeGestureId, currentTouchPointerCount);
 #endif
-            assert(mPointerGesture.activeGestureId >= 0);
+            LOG_ASSERT(mPointerGesture.activeGestureId >= 0);
 
             mPointerGesture.currentGesturePointerCount = currentTouchPointerCount;
             mPointerGesture.currentGestureIdBits.clear();
@@ -3895,7 +3895,7 @@
         pointerCount += 1;
     }
 
-    assert(pointerCount != 0);
+    LOG_ASSERT(pointerCount != 0);
 
     if (changedId >= 0 && pointerCount == 1) {
         // Replace initial down and final up action.
@@ -3907,7 +3907,7 @@
             action = AMOTION_EVENT_ACTION_UP;
         } else {
             // Can't happen.
-            assert(false);
+            LOG_ASSERT(false);
         }
     }
 
@@ -4072,7 +4072,7 @@
                     // Previous iterations consumed the root element of the heap.
                     // Pop root element off of the heap (sift down).
                     heapSize -= 1;
-                    assert(heapSize > 0);
+                    LOG_ASSERT(heapSize > 0);
 
                     // Sift down.
                     heap[0] = heap[heapSize];
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/ApnContext.java b/telephony/java/com/android/internal/telephony/ApnContext.java
index bd1bc1d..a86ea7e 100644
--- a/telephony/java/com/android/internal/telephony/ApnContext.java
+++ b/telephony/java/com/android/internal/telephony/ApnContext.java
@@ -42,7 +42,8 @@
 
     ArrayList<ApnSetting> mWaitingApns = null;
 
-    private int mWaitingApnsPermanentFailureCountDown = 0;
+    /** A zero indicates that all waiting APNs had a permanent error */
+    private int mWaitingApnsPermanentFailureCountDown;
 
     ApnSetting mApnSetting;
 
@@ -68,14 +69,6 @@
         pendingAction = pa;
     }
 
-    public int getPermFailCount() {
-        return mWaitingApnsPermanentFailureCountDown;
-    }
-
-    public void decPermFailCount() {
-        mWaitingApnsPermanentFailureCountDown--;
-    }
-
     public String getApnType() {
         return mApnType;
     }
@@ -98,9 +91,18 @@
 
     public void setWaitingApns(ArrayList<ApnSetting> waitingApns) {
         mWaitingApns = waitingApns;
+        mWaitingApnsPermanentFailureCountDown = mWaitingApns.size();
     }
 
-    public ApnSetting getNextApn() {
+    public int getWaitingApnsPermFailCount() {
+        return mWaitingApnsPermanentFailureCountDown;
+    }
+
+    public void decWaitingApnsPermFailCount() {
+        mWaitingApnsPermanentFailureCountDown--;
+    }
+
+    public ApnSetting getNextWaitingApn() {
         ArrayList<ApnSetting> list = mWaitingApns;
         ApnSetting apn = null;
 
@@ -112,7 +114,7 @@
         return apn;
     }
 
-    public void removeNextApn() {
+    public void removeNextWaitingApn() {
         if ((mWaitingApns != null) && (!mWaitingApns.isEmpty())) {
             mWaitingApns.remove(0);
         }
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..891a237 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -574,9 +574,11 @@
     }
 
     private boolean trySetupData(String reason, String type) {
-        if (DBG)
-            log("***trySetupData for type:" + type+" due to " + (reason == null ? "(unspecified)" : reason));
-        log("[DSAC DEB] " + "trySetupData with mIsPsRestricted=" + mIsPsRestricted);
+        if (DBG) {
+            log("***trySetupData for type:" + type +
+                    " due to " + (reason == null ? "(unspecified)" : reason) +
+                    " isPsRestricted=" + mIsPsRestricted);
+        }
 
         if (type == null) {
             type = Phone.APN_TYPE_DEFAULT;
@@ -585,12 +587,8 @@
         ApnContext apnContext = mApnContexts.get(type);
 
         if (apnContext == null ){
-            if (DBG) log("***new apn context for type:" + type);
+            if (DBG) log("new apn context for type:" + type);
             apnContext = new ApnContext(type, LOG_TAG);
-            if (apnContext == null) {
-                if (DBG) log("***new apn context failed ");
-                return false;
-            }
             mApnContexts.put(type, apnContext);
         }
         apnContext.setReason(reason);
@@ -839,7 +837,7 @@
         GsmDataConnection dc;
 
         int profileId = getApnProfileID(apnContext.getApnType());
-        apn = apnContext.getNextApn();
+        apn = apnContext.getNextWaitingApn();
         if (apn == null) {
             if (DBG) log("setupData: return for no apn found!");
             return false;
@@ -1073,7 +1071,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
@@ -1449,19 +1447,22 @@
             }
 
             // Count permanent failures and remove the APN we just tried
-            // TODO: Where is mWaitingApnsPermanentFailureCountDown initialized
-            if (cause.isPermanentFail())
-                apnContext.decPermFailCount();
+            if (cause.isPermanentFail()) apnContext.decWaitingApnsPermFailCount();
 
-            apnContext.removeNextApn();
-            if (DBG) log(String.format("onDataSetupComplete: mWaitingApns.size=%d" +
-                            " mWaitingApnsPermanenatFailureCountDown=%d",
-                            apnContext.getWaitingApns().size(), apnContext.getPermFailCount()));
+            apnContext.removeNextWaitingApn();
+            if (DBG) {
+                log(String.format("onDataSetupComplete: WaitingApns.size=%d" +
+                        " WaitingApnsPermFailureCountDown=%d",
+                        apnContext.getWaitingApns().size(),
+                        apnContext.getWaitingApnsPermFailCount()));
+            }
 
             // See if there are more APN's to try
             if (apnContext.getWaitingApns().isEmpty()) {
-                if (apnContext.getPermFailCount() == 0) {
-                    if (DBG) log("onDataSetupComplete: Permanent failures stop retrying");
+                if (apnContext.getWaitingApnsPermFailCount() == 0) {
+                    if (DBG) {
+                        log("onDataSetupComplete: All APN's had permanent failures, stop retrying");
+                    }
                     apnContext.setState(State.FAILED);
                     notifyDataConnection(Phone.REASON_APN_FAILED);
                 } else {
@@ -1473,7 +1474,8 @@
                 apnContext.setState(State.SCANNING);
                 // Wait a bit before trying the next APN, so that
                 // we're not tying up the RIL command channel
-                sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext), APN_DELAY_MILLIS);
+                sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext),
+                        APN_DELAY_MILLIS);
             }
         }
     }
@@ -1491,14 +1493,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 +1907,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();