Merge "Remove deprecated and unused entryEvicted method." into honeycomb-mr1
diff --git a/api/current.xml b/api/current.xml
index 416516f..4996e6a 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -28850,7 +28850,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<method name="completedDownload"
+<method name="addCompletedDownload"
  return="long"
  abstract="false"
  native="false"
@@ -118085,6 +118085,497 @@
 </field>
 </class>
 </package>
+<package name="android.net.rtp"
+>
+<class name="AudioCodec"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getCodec"
+ return="android.net.rtp.AudioCodec"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="int">
+</parameter>
+<parameter name="rtpmap" type="java.lang.String">
+</parameter>
+<parameter name="fmtp" type="java.lang.String">
+</parameter>
+</method>
+<method name="getCodecs"
+ return="android.net.rtp.AudioCodec[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<field name="AMR"
+ type="android.net.rtp.AudioCodec"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="GSM"
+ type="android.net.rtp.AudioCodec"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="GSM_EFR"
+ type="android.net.rtp.AudioCodec"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PCMA"
+ type="android.net.rtp.AudioCodec"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PCMU"
+ type="android.net.rtp.AudioCodec"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="fmtp"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="rtpmap"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="type"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="AudioGroup"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AudioGroup"
+ type="android.net.rtp.AudioGroup"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="clear"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getStreams"
+ return="android.net.rtp.AudioStream[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="sendDtmf"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="int">
+</parameter>
+</method>
+<method name="setMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="int">
+</parameter>
+</method>
+<field name="MODE_ECHO_SUPPRESSION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODE_MUTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODE_NORMAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODE_ON_HOLD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="AudioStream"
+ extends="android.net.rtp.RtpStream"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AudioStream"
+ type="android.net.rtp.AudioStream"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="address" type="java.net.InetAddress">
+</parameter>
+<exception name="SocketException" type="java.net.SocketException">
+</exception>
+</constructor>
+<method name="getCodec"
+ return="android.net.rtp.AudioCodec"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDtmfType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGroup"
+ return="android.net.rtp.AudioGroup"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isBusy"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="join"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="group" type="android.net.rtp.AudioGroup">
+</parameter>
+</method>
+<method name="setCodec"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="codec" type="android.net.rtp.AudioCodec">
+</parameter>
+</method>
+<method name="setDtmfType"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="int">
+</parameter>
+</method>
+</class>
+<class name="RtpStream"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="associate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="address" type="java.net.InetAddress">
+</parameter>
+<parameter name="port" type="int">
+</parameter>
+</method>
+<method name="getLocalAddress"
+ return="java.net.InetAddress"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getLocalPort"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRemoteAddress"
+ return="java.net.InetAddress"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRemotePort"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isBusy"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="release"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="int">
+</parameter>
+</method>
+<field name="MODE_NORMAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODE_RECEIVE_ONLY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MODE_SEND_ONLY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+</package>
 <package name="android.net.sip"
 >
 <class name="SipAudioCall"
@@ -218100,6 +218591,28 @@
  visibility="public"
 >
 </field>
+<field name="AXIS_BRAKE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="23"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AXIS_GAS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="22"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="AXIS_GENERIC_1"
  type="int"
  transient="false"
@@ -218353,6 +218866,17 @@
  visibility="public"
 >
 </field>
+<field name="AXIS_RUDDER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="20"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="AXIS_RX"
  type="int"
  transient="false"
@@ -218397,6 +218921,17 @@
  visibility="public"
 >
 </field>
+<field name="AXIS_THROTTLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="19"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="AXIS_TOOL_MAJOR"
  type="int"
  transient="false"
@@ -218452,6 +218987,17 @@
  visibility="public"
 >
 </field>
+<field name="AXIS_WHEEL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="21"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="AXIS_X"
  type="int"
  transient="false"
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index d04fa57..b88e5cf 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -376,8 +376,8 @@
         /**
          * This download shows in the notifications after completion ONLY.
          * It is usuable only with
-         * {@link DownloadManager#completedDownload(String, String, boolean, String,
-         * String, long, boolean)}.
+         * {@link DownloadManager#addCompletedDownload(String, String,
+         * boolean, String, String, long, boolean)}.
          */
         public static final int VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION = 3;
 
@@ -1111,7 +1111,7 @@
      * @return  an ID for the download entry added to the downloads app, unique across the system
      * This ID is used to make future calls related to this download.
      */
-    public long completedDownload(String title, String description,
+    public long addCompletedDownload(String title, String description,
             boolean isMediaScannerScannable, String mimeType, String path, long length,
             boolean showNotification) {
         // make sure the input args are non-null/non-zero
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 9a6a274..fa5479b 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -245,11 +245,12 @@
     private static native void nDestroyDisplayList(int displayList);
 
     @Override
-    public boolean drawDisplayList(DisplayList displayList) {
-        return nDrawDisplayList(mRenderer, ((GLES20DisplayList) displayList).mNativeDisplayList);
+    public boolean drawDisplayList(DisplayList displayList, Rect dirty) {
+        return nDrawDisplayList(mRenderer,
+                ((GLES20DisplayList) displayList).mNativeDisplayList, dirty);
     }
 
-    private static native boolean nDrawDisplayList(int renderer, int displayList);
+    private static native boolean nDrawDisplayList(int renderer, int displayList, Rect dirty);
 
     ///////////////////////////////////////////////////////////////////////////
     // Hardware layer
diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java
index 262eb81..aeff31f 100644
--- a/core/java/android/view/GLES20DisplayList.java
+++ b/core/java/android/view/GLES20DisplayList.java
@@ -40,13 +40,6 @@
         hostView = new WeakReference<View>(view);
     }
 
-    public void invalidateView() {
-        View v = hostView.get();
-        if (v != null) {
-            v.invalidate();
-        }
-    }
-
     @Override
     HardwareCanvas start() {
         if (mStarted) {
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index e6fecc8..cb1003a 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -53,8 +53,13 @@
      * Draws the specified display list onto this canvas.
      * 
      * @param displayList The display list to replay.
+     * @param dirty The dirty region to redraw in the next pass, matters only
+     *        if this method returns true, can be null.
+     * 
+     * @return True if the content of the display list requires another
+     *         drawing pass (invalidate()), false otherwise
      */
-    abstract boolean drawDisplayList(DisplayList displayList);
+    abstract boolean drawDisplayList(DisplayList displayList, Rect dirty);
 
     /**
      * Draws the specified layer onto this canvas.
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index b53aa21..c078c08 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -269,7 +269,7 @@
         static EGLDisplay sEglDisplay;
         static EGLConfig sEglConfig;
 
-        private static Thread sEglThread;        
+        private static Thread sEglThread;
 
         EGLSurface mEglSurface;
         
@@ -284,6 +284,8 @@
         final boolean mTranslucent;
 
         private boolean mDestroyed;
+        
+        private final Rect mRedrawClip = new Rect();
 
         GlRenderer(int glVersion, boolean translucent) {
             mGlVersion = glVersion;
@@ -606,8 +608,13 @@
 
                         DisplayList displayList = view.getDisplayList();
                         if (displayList != null) {
-                            if (canvas.drawDisplayList(displayList)) {
-                                view.invalidate();
+                            if (canvas.drawDisplayList(displayList, mRedrawClip)) {
+                                if (mRedrawClip.isEmpty()) {
+                                    view.invalidate();
+                                } else {
+                                    view.getParent().invalidateChild(view, mRedrawClip);
+                                }
+                                mRedrawClip.setEmpty();
                             }
                         } else {
                             // Shouldn't reach here
@@ -646,8 +653,8 @@
         private int checkCurrent() {
             // TODO: Don't check the current context when we have one per UI thread
             // TODO: Use a threadlocal flag to know whether the surface has changed
-            if (sEgl.eglGetCurrentContext() != sEglContext ||
-                    sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW) != mEglSurface) {
+            if (!sEglContext.equals(sEgl.eglGetCurrentContext()) ||
+                    !mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
                 if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
                     fallback(true);
                     Log.e(LOG_TAG, "eglMakeCurrent failed " +
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 076f712..3c39149 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -675,6 +675,87 @@
     public static final int AXIS_RTRIGGER = 18;
 
     /**
+     * Constant used to identify the Throttle axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a joystick, reports the absolute position of the throttle control.
+     * The value is normalized to a range from 0.0 (fully open) to 1.0 (fully closed).
+     * </ul>
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_THROTTLE = 19;
+
+    /**
+     * Constant used to identify the Rudder axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a joystick, reports the absolute position of the rudder control.
+     * The value is normalized to a range from -1.0 (turn left) to 1.0 (turn right).
+     * </ul>
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_RUDDER = 20;
+
+    /**
+     * Constant used to identify the Wheel axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a joystick, reports the absolute position of the steering wheel control.
+     * The value is normalized to a range from -1.0 (turn left) to 1.0 (turn right).
+     * </ul>
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_WHEEL = 21;
+
+    /**
+     * Constant used to identify the Gas axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a joystick, reports the absolute position of the gas (accelerator) control.
+     * The value is normalized to a range from 0.0 (no acceleration)
+     * to 1.0 (maximum acceleration).
+     * </ul>
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_GAS = 22;
+
+    /**
+     * Constant used to identify the Brake axis of a motion event.
+     * <p>
+     * <ul>
+     * <li>For a joystick, reports the absolute position of the brake control.
+     * The value is normalized to a range from 0.0 (no braking) to 1.0 (maximum braking).
+     * </ul>
+     * </p>
+     *
+     * @see #getAxisValue(int, int)
+     * @see #getHistoricalAxisValue(int, int, int)
+     * @see MotionEvent.PointerCoords#getAxisValue(int)
+     * @see InputDevice#getMotionRange
+     */
+    public static final int AXIS_BRAKE = 23;
+
+    /**
      * Constant used to identify the Generic 1 axis of a motion event.
      * The interpretation of a generic axis is device-specific.
      *
@@ -877,6 +958,11 @@
         names.append(AXIS_HAT_Y, "AXIS_HAT_Y");
         names.append(AXIS_LTRIGGER, "AXIS_LTRIGGER");
         names.append(AXIS_RTRIGGER, "AXIS_RTRIGGER");
+        names.append(AXIS_THROTTLE, "AXIS_THROTTLE");
+        names.append(AXIS_RUDDER, "AXIS_RUDDER");
+        names.append(AXIS_WHEEL, "AXIS_WHEEL");
+        names.append(AXIS_GAS, "AXIS_GAS");
+        names.append(AXIS_BRAKE, "AXIS_BRAKE");
         names.append(AXIS_GENERIC_1, "AXIS_GENERIC_1");
         names.append(AXIS_GENERIC_2, "AXIS_GENERIC_2");
         names.append(AXIS_GENERIC_3, "AXIS_GENERIC_3");
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 0124151..5521e92 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.util.DisplayMetrics;
 import android.util.FloatMath;
+import android.util.Log;
 
 /**
  * Detects transformation gestures involving more than one pointer ("multitouch")
@@ -36,6 +37,8 @@
  * </ul>
  */
 public class ScaleGestureDetector {
+    private static final String TAG = "ScaleGestureDetector";
+
     /**
      * The listener for receiving notifications when gestures occur.
      * If you want to listen for all the different gestures then implement
@@ -170,6 +173,10 @@
         final int action = event.getActionMasked();
         boolean handled = true;
 
+        if (action == MotionEvent.ACTION_DOWN) {
+            reset(); // Start fresh
+        }
+
         if (!mGestureInProgress) {
             switch (action) {
             case MotionEvent.ACTION_DOWN: {
@@ -197,6 +204,11 @@
                 int index1 = event.getActionIndex();
                 int index0 = event.findPointerIndex(mActiveId0);
                 mActiveId1 = event.getPointerId(index1);
+                if (index0 < 0 || index0 == index1) {
+                    // Probably someone sending us a broken event stream.
+                    index0 = findNewActiveIndex(event, index0 == index1 ? -1 : mActiveId1, index0);
+                    mActiveId0 = event.getPointerId(index0);
+                }
                 mActive0MostRecent = false;
 
                 setContext(event);
@@ -315,6 +327,7 @@
                         final int index = event.findPointerIndex(actionId == mActiveId0 ?
                                 mActiveId1 : mActiveId0);
                         mActiveId0 = event.getPointerId(index);
+
                         mActive0MostRecent = true;
                         mActiveId1 = -1;
                         mFocusX = event.getX(index);
@@ -338,6 +351,18 @@
                     mActiveId1 = event.getPointerId(event.getActionIndex());
                     mActive0MostRecent = false;
 
+                    int index0 = event.findPointerIndex(mActiveId0);
+                    if (index0 < 0 || mActiveId0 == mActiveId1) {
+                        // Probably someone sending us a broken event stream.
+                        Log.e(TAG, "Got " + MotionEvent.actionToString(action) +
+                                " with bad state while a gesture was in progress. " +
+                                "Did you forget to pass an event to " +
+                                "ScaleGestureDetector#onTouchEvent?");
+                        index0 = findNewActiveIndex(event,
+                                mActiveId0 == mActiveId1 ? -1 : mActiveId1, index0);
+                        mActiveId0 = event.getPointerId(index0);
+                    }
+
                     setContext(event);
 
                     mGestureInProgress = mListener.onScaleBegin(this);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 3153ac5..6ef680b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2585,7 +2585,7 @@
                     }
                 } else {
                     child.mPrivateFlags &= ~DIRTY_MASK;
-                    ((HardwareCanvas) canvas).drawDisplayList(displayList);
+                    ((HardwareCanvas) canvas).drawDisplayList(displayList, null);
                 }
             }
         } else if (cache != null) {
@@ -4275,11 +4275,10 @@
      *         does not exist within the group
      */
     public View getChildAt(int index) {
-        try {
-            return mChildren[index];
-        } catch (IndexOutOfBoundsException ex) {
+        if (index < 0 || index >= mChildrenCount) {
             return null;
         }
+        return mChildren[index];
     }
 
     /**
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 5468230..11908bb 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -1606,13 +1606,9 @@
                 return;
             } catch (IllegalArgumentException e) {
                 Log.e(TAG, "IllegalArgumentException locking surface", e);
-                try {
-                    if (!sWindowSession.outOfMemory(mWindow)) {
-                        Slog.w(TAG, "No processes killed for memory; killing self");
-                        Process.killProcess(Process.myPid());
-                    }
-                } catch (RemoteException ex) {
-                }
+                // Don't assume this is due to out of memory, it could be
+                // something else, and if it is something else then we could
+                // kill stuff (or ourself) for no reason.
                 mLayoutRequested = true;    // ask wm for a new surface next time.
                 return;
             }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 2d3157e9d..eb7269b 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -351,7 +351,8 @@
 
     private ZoomManager mZoomManager;
 
-    private Rect mGLRectViewport = new Rect();
+    private final Rect mGLRectViewport = new Rect();
+    private final Rect mViewRectViewport = new Rect();
     private boolean mGLViewportEmpty = false;
 
     /**
@@ -847,6 +848,9 @@
     private Rect mScrollingLayerBounds = new Rect();
     private boolean mSentAutoScrollMessage = false;
 
+    // used for serializing asynchronously handled touch events.
+    private final TouchEventQueue mTouchEventQueue = new TouchEventQueue();
+
     // Used to notify listeners of a new picture.
     private PictureListener mPictureListener;
     /**
@@ -4112,7 +4116,7 @@
         }
         if (animateZoom) {
             mZoomManager.animateZoom(canvas);
-        } else {
+        } else if (!canvas.isHardwareAccelerated()) {
             canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
         }
 
@@ -4149,7 +4153,7 @@
 
         if (canvas.isHardwareAccelerated()) {
             int functor = nativeGetDrawGLFunction(mGLViewportEmpty ? null : mGLRectViewport,
-                    getScale(), extras);
+                    mGLViewportEmpty ? null : mViewRectViewport, getScale(), extras);
             ((HardwareCanvas) canvas).callDrawGLFunction(functor);
         } else {
             DrawFilter df = null;
@@ -5081,6 +5085,8 @@
         }
 
         addAccessibilityApisToJavaScript();
+
+        mTouchEventQueue.reset();
     }
 
     @Override
@@ -5253,6 +5259,7 @@
             // Then need to invert the Y axis, just for GL
             View rootView = getRootView();
             int rootViewHeight = rootView.getHeight();
+            mViewRectViewport.set(mGLRectViewport);
             int savedWebViewBottom = mGLRectViewport.bottom;
             mGLRectViewport.bottom = rootViewHeight - mGLRectViewport.top - getVisibleTitleHeight();
             mGLRectViewport.top = rootViewHeight - savedWebViewBottom;
@@ -5260,7 +5267,8 @@
         } else {
             mGLViewportEmpty = true;
         }
-        nativeUpdateDrawGLFunction(mGLViewportEmpty ? null : mGLRectViewport);
+        nativeUpdateDrawGLFunction(mGLViewportEmpty ? null : mGLRectViewport,
+                mGLViewportEmpty ? null : mViewRectViewport);
     }
 
     /**
@@ -5432,34 +5440,17 @@
                 + " numPointers=" + ev.getPointerCount());
         }
 
-        int action = ev.getActionMasked();
-        if (ev.getPointerCount() > 1) {  // Multi-touch
-            mIsHandlingMultiTouch = true;
-
-            // If WebKit already showed no interests in this sequence of events,
-            // WebView handles them directly.
-            if (mPreventDefault == PREVENT_DEFAULT_NO) {
-                handleMultiTouchInWebView(ev);
-            } else {
-                passMultiTouchToWebKit(ev);
-            }
-            return true;
+        // If WebKit wasn't interested in this multitouch gesture, enqueue
+        // the event for handling directly rather than making the round trip
+        // to WebKit and back.
+        if (ev.getPointerCount() > 1 && mPreventDefault != PREVENT_DEFAULT_NO) {
+            passMultiTouchToWebKit(ev, mTouchEventQueue.nextTouchSequence());
         } else {
-            final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
-            if (detector != null) {
-                // ScaleGestureDetector needs a consistent event stream to operate properly.
-                // It won't take any action with fewer than two pointers, but it needs to
-                // update internal bookkeeping state.
-                detector.onTouchEvent(ev);
-            }
+            mTouchEventQueue.enqueueTouchEvent(ev);
         }
 
-        // Skip ACTION_MOVE for single touch if it's still handling multi-touch.
-        if (mIsHandlingMultiTouch && action == MotionEvent.ACTION_MOVE) {
-            return false;
-        }
-
-        return handleTouchEventCommon(ev, action, Math.round(ev.getX()), Math.round(ev.getY()));
+        // Since all events are handled asynchronously, we always want the gesture stream.
+        return true;
     }
 
     /*
@@ -5579,6 +5570,7 @@
                         ted.mReprocess = mDeferTouchProcess;
                         ted.mNativeLayer = nativeScrollableLayer(
                                 contentX, contentY, ted.mNativeLayerRect, null);
+                        ted.mDontEnqueueResult = true;
                         mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                         if (mDeferTouchProcess) {
                             // still needs to set them for compute deltaX/Y
@@ -5625,6 +5617,7 @@
                     ted.mReprocess = mDeferTouchProcess;
                     ted.mNativeLayer = mScrollingLayer;
                     ted.mNativeLayerRect.set(mScrollingLayerRect);
+                    ted.mDontEnqueueResult = true;
                     mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                     mLastSentTouchTime = eventTime;
                     if (mDeferTouchProcess) {
@@ -5806,6 +5799,7 @@
                     ted.mReprocess = mDeferTouchProcess;
                     ted.mNativeLayer = mScrollingLayer;
                     ted.mNativeLayerRect.set(mScrollingLayerRect);
+                    ted.mDontEnqueueResult = true;
                     mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                 }
                 mLastTouchUpTime = eventTime;
@@ -5828,6 +5822,7 @@
                             ted.mNativeLayer = nativeScrollableLayer(
                                     contentX, contentY,
                                     ted.mNativeLayerRect, null);
+                            ted.mDontEnqueueResult = true;
                             mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                         } else if (mPreventDefault != PREVENT_DEFAULT_YES){
                             mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
@@ -5952,7 +5947,7 @@
         return true;
     }
 
-    private void passMultiTouchToWebKit(MotionEvent ev) {
+    private void passMultiTouchToWebKit(MotionEvent ev, long sequence) {
         TouchEventData ted = new TouchEventData();
         ted.mAction = ev.getActionMasked();
         final int count = ev.getPointerCount();
@@ -5967,6 +5962,7 @@
         ted.mMetaState = ev.getMetaState();
         ted.mReprocess = true;
         ted.mMotionEvent = MotionEvent.obtain(ev);
+        ted.mSequence = sequence;
         mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
         cancelLongPress();
         mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
@@ -6016,7 +6012,7 @@
         if (action == MotionEvent.ACTION_POINTER_DOWN) {
             cancelTouch();
             action = MotionEvent.ACTION_DOWN;
-        } else if (action == MotionEvent.ACTION_POINTER_UP) {
+        } else if (action == MotionEvent.ACTION_POINTER_UP && ev.getPointerCount() == 2) {
             // set mLastTouchX/Y to the remaining point
             mLastTouchX = Math.round(x);
             mLastTouchY = Math.round(y);
@@ -6044,6 +6040,7 @@
             ted.mAction = MotionEvent.ACTION_CANCEL;
             ted.mNativeLayer = nativeScrollableLayer(
                     x, y, ted.mNativeLayerRect, null);
+            ted.mDontEnqueueResult = true;
             mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
             mPreventDefault = PREVENT_DEFAULT_IGNORE;
         }
@@ -7121,6 +7118,297 @@
         return mWebViewCore;
     }
 
+    /**
+     * Used only by TouchEventQueue to store pending touch events.
+     */
+    private static class QueuedTouch {
+        long mSequence;
+        MotionEvent mEvent; // Optional
+        TouchEventData mTed; // Optional
+
+        QueuedTouch mNext;
+
+        public QueuedTouch set(TouchEventData ted) {
+            mSequence = ted.mSequence;
+            mTed = ted;
+            mEvent = null;
+            mNext = null;
+            return this;
+        }
+
+        public QueuedTouch set(MotionEvent ev, long sequence) {
+            mEvent = MotionEvent.obtain(ev);
+            mSequence = sequence;
+            mTed = null;
+            mNext = null;
+            return this;
+        }
+
+        public QueuedTouch add(QueuedTouch other) {
+            if (other.mSequence < mSequence) {
+                other.mNext = this;
+                return other;
+            }
+
+            QueuedTouch insertAt = this;
+            while (insertAt.mNext != null && insertAt.mNext.mSequence < other.mSequence) {
+                insertAt = insertAt.mNext;
+            }
+            other.mNext = insertAt.mNext;
+            insertAt.mNext = other;
+            return this;
+        }
+    }
+
+    /**
+     * WebView handles touch events asynchronously since some events must be passed to WebKit
+     * for potentially slower processing. TouchEventQueue serializes touch events regardless
+     * of which path they take to ensure that no events are ever processed out of order
+     * by WebView.
+     */
+    private class TouchEventQueue {
+        private long mNextTouchSequence = Long.MIN_VALUE + 1;
+        private long mLastHandledTouchSequence = Long.MIN_VALUE;
+        private QueuedTouch mTouchEventQueue;
+        private QueuedTouch mQueuedTouchRecycleBin;
+        private int mQueuedTouchRecycleCount;
+        private static final int MAX_RECYCLED_QUEUED_TOUCH = 15;
+
+        private QueuedTouch obtainQueuedTouch() {
+            if (mQueuedTouchRecycleBin != null) {
+                QueuedTouch result = mQueuedTouchRecycleBin;
+                mQueuedTouchRecycleBin = result.mNext;
+                mQueuedTouchRecycleCount--;
+                return result;
+            }
+            return new QueuedTouch();
+        }
+
+        private void recycleQueuedTouch(QueuedTouch qd) {
+            if (mQueuedTouchRecycleCount < MAX_RECYCLED_QUEUED_TOUCH) {
+                qd.mNext = mQueuedTouchRecycleBin;
+                mQueuedTouchRecycleBin = qd;
+                mQueuedTouchRecycleCount++;
+            }
+        }
+
+        /**
+         * Reset the touch event queue. This will dump any pending events
+         * and reset the sequence numbering.
+         */
+        public void reset() {
+            mNextTouchSequence = Long.MIN_VALUE + 1;
+            mLastHandledTouchSequence = Long.MIN_VALUE;
+            while (mTouchEventQueue != null) {
+                QueuedTouch recycleMe = mTouchEventQueue;
+                mTouchEventQueue = mTouchEventQueue.mNext;
+                recycleQueuedTouch(recycleMe);
+            }
+        }
+
+        /**
+         * Return the next valid sequence number for tagging incoming touch events.
+         * @return The next touch event sequence number
+         */
+        public long nextTouchSequence() {
+            return mNextTouchSequence++;
+        }
+
+        /**
+         * Enqueue a touch event in the form of TouchEventData.
+         * The sequence number will be read from the mSequence field of the argument.
+         *
+         * If the touch event's sequence number is the next in line to be processed, it will
+         * be handled before this method returns. Any subsequent events that have already
+         * been queued will also be processed in their proper order.
+         *
+         * @param ted Touch data to be processed in order.
+         */
+        public void enqueueTouchEvent(TouchEventData ted) {
+            if (mLastHandledTouchSequence + 1 == ted.mSequence) {
+                handleQueuedTouchEventData(ted);
+
+                mLastHandledTouchSequence++;
+
+                // Do we have any more? Run them if so.
+                QueuedTouch qd = mTouchEventQueue;
+                while (qd != null && qd.mSequence == mLastHandledTouchSequence + 1) {
+                    handleQueuedTouch(qd);
+                    QueuedTouch recycleMe = qd;
+                    qd = qd.mNext;
+                    recycleQueuedTouch(recycleMe);
+                    mLastHandledTouchSequence++;
+                }
+                mTouchEventQueue = qd;
+            } else {
+                QueuedTouch qd = obtainQueuedTouch().set(ted);
+                mTouchEventQueue = mTouchEventQueue == null ? qd : mTouchEventQueue.add(qd);
+            }
+        }
+
+        /**
+         * Enqueue a touch event in the form of a MotionEvent from the framework.
+         *
+         * If the touch event's sequence number is the next in line to be processed, it will
+         * be handled before this method returns. Any subsequent events that have already
+         * been queued will also be processed in their proper order.
+         *
+         * @param ev MotionEvent to be processed in order
+         */
+        public void enqueueTouchEvent(MotionEvent ev) {
+            final long sequence = nextTouchSequence();
+            if (mLastHandledTouchSequence + 1 == sequence) {
+                handleQueuedMotionEvent(ev);
+
+                mLastHandledTouchSequence++;
+
+                // Do we have any more? Run them if so.
+                QueuedTouch qd = mTouchEventQueue;
+                while (qd != null && qd.mSequence == mLastHandledTouchSequence + 1) {
+                    handleQueuedTouch(qd);
+                    QueuedTouch recycleMe = qd;
+                    qd = qd.mNext;
+                    recycleQueuedTouch(recycleMe);
+                    mLastHandledTouchSequence++;
+                }
+                mTouchEventQueue = qd;
+            } else {
+                QueuedTouch qd = obtainQueuedTouch().set(ev, sequence);
+                mTouchEventQueue = mTouchEventQueue == null ? qd : mTouchEventQueue.add(qd);
+            }
+        }
+
+        private void handleQueuedTouch(QueuedTouch qt) {
+            if (qt.mTed != null) {
+                handleQueuedTouchEventData(qt.mTed);
+            } else {
+                handleQueuedMotionEvent(qt.mEvent);
+                qt.mEvent.recycle();
+            }
+        }
+
+        private void handleQueuedMotionEvent(MotionEvent ev) {
+            int action = ev.getActionMasked();
+            if (ev.getPointerCount() > 1) {  // Multi-touch
+                mIsHandlingMultiTouch = true;
+
+                handleMultiTouchInWebView(ev);
+            } else {
+                final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+                if (detector != null) {
+                    // ScaleGestureDetector needs a consistent event stream to operate properly.
+                    // It won't take any action with fewer than two pointers, but it needs to
+                    // update internal bookkeeping state.
+                    detector.onTouchEvent(ev);
+                }
+
+                handleTouchEventCommon(ev, action, Math.round(ev.getX()), Math.round(ev.getY()));
+            }
+        }
+
+        private void handleQueuedTouchEventData(TouchEventData ted) {
+            if (!ted.mReprocess) {
+                if (ted.mAction == MotionEvent.ACTION_DOWN
+                        && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES) {
+                    // if prevent default is called from WebCore, UI
+                    // will not handle the rest of the touch events any
+                    // more.
+                    mPreventDefault = ted.mNativeResult ? PREVENT_DEFAULT_YES
+                            : PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN;
+                } else if (ted.mAction == MotionEvent.ACTION_MOVE
+                        && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) {
+                    // the return for the first ACTION_MOVE will decide
+                    // whether UI will handle touch or not. Currently no
+                    // support for alternating prevent default
+                    mPreventDefault = ted.mNativeResult ? PREVENT_DEFAULT_YES
+                            : PREVENT_DEFAULT_NO;
+                }
+                if (mPreventDefault == PREVENT_DEFAULT_YES) {
+                    mTouchHighlightRegion.setEmpty();
+                }
+            } else {
+                if (ted.mPoints.length > 1) {  // multi-touch
+                    if (ted.mAction == MotionEvent.ACTION_POINTER_UP &&
+                            ted.mMotionEvent.getPointerCount() == 2) {
+                        mIsHandlingMultiTouch = false;
+                    }
+                    if (!ted.mNativeResult) {
+                        mPreventDefault = PREVENT_DEFAULT_NO;
+                        handleMultiTouchInWebView(ted.mMotionEvent);
+                    } else {
+                        mPreventDefault = PREVENT_DEFAULT_YES;
+                    }
+                    return;
+                }
+
+                // prevent default is not called in WebCore, so the
+                // message needs to be reprocessed in UI
+                if (!ted.mNativeResult) {
+                    // Following is for single touch.
+                    switch (ted.mAction) {
+                        case MotionEvent.ACTION_DOWN:
+                            mLastDeferTouchX = contentToViewX(ted.mPoints[0].x)
+                                    - mScrollX;
+                            mLastDeferTouchY = contentToViewY(ted.mPoints[0].y)
+                                    - mScrollY;
+                            mDeferTouchMode = TOUCH_INIT_MODE;
+                            break;
+                        case MotionEvent.ACTION_MOVE: {
+                            // no snapping in defer process
+                            int x = contentToViewX(ted.mPoints[0].x) - mScrollX;
+                            int y = contentToViewY(ted.mPoints[0].y) - mScrollY;
+                            if (mDeferTouchMode != TOUCH_DRAG_MODE) {
+                                mDeferTouchMode = TOUCH_DRAG_MODE;
+                                mLastDeferTouchX = x;
+                                mLastDeferTouchY = y;
+                                startScrollingLayer(x, y);
+                                startDrag();
+                            }
+                            int deltaX = pinLocX((int) (mScrollX
+                                    + mLastDeferTouchX - x))
+                                    - mScrollX;
+                            int deltaY = pinLocY((int) (mScrollY
+                                    + mLastDeferTouchY - y))
+                                    - mScrollY;
+                            doDrag(deltaX, deltaY);
+                            if (deltaX != 0) mLastDeferTouchX = x;
+                            if (deltaY != 0) mLastDeferTouchY = y;
+                            break;
+                        }
+                        case MotionEvent.ACTION_UP:
+                        case MotionEvent.ACTION_CANCEL:
+                            if (mDeferTouchMode == TOUCH_DRAG_MODE) {
+                                // no fling in defer process
+                                mScroller.springBack(mScrollX, mScrollY, 0,
+                                        computeMaxScrollX(), 0,
+                                        computeMaxScrollY());
+                                invalidate();
+                                WebViewCore.resumePriority();
+                                WebViewCore.resumeUpdatePicture(mWebViewCore);
+                            }
+                            mDeferTouchMode = TOUCH_DONE_MODE;
+                            break;
+                        case WebViewCore.ACTION_DOUBLETAP:
+                            // doDoubleTap() needs mLastTouchX/Y as anchor
+                            mLastTouchX = contentToViewX(ted.mPoints[0].x) - mScrollX;
+                            mLastTouchY = contentToViewY(ted.mPoints[0].y) - mScrollY;
+                            mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
+                            mDeferTouchMode = TOUCH_DONE_MODE;
+                            break;
+                        case WebViewCore.ACTION_LONGPRESS:
+                            HitTestResult hitTest = getHitTestResult();
+                            if (hitTest != null && hitTest.mType
+                                    != HitTestResult.UNKNOWN_TYPE) {
+                                performLongClick();
+                            }
+                            mDeferTouchMode = TOUCH_DONE_MODE;
+                            break;
+                    }
+                }
+            }
+        }
+    }
+
     //-------------------------------------------------------------------------
     // Methods can be called from a separate thread, like WebViewCore
     // If it needs to call the View system, it has to send message.
@@ -7236,6 +7524,7 @@
                         ted.mNativeLayer = nativeScrollableLayer(
                                 ted.mPoints[0].x, ted.mPoints[0].y,
                                 ted.mNativeLayerRect, null);
+                        ted.mDontEnqueueResult = true;
                         mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
                     } else if (mPreventDefault != PREVENT_DEFAULT_YES) {
                         mTouchMode = TOUCH_DONE_MODE;
@@ -7450,105 +7739,9 @@
                     if (inFullScreenMode()) {
                         break;
                     }
-                    if (msg.obj == null) {
-                        if (msg.arg1 == MotionEvent.ACTION_DOWN
-                                && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES) {
-                            // if prevent default is called from WebCore, UI
-                            // will not handle the rest of the touch events any
-                            // more.
-                            mPreventDefault = msg.arg2 == 1 ? PREVENT_DEFAULT_YES
-                                    : PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN;
-                        } else if (msg.arg1 == MotionEvent.ACTION_MOVE
-                                && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) {
-                            // the return for the first ACTION_MOVE will decide
-                            // whether UI will handle touch or not. Currently no
-                            // support for alternating prevent default
-                            mPreventDefault = msg.arg2 == 1 ? PREVENT_DEFAULT_YES
-                                    : PREVENT_DEFAULT_NO;
-                        }
-                        if (mPreventDefault == PREVENT_DEFAULT_YES) {
-                            mTouchHighlightRegion.setEmpty();
-                        }
-                    } else {
-                        TouchEventData ted = (TouchEventData) msg.obj;
-
-                        if (ted.mPoints.length > 1) {  // multi-touch
-                            if (ted.mAction == MotionEvent.ACTION_POINTER_UP) {
-                                mIsHandlingMultiTouch = false;
-                            }
-                            if (msg.arg2 == 0) {
-                                mPreventDefault = PREVENT_DEFAULT_NO;
-                                handleMultiTouchInWebView(ted.mMotionEvent);
-                            } else {
-                                mPreventDefault = PREVENT_DEFAULT_YES;
-                            }
-                            break;
-                        }
-
-                        // prevent default is not called in WebCore, so the
-                        // message needs to be reprocessed in UI
-                        if (msg.arg2 == 0) {
-                            // Following is for single touch.
-                            switch (ted.mAction) {
-                                case MotionEvent.ACTION_DOWN:
-                                    mLastDeferTouchX = contentToViewX(ted.mPoints[0].x)
-                                            - mScrollX;
-                                    mLastDeferTouchY = contentToViewY(ted.mPoints[0].y)
-                                            - mScrollY;
-                                    mDeferTouchMode = TOUCH_INIT_MODE;
-                                    break;
-                                case MotionEvent.ACTION_MOVE: {
-                                    // no snapping in defer process
-                                    int x = contentToViewX(ted.mPoints[0].x) - mScrollX;
-                                    int y = contentToViewY(ted.mPoints[0].y) - mScrollY;
-                                    if (mDeferTouchMode != TOUCH_DRAG_MODE) {
-                                        mDeferTouchMode = TOUCH_DRAG_MODE;
-                                        mLastDeferTouchX = x;
-                                        mLastDeferTouchY = y;
-                                        startScrollingLayer(x, y);
-                                        startDrag();
-                                    }
-                                    int deltaX = pinLocX((int) (mScrollX
-                                            + mLastDeferTouchX - x))
-                                            - mScrollX;
-                                    int deltaY = pinLocY((int) (mScrollY
-                                            + mLastDeferTouchY - y))
-                                            - mScrollY;
-                                    doDrag(deltaX, deltaY);
-                                    if (deltaX != 0) mLastDeferTouchX = x;
-                                    if (deltaY != 0) mLastDeferTouchY = y;
-                                    break;
-                                }
-                                case MotionEvent.ACTION_UP:
-                                case MotionEvent.ACTION_CANCEL:
-                                    if (mDeferTouchMode == TOUCH_DRAG_MODE) {
-                                        // no fling in defer process
-                                        mScroller.springBack(mScrollX, mScrollY, 0,
-                                                computeMaxScrollX(), 0,
-                                                computeMaxScrollY());
-                                        invalidate();
-                                        WebViewCore.resumePriority();
-                                        WebViewCore.resumeUpdatePicture(mWebViewCore);
-                                    }
-                                    mDeferTouchMode = TOUCH_DONE_MODE;
-                                    break;
-                                case WebViewCore.ACTION_DOUBLETAP:
-                                    // doDoubleTap() needs mLastTouchX/Y as anchor
-                                    mLastTouchX = contentToViewX(ted.mPoints[0].x) - mScrollX;
-                                    mLastTouchY = contentToViewY(ted.mPoints[0].y) - mScrollY;
-                                    mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
-                                    mDeferTouchMode = TOUCH_DONE_MODE;
-                                    break;
-                                case WebViewCore.ACTION_LONGPRESS:
-                                    HitTestResult hitTest = getHitTestResult();
-                                    if (hitTest != null && hitTest.mType
-                                            != HitTestResult.UNKNOWN_TYPE) {
-                                        performLongClick();
-                                    }
-                                    mDeferTouchMode = TOUCH_DONE_MODE;
-                                    break;
-                            }
-                        }
+                    TouchEventData ted = (TouchEventData) msg.obj;
+                    if (!ted.mDontEnqueueResult) {
+                        mTouchEventQueue.enqueueTouchEvent(ted);
                     }
                     break;
 
@@ -8341,8 +8534,9 @@
             boolean splitIfNeeded);
     private native void     nativeDumpDisplayTree(String urlOrNull);
     private native boolean  nativeEvaluateLayersAnimations();
-    private native int      nativeGetDrawGLFunction(Rect rect, float scale, int extras);
-    private native void     nativeUpdateDrawGLFunction(Rect rect);
+    private native int      nativeGetDrawGLFunction(Rect rect, Rect viewRect,
+            float scale, int extras);
+    private native void     nativeUpdateDrawGLFunction(Rect rect, Rect viewRect);
     private native boolean  nativeDrawGL(Rect rect, float scale, int extras);
     private native void     nativeExtendSelection(int x, int y);
     private native int      nativeFindAll(String findLower, String findUpper,
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index c18c9e4..f367b93 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -825,6 +825,9 @@
         MotionEvent mMotionEvent;
         int mNativeLayer;
         Rect mNativeLayerRect = new Rect();
+        long mSequence;
+        boolean mNativeResult;
+        boolean mDontEnqueueResult;
     }
 
     static class GeolocationPermissionsData {
@@ -1332,13 +1335,14 @@
                                 nativeScrollLayer(ted.mNativeLayer,
                                         ted.mNativeLayerRect);
                             }
+                            ted.mNativeResult = nativeHandleTouchEvent(ted.mAction, ted.mIds,
+                                    xArray, yArray, count, ted.mMetaState);
                             Message.obtain(
                                     mWebView.mPrivateHandler,
                                     WebView.PREVENT_TOUCH_ID,
                                     ted.mAction,
-                                    nativeHandleTouchEvent(ted.mAction, ted.mIds,
-                                        xArray, yArray, count, ted.mMetaState) ? 1 : 0,
-                                    ted.mReprocess ? ted : null).sendToTarget();
+                                    ted.mNativeResult ? 1 : 0,
+                                    ted).sendToTarget();
                             break;
                         }
 
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 427126b..af954c9 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -3019,12 +3019,13 @@
                 hasOpaqueScrollbars()) || super.isOpaque();
         if (retValue) {
             // only return true if the list items cover the entire area of the view
-            final int listTop = mListPadding.top;
+            final int listTop = mListPadding != null ? mListPadding.top : mPaddingTop;
             View first = getChildAt(0);
             if (first == null || first.getTop() > listTop) {
                 return false;
             }
-            final int listBottom = getHeight() - mListPadding.bottom;
+            final int listBottom = getHeight() -
+                    (mListPadding != null ? mListPadding.bottom : mPaddingBottom);
             View last = getChildAt(getChildCount() - 1);
             if (last == null || last.getBottom() < listBottom) {
                 return false;
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 2d2165b..1c0a2bb 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -889,7 +889,7 @@
             boolean isConnected = mServiceConnection.isConnected();
             boolean hasNewItems = false;
 
-            if (!isConnected) {
+            if (!isInCache && !isConnected) {
                 // Requesting bind service will trigger a super.notifyDataSetChanged(), which will
                 // in turn trigger another request to getView()
                 requestBindService();
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index e3a66c5..a41b348 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -385,7 +385,7 @@
 
             if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
                 mHomeAsUpView.setVisibility((options & ActionBar.DISPLAY_HOME_AS_UP) != 0
-                        ? VISIBLE : INVISIBLE);
+                        ? VISIBLE : GONE);
             }
 
             if ((flagsChanged & ActionBar.DISPLAY_USE_LOGO) != 0) {
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 7a609a5..a78f660 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -35,7 +35,6 @@
 
 #include <DisplayListRenderer.h>
 #include <LayerRenderer.h>
-#include <OpenGLDebugRenderer.h>
 #include <OpenGLRenderer.h>
 #include <SkiaShader.h>
 #include <SkiaColorFilter.h>
@@ -60,7 +59,6 @@
 
 // Debug
 #define DEBUG_RENDERER 0
-#define PROFILE_RENDERER 0
 
 // Debug
 #if DEBUG_RENDERER
@@ -99,11 +97,7 @@
 
 static OpenGLRenderer* android_view_GLES20Canvas_createRenderer(JNIEnv* env, jobject clazz) {
     RENDERER_LOGD("Create OpenGLRenderer");
-#if PROFILE_RENDERER
-    return new OpenGLDebugRenderer;
-#else
     return new OpenGLRenderer;
-#endif
 }
 
 static void android_view_GLES20Canvas_destroyRenderer(JNIEnv* env, jobject clazz,
@@ -139,7 +133,8 @@
 
 static bool android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
         OpenGLRenderer* renderer, Functor *functor) {
-    return renderer->callDrawGLFunction(functor);
+    android::uirenderer::Rect dirty;
+    return renderer->callDrawGLFunction(functor, dirty);
 }
 
 // ----------------------------------------------------------------------------
@@ -503,8 +498,14 @@
 }
 
 static bool android_view_GLES20Canvas_drawDisplayList(JNIEnv* env,
-        jobject clazz, OpenGLRenderer* renderer, DisplayList* displayList) {
-    return renderer->drawDisplayList(displayList);
+        jobject clazz, OpenGLRenderer* renderer, DisplayList* displayList, jobject dirty) {
+    android::uirenderer::Rect bounds;
+    bool redraw = renderer->drawDisplayList(displayList, bounds);
+    if (redraw && dirty != NULL) {
+        env->CallVoidMethod(dirty, gRectClassInfo.set,
+                int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom));
+    }
+    return redraw;
 }
 
 // ----------------------------------------------------------------------------
@@ -663,7 +664,8 @@
     { "nGetDisplayList",         "(I)I",       (void*) android_view_GLES20Canvas_getDisplayList },
     { "nDestroyDisplayList",     "(I)V",       (void*) android_view_GLES20Canvas_destroyDisplayList },
     { "nGetDisplayListRenderer", "(I)I",       (void*) android_view_GLES20Canvas_getDisplayListRenderer },
-    { "nDrawDisplayList",        "(II)Z",      (void*) android_view_GLES20Canvas_drawDisplayList },
+    { "nDrawDisplayList",        "(IILandroid/graphics/Rect;)Z",
+                                               (void*) android_view_GLES20Canvas_drawDisplayList },
 
     { "nInterrupt",              "(I)V",       (void*) android_view_GLES20Canvas_interrupt },
     { "nResume",                 "(I)V",       (void*) android_view_GLES20Canvas_resume },
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index e7ea8c8..3d24bee 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -400,7 +400,7 @@
 }
 
 static jint jni_eglGetCurrentSurface(JNIEnv *_env, jobject _this, jint readdraw) {
-    if (!(readdraw == EGL_READ) || (readdraw == EGL_DRAW)) {
+    if ((readdraw != EGL_READ) && (readdraw != EGL_DRAW)) {
         doThrow(_env, "java/lang/IllegalArgumentException");
         return 0;
     }
diff --git a/core/res/res/drawable-hdpi/btn_default_disabled_holo.9.png b/core/res/res/drawable-hdpi/btn_default_disabled_holo.9.png
new file mode 100644
index 0000000..6840962
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_disabled_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_focused_holo.9.png b/core/res/res/drawable-hdpi/btn_default_focused_holo.9.png
new file mode 100644
index 0000000..b3196c3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_holo.9.png b/core/res/res/drawable-hdpi/btn_default_normal_holo.9.png
new file mode 100644
index 0000000..c44ae7a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_normal_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_pressed_holo.9.png b/core/res/res/drawable-hdpi/btn_default_pressed_holo.9.png
new file mode 100644
index 0000000..e240a2d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_ab_back_holo_dark.png b/core/res/res/drawable-hdpi/ic_ab_back_holo_dark.png
index 52fccf8..7855cda 100644
--- a/core/res/res/drawable-hdpi/ic_ab_back_holo_dark.png
+++ b/core/res/res/drawable-hdpi/ic_ab_back_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_ab_back_holo_light.png b/core/res/res/drawable-hdpi/ic_ab_back_holo_light.png
index 0354599..c062773 100644
--- a/core/res/res/drawable-hdpi/ic_ab_back_holo_light.png
+++ b/core/res/res/drawable-hdpi/ic_ab_back_holo_light.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_disabled_holo.9.png b/core/res/res/drawable-mdpi/btn_default_disabled_holo.9.png
new file mode 100644
index 0000000..9a24b9c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_disabled_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_focused_holo.9.png b/core/res/res/drawable-mdpi/btn_default_focused_holo.9.png
new file mode 100644
index 0000000..93d9741
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_focused_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_normal_holo.9.png b/core/res/res/drawable-mdpi/btn_default_normal_holo.9.png
new file mode 100644
index 0000000..f462d98
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_normal_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_default_pressed_holo.9.png b/core/res/res/drawable-mdpi/btn_default_pressed_holo.9.png
new file mode 100644
index 0000000..30373a6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_default_pressed_holo.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_ab_back_holo_dark.png b/core/res/res/drawable-mdpi/ic_ab_back_holo_dark.png
index 959b4e4..ae3e6bf 100644
--- a/core/res/res/drawable-mdpi/ic_ab_back_holo_dark.png
+++ b/core/res/res/drawable-mdpi/ic_ab_back_holo_dark.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_ab_back_holo_light.png b/core/res/res/drawable-mdpi/ic_ab_back_holo_light.png
index 41333b8..c61e3fa 100644
--- a/core/res/res/drawable-mdpi/ic_ab_back_holo_light.png
+++ b/core/res/res/drawable-mdpi/ic_ab_back_holo_light.png
Binary files differ
diff --git a/core/res/res/layout/action_bar_home.xml b/core/res/res/layout/action_bar_home.xml
index e8b5637..7867577 100644
--- a/core/res/res/layout/action_bar_home.xml
+++ b/core/res/res/layout/action_bar_home.xml
@@ -14,16 +14,18 @@
      limitations under the License.
 -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-             android:layout_width="wrap_content"
-             android:layout_height="match_parent"
-             android:background="?android:attr/selectableItemBackground">
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="wrap_content"
+              android:layout_height="match_parent"
+              android:background="?android:attr/selectableItemBackground"
+              android:orientation="horizontal">
     <ImageView android:id="@android:id/up"
                android:src="?android:attr/homeAsUpIndicator"
                android:layout_gravity="top|left"
-               android:visibility="invisible"
+               android:visibility="gone"
                android:layout_width="wrap_content"
-               android:layout_height="wrap_content" />
+               android:layout_height="wrap_content"
+               android:layout_marginRight="-12dip" />
     <ImageView android:id="@android:id/home"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
@@ -31,4 +33,4 @@
                android:paddingRight="16dip"
                android:layout_gravity="center"
                android:scaleType="center" />
-</FrameLayout>
+</LinearLayout>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 066aae9..af0074b 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -828,7 +828,7 @@
     <string name="anr_application_process" msgid="4185842666452210193">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması (<xliff:g id="PROCESS">%2$s</xliff:g> işleminde) yanıt vermiyor."</string>
     <string name="anr_process" msgid="1246866008169975783">"<xliff:g id="PROCESS">%1$s</xliff:g> işlemi yanıt vermiyor."</string>
     <string name="force_close" msgid="3653416315450806396">"Kapanmaya zorla"</string>
-    <string name="report" msgid="4060218260984795706">"Rapor"</string>
+    <string name="report" msgid="4060218260984795706">"Bildir"</string>
     <string name="wait" msgid="7147118217226317732">"Bekle"</string>
     <string name="launch_warning_title" msgid="8323761616052121936">"Uygulama yönlendirildi"</string>
     <string name="launch_warning_replace" msgid="6202498949970281412">"<xliff:g id="APP_NAME">%1$s</xliff:g> ÅŸimdi çalışıyor."</string>
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 6d925d6..1428b63 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -409,6 +409,10 @@
 axis 0x03 RX
 axis 0x04 RY
 axis 0x05 RZ
+axis 0x06 THROTTLE
+axis 0x07 RUDDER
+axis 0x08 WHEEL
+axis 0x09 GAS
+axis 0x0a BRAKE
 axis 0x10 HAT_X
 axis 0x11 HAT_Y
- 
\ No newline at end of file
diff --git a/data/keyboards/Vendor_045e_Product_028e.kl b/data/keyboards/Vendor_045e_Product_028e.kl
new file mode 100644
index 0000000..99f046a
--- /dev/null
+++ b/data/keyboards/Vendor_045e_Product_028e.kl
@@ -0,0 +1,46 @@
+# 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.
+
+#
+# XBox 360 USB Controller
+#
+
+key 304   BUTTON_A
+key 305   BUTTON_B
+key 307   BUTTON_X
+key 308   BUTTON_Y
+key 310   BUTTON_L1
+key 311   BUTTON_R1
+key 314   BUTTON_SELECT
+key 315   BUTTON_START
+key 316   BUTTON_MODE
+key 317   BUTTON_THUMBL
+key 318   BUTTON_THUMBR
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
diff --git a/data/keyboards/Vendor_046d_Product_c294.kl b/data/keyboards/Vendor_046d_Product_c294.kl
new file mode 100644
index 0000000..5492f49
--- /dev/null
+++ b/data/keyboards/Vendor_046d_Product_c294.kl
@@ -0,0 +1,53 @@
+# 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.
+
+#
+# Logitech G25 Racing Wheel (in Compatibility Mode)
+#
+
+# 4 way buttons above hat
+key 0x121    BUTTON_A
+key 0x123    BUTTON_B
+key 0x120    BUTTON_X
+key 0x122    BUTTON_Y
+
+# Row of buttons under hat
+key 0x12b    BUTTON_1
+key 0x128    BUTTON_2
+key 0x129    BUTTON_3
+key 0x12a    BUTTON_4
+
+# Gear shift positions
+#   0x12a top-left gear (aliased as BUTTON_4)
+#   0x12b bottom-left gear (aliased as BUTTON_1)
+
+# Buttons on wheel
+key 0x127    BUTTON_L1
+key 0x126    BUTTON_R1
+
+# Toggles under wheel
+key 0x125    BUTTON_L2
+key 0x124    BUTTON_R2
+
+# Hat
+axis 0x10    HAT_X
+axis 0x11    HAT_Y
+
+# Steering Wheel
+axis 0x00    WHEEL
+
+# Accelerator / Brake
+#   00..7e : accelerator
+#   80..ff : brake
+axis 0x01    split 0x7f GAS BRAKE
diff --git a/data/keyboards/Vendor_046d_Product_c299.kl b/data/keyboards/Vendor_046d_Product_c299.kl
new file mode 100644
index 0000000..d42963d
--- /dev/null
+++ b/data/keyboards/Vendor_046d_Product_c299.kl
@@ -0,0 +1,62 @@
+# 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.
+
+#
+# Logitech G25 Racing Wheel (in Native Mode)
+#
+
+# 4 way buttons above hat
+key 0x121    BUTTON_A
+key 0x123    BUTTON_B
+key 0x120    BUTTON_X
+key 0x122    BUTTON_Y
+
+# Row of buttons under hat
+key 0x12b    BUTTON_1
+key 0x128    BUTTON_2
+key 0x129    BUTTON_3
+key 0x12a    BUTTON_4
+
+# Gear shift positions
+key 0x12c    BUTTON_5
+key 0x12d    BUTTON_6
+key 0x12e    BUTTON_7
+key 0x12f    BUTTON_8
+key 0x2d0    BUTTON_9
+key 0x2d1    BUTTON_10
+key 0x2d2    BUTTON_11
+
+# Buttons on wheel
+key 0x127    BUTTON_L1
+key 0x126    BUTTON_R1
+
+# Toggles under wheel
+key 0x125    BUTTON_L2
+key 0x124    BUTTON_R2
+
+# Hat
+axis 0x10    HAT_X
+axis 0x11    HAT_Y
+
+# Steering Wheel
+axis 0x00    WHEEL
+
+# Clutch
+axis 0x01    invert GENERIC_1
+
+# Accelerator
+axis 0x02    invert GAS
+
+# Brake
+axis 0x05    invert BRAKE
diff --git a/data/keyboards/common.mk b/data/keyboards/common.mk
index 5b367b9..335298c 100644
--- a/data/keyboards/common.mk
+++ b/data/keyboards/common.mk
@@ -19,7 +19,10 @@
     Generic.kl \
     AVRCP.kl \
     qwerty.kl \
+    Vendor_045e_Product_028e.kl \
     Vendor_046d_Product_c216.kl \
+    Vendor_046d_Product_c294.kl \
+    Vendor_046d_Product_c299.kl \
     Vendor_046d_Product_c532.kl \
     Vendor_054c_Product_0268.kl \
     Vendor_05ac_Product_0239.kl \
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index c541456..782ffdb 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -735,6 +735,7 @@
      * This method expects uri in the following format
      *     content://media/<table_name>/<row_index> (or)
      *     file://sdcard/test.mp4
+     *     http://test.com/test.mp4
      *
      * Here <table_name> shall be "video" or "audio" or "images"
      * <row_index> the index of the content in given table
@@ -746,6 +747,10 @@
             if (null == scheme || scheme.equals("") ||
                     scheme.equals(ContentResolver.SCHEME_FILE)) {
                 path = uri.getPath();
+
+            } else if (scheme.equals("http")) {
+                path = uri.toString();
+
             } else if (scheme.equals(ContentResolver.SCHEME_CONTENT)) {
                 String[] projection = new String[] {MediaStore.MediaColumns.DATA};
                 Cursor cursor = null;
diff --git a/include/ui/KeyLayoutMap.h b/include/ui/KeyLayoutMap.h
index 904c8f3..d82d0c8 100644
--- a/include/ui/KeyLayoutMap.h
+++ b/include/ui/KeyLayoutMap.h
@@ -24,6 +24,36 @@
 
 namespace android {
 
+struct AxisInfo {
+    enum Mode {
+        // Axis value is reported directly.
+        MODE_NORMAL = 0,
+        // Axis value should be inverted before reporting.
+        MODE_INVERT = 1,
+        // Axis value should be split into two axes
+        MODE_SPLIT = 2,
+    };
+
+    // Axis mode.
+    Mode mode;
+
+    // Axis id.
+    // When split, this is the axis used for values smaller than the split position.
+    int32_t axis;
+
+    // When split, this is the axis used for values after higher than the split position.
+    int32_t highAxis;
+
+    // The split value, or 0 if not split.
+    int32_t splitValue;
+
+    // The flat value, or -1 if none.
+    int32_t flatOverride;
+
+    AxisInfo() : mode(MODE_NORMAL), axis(-1), highAxis(-1), splitValue(0), flatOverride(-1) {
+    }
+};
+
 /**
  * Describes a mapping from keyboard scan codes and joystick axes to Android key codes and axes.
  */
@@ -36,7 +66,7 @@
     status_t mapKey(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const;
     status_t findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const;
 
-    status_t mapAxis(int32_t scanCode, int32_t* axis) const;
+    status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const;
 
 private:
     struct Key {
@@ -45,7 +75,7 @@
     };
 
     KeyedVector<int32_t, Key> mKeys;
-    KeyedVector<int32_t, int32_t> mAxes;
+    KeyedVector<int32_t, AxisInfo> mAxes;
 
     KeyLayoutMap();
 
diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h
index bdfbf7c..b912e9b 100755
--- a/include/ui/KeycodeLabels.h
+++ b/include/ui/KeycodeLabels.h
@@ -270,6 +270,11 @@
     { "HAT_Y", 16 },
     { "LTRIGGER", 17 },
     { "RTRIGGER", 18 },
+    { "THROTTLE", 19 },
+    { "RUDDER", 20 },
+    { "WHEEL", 21 },
+    { "GAS", 22 },
+    { "BRAKE", 23 },
     { "GENERIC_1", 32 },
     { "GENERIC_2", 33 },
     { "GENERIC_3", 34 },
diff --git a/include/utils/Functor.h b/include/utils/Functor.h
index 3955bc3..565f4a3 100644
--- a/include/utils/Functor.h
+++ b/include/utils/Functor.h
@@ -26,6 +26,7 @@
     Functor() {}
     virtual ~Functor() {}
     virtual status_t operator ()() { return true; }
+    virtual status_t operator ()(float* data, uint32_t len) { return true; }
 };
 
 }; // namespace android
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 38e0848..f4a0161 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -15,7 +15,6 @@
 		LayerCache.cpp \
 		LayerRenderer.cpp \
 		Matrix.cpp \
-		OpenGLDebugRenderer.cpp \
 		OpenGLRenderer.cpp \
 		Patch.cpp \
 		PatchCache.cpp \
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index d5d2ba0..737fa02 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -166,7 +166,7 @@
 void DisplayList::init() {
 }
 
-bool DisplayList::replay(OpenGLRenderer& renderer, uint32_t level) {
+bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) {
     bool needsInvalidate = false;
     TextContainer text;
     mReader.rewind();
@@ -189,7 +189,7 @@
             case DrawGLFunction: {
                 Functor *functor = (Functor *) getInt();
                 DISPLAY_LIST_LOGD("%s%s %p", (char*) indent, OP_NAMES[op], functor);
-                needsInvalidate |= renderer.callDrawGLFunction(functor);
+                needsInvalidate |= renderer.callDrawGLFunction(functor, dirty);
             }
             break;
             case Save: {
@@ -287,7 +287,7 @@
                 DisplayList* displayList = getDisplayList();
                 DISPLAY_LIST_LOGD("%s%s %p, %d", (char*) indent, OP_NAMES[op],
                     displayList, level + 1);
-                needsInvalidate |= renderer.drawDisplayList(displayList, level + 1);
+                needsInvalidate |= renderer.drawDisplayList(displayList, dirty, level + 1);
             }
             break;
             case DrawLayer: {
@@ -589,7 +589,8 @@
 void DisplayListRenderer::resume() {
 }
 
-bool DisplayListRenderer::callDrawGLFunction(Functor *functor) {
+bool DisplayListRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) {
+    // Ignore dirty during recording, it matters only when we replay
     addOp(DisplayList::DrawGLFunction);
     addInt((int) functor);
     return false; // No invalidate needed at record-time
@@ -673,7 +674,9 @@
     return OpenGLRenderer::clipRect(left, top, right, bottom, op);
 }
 
-bool DisplayListRenderer::drawDisplayList(DisplayList* displayList, uint32_t level) {
+bool DisplayListRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, uint32_t level) {
+    // dirty is an out parameter and should not be recorded,
+    // it matters only when replaying the display list
     addOp(DisplayList::DrawDisplayList);
     addDisplayList(displayList);
     return false;
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index e8f189d..f24545d 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -103,7 +103,7 @@
 
     void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false);
 
-    bool replay(OpenGLRenderer& renderer, uint32_t level = 0);
+    bool replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level = 0);
 
 private:
     void init();
@@ -214,7 +214,7 @@
     void prepareDirty(float left, float top, float right, float bottom, bool opaque);
     void finish();
 
-    bool callDrawGLFunction(Functor *functor);
+    bool callDrawGLFunction(Functor *functor, Rect& dirty);
 
     void interrupt();
     void resume();
@@ -238,7 +238,7 @@
 
     bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
 
-    bool drawDisplayList(DisplayList* displayList, uint32_t level = 0);
+    bool drawDisplayList(DisplayList* displayList, Rect& dirty, uint32_t level = 0);
     void drawLayer(Layer* layer, float x, float y, SkPaint* paint);
     void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
     void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
diff --git a/libs/hwui/OpenGLDebugRenderer.cpp b/libs/hwui/OpenGLDebugRenderer.cpp
deleted file mode 100644
index 05870bb..0000000
--- a/libs/hwui/OpenGLDebugRenderer.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "OpenGLRenderer"
-
-#include <utils/StopWatch.h>
-
-#include "OpenGLDebugRenderer.h"
-
-namespace android {
-namespace uirenderer {
-
-void OpenGLDebugRenderer::prepareDirty(float left, float top,
-        float right, float bottom, bool opaque) {
-    mPrimitivesCount = 0;
-    LOGD("========= Frame start =========");
-    OpenGLRenderer::prepareDirty(left, top, right, bottom, opaque);
-}
-
-void OpenGLDebugRenderer::finish() {
-    LOGD("========= Frame end =========");
-    LOGD("Primitives draw count = %d", mPrimitivesCount);
-    OpenGLRenderer::finish();
-}
-
-void OpenGLDebugRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
-    mPrimitivesCount++;
-    StopWatch w("composeLayer");
-    return OpenGLRenderer::composeLayer(current, previous);
-}
-
-int OpenGLDebugRenderer::saveLayer(float left, float top, float right, float bottom,
-        SkPaint* p, int flags) {
-    mPrimitivesCount++;
-    StopWatch w("saveLayer");
-    return OpenGLRenderer::saveLayer(left, top, right, bottom, p, flags);
-}
-
-bool OpenGLDebugRenderer::drawDisplayList(DisplayList* displayList, uint32_t level) {
-    mPrimitivesCount++;
-    StopWatch w("drawDisplayList");
-    return OpenGLRenderer::drawDisplayList(displayList);
-}
-
-void OpenGLDebugRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) {
-    mPrimitivesCount++;
-    StopWatch w("drawLayer");
-    OpenGLRenderer::drawLayer(layer, x, y, paint);
-}
-
-void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, float left, float top,
-        SkPaint* paint) {
-    mPrimitivesCount++;
-    StopWatch w("drawBitmap");
-    OpenGLRenderer::drawBitmap(bitmap, left, top, paint);
-}
-
-void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix,
-        SkPaint* paint) {
-    mPrimitivesCount++;
-    StopWatch w("drawBitmapMatrix");
-    OpenGLRenderer::drawBitmap(bitmap, matrix, paint);
-}
-
-void OpenGLDebugRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
-        float srcRight, float srcBottom, float dstLeft, float dstTop,
-        float dstRight, float dstBottom, SkPaint* paint) {
-    mPrimitivesCount++;
-    StopWatch w("drawBitmapRect");
-    OpenGLRenderer::drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
-            dstLeft, dstTop, dstRight, dstBottom, paint);
-}
-
-void OpenGLDebugRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
-        const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
-        float left, float top, float right, float bottom, SkPaint* paint) {
-    mPrimitivesCount++;
-    StopWatch w("drawPatch");
-    OpenGLRenderer::drawPatch(bitmap, xDivs, yDivs, colors, width, height, numColors,
-            left, top, right, bottom, paint);
-}
-
-void OpenGLDebugRenderer::drawColor(int color, SkXfermode::Mode mode) {
-    mPrimitivesCount++;
-    StopWatch w("drawColor");
-    OpenGLRenderer::drawColor(color, mode);
-}
-
-void OpenGLDebugRenderer::drawRect(float left, float top, float right, float bottom,
-        SkPaint* paint) {
-    mPrimitivesCount++;
-    StopWatch w("drawRect");
-    OpenGLRenderer::drawRect(left, top, right, bottom, paint);
-}
-
-void OpenGLDebugRenderer::drawRoundRect(float left, float top, float right, float bottom,
-        float rx, float ry, SkPaint* paint) {
-    mPrimitivesCount++;
-    StopWatch w("drawRoundRect");
-    OpenGLRenderer::drawRoundRect(left, top, right, bottom, rx, ry, paint);
-}
-
-void OpenGLDebugRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) {
-    mPrimitivesCount++;
-    StopWatch w("drawCircle");
-    OpenGLRenderer::drawCircle(x, y, radius, paint);
-}
-
-void OpenGLDebugRenderer::drawOval(float left, float top, float right, float bottom,
-        SkPaint* paint) {
-    mPrimitivesCount++;
-    StopWatch w("drawOval");
-    OpenGLRenderer::drawOval(left, top, right, bottom, paint);
-}
-
-void OpenGLDebugRenderer::drawArc(float left, float top, float right, float bottom,
-        float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) {
-    mPrimitivesCount++;
-    StopWatch w("drawArc");
-    OpenGLRenderer::drawArc(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint);
-}
-
-void OpenGLDebugRenderer::drawPath(SkPath* path, SkPaint* paint) {
-    mPrimitivesCount++;
-    StopWatch w("drawPath");
-    OpenGLRenderer::drawPath(path, paint);
-}
-
-void OpenGLDebugRenderer::drawLines(float* points, int count, SkPaint* paint) {
-    mPrimitivesCount++;
-    StopWatch w("drawLines");
-    OpenGLRenderer::drawLines(points, count, paint);
-}
-
-void OpenGLDebugRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
-        SkPaint* paint) {
-    mPrimitivesCount++;
-    StopWatch w("drawText");
-    OpenGLRenderer::drawText(text, bytesCount, count, x, y, paint);
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/OpenGLDebugRenderer.h b/libs/hwui/OpenGLDebugRenderer.h
deleted file mode 100644
index 1a18a67..0000000
--- a/libs/hwui/OpenGLDebugRenderer.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_HWUI_OPENGL_DEBUG_RENDERER_H
-#define ANDROID_HWUI_OPENGL_DEBUG_RENDERER_H
-
-#include "OpenGLRenderer.h"
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Renderer
-///////////////////////////////////////////////////////////////////////////////
-
-class OpenGLDebugRenderer: public OpenGLRenderer {
-public:
-    OpenGLDebugRenderer(): mPrimitivesCount(0) {
-    }
-
-    ~OpenGLDebugRenderer() {
-    }
-
-    void prepareDirty(float left, float top, float right, float bottom, bool opaque);
-    void finish();
-
-    int saveLayer(float left, float top, float right, float bottom,
-            SkPaint* p, int flags);
-
-    bool drawDisplayList(DisplayList* displayList, uint32_t level = 0);
-    void drawLayer(Layer* layer, float x, float y, SkPaint* paint);
-    void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
-    void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
-    void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
-            float srcRight, float srcBottom, float dstLeft, float dstTop,
-            float dstRight, float dstBottom, SkPaint* paint);
-    void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
-            const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
-            float left, float top, float right, float bottom, SkPaint* paint);
-    void drawColor(int color, SkXfermode::Mode mode);
-    void drawRect(float left, float top, float right, float bottom, SkPaint* paint);
-    void drawRoundRect(float left, float top, float right, float bottom,
-            float rx, float ry, SkPaint* paint);
-    void drawCircle(float x, float y, float radius, SkPaint* paint);
-    void drawOval(float left, float top, float right, float bottom, SkPaint* paint);
-    void drawArc(float left, float top, float right, float bottom,
-            float startAngle, float sweepAngle, bool useCenter, SkPaint* paint);
-    void drawPath(SkPath* path, SkPaint* paint);
-    void drawLines(float* points, int count, SkPaint* paint);
-    void drawText(const char* text, int bytesCount, int count, float x, float y,
-            SkPaint* paint);
-
-protected:
-    void composeLayer(sp<Snapshot> current, sp<Snapshot> previous);
-
-private:
-    uint32_t mPrimitivesCount;
-
-}; // class OpenGLDebugRenderer
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_OPENGL_DEBUG_RENDERER_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index dfca7eb..1f65201 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -210,7 +210,7 @@
     glBlendEquation(GL_FUNC_ADD);
 }
 
-bool OpenGLRenderer::callDrawGLFunction(Functor *functor) {
+bool OpenGLRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) {
     interrupt();
     if (mDirtyClip) {
         setScissorFromClip();
@@ -226,9 +226,16 @@
     }
 #endif
 
-    status_t result = (*functor)();
+    float bounds[4];
+    status_t result = (*functor)(&bounds[0], 4);
+
+    if (result != 0) {
+        Rect localDirty(bounds[0], bounds[1], bounds[2], bounds[3]);
+        dirty.unionWith(localDirty);
+    }
+
     resume();
-    return (result == 0) ? false : true;
+    return result != 0;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1057,11 +1064,11 @@
 // Drawing
 ///////////////////////////////////////////////////////////////////////////////
 
-bool OpenGLRenderer::drawDisplayList(DisplayList* displayList, uint32_t level) {
+bool OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, uint32_t level) {
     // All the usual checks and setup operations (quickReject, setupDraw, etc.)
     // will be performed by the display list itself
     if (displayList) {
-        return displayList->replay(*this, level);
+        return displayList->replay(*this, dirty, level);
     }
     return false;
 }
@@ -1522,7 +1529,6 @@
             break;
     }
 
-    // TODO: Handle paint->getTextScaleX()
     const float oldX = x;
     const float oldY = y;
     const bool pureTranslate = mSnapshot->transform->isPureTranslate();
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 7bbf034..9d86388 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -70,7 +70,7 @@
     virtual void interrupt();
     virtual void resume();
 
-    virtual bool callDrawGLFunction(Functor *functor);
+    virtual bool callDrawGLFunction(Functor *functor, Rect& dirty);
 
     int getSaveCount() const;
     virtual int save(int flags);
@@ -96,7 +96,7 @@
     bool quickReject(float left, float top, float right, float bottom);
     virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
 
-    virtual bool drawDisplayList(DisplayList* displayList, uint32_t level = 0);
+    virtual bool drawDisplayList(DisplayList* displayList, Rect& dirty, uint32_t level = 0);
     virtual void drawLayer(Layer* layer, float x, float y, SkPaint* paint);
     virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
     virtual void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index 62c4250..d46686d 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -42,6 +42,17 @@
 
         textSize = paint->getTextSize();
         typeface = paint->getTypeface();
+
+        flags = 0;
+        if (paint->isFakeBoldText()) {
+            flags |= Font::kFakeBold;
+        }
+
+        const float skewX = paint->getTextSkewX();
+        italicStyle = *(uint32_t*) &skewX;
+
+        const float scaleXFloat = paint->getTextScaleX();
+        scaleX = *(uint32_t*) &scaleXFloat;
     }
 
     ~ShadowText() {
@@ -51,6 +62,9 @@
     uint32_t len;
     float textSize;
     SkTypeface* typeface;
+    uint32_t flags;
+    uint32_t italicStyle;
+    uint32_t scaleX;
     const char16_t* text;
     String16 str;
 
@@ -65,7 +79,13 @@
             LTE_INT(radius) {
                 LTE_FLOAT(textSize) {
                     LTE_INT(typeface) {
-                        return strncmp16(text, rhs.text, len >> 1) < 0;
+                        LTE_INT(flags) {
+                            LTE_INT(italicStyle) {
+                                LTE_INT(scaleX) {
+                                    return strncmp16(text, rhs.text, len >> 1) < 0;
+                                }
+                            }
+                        }
                     }
                 }
             }
diff --git a/libs/ui/KeyLayoutMap.cpp b/libs/ui/KeyLayoutMap.cpp
index 2ed0e66..8626a03 100644
--- a/libs/ui/KeyLayoutMap.cpp
+++ b/libs/ui/KeyLayoutMap.cpp
@@ -113,20 +113,23 @@
     return NO_ERROR;
 }
 
-status_t KeyLayoutMap::mapAxis(int32_t scanCode, int32_t* axis) const {
+status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const {
     ssize_t index = mAxes.indexOfKey(scanCode);
     if (index < 0) {
 #if DEBUG_MAPPING
         LOGD("mapAxis: scanCode=%d ~ Failed.", scanCode);
 #endif
-        *axis = -1;
         return NAME_NOT_FOUND;
     }
 
-    *axis = mAxes.valueAt(index);
+    *outAxisInfo = mAxes.valueAt(index);
 
 #if DEBUG_MAPPING
-    LOGD("mapAxis: scanCode=%d ~ Result axis=%d.", scanCode, *axis);
+    LOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, "
+            "splitValue=%d, flatOverride=%d.",
+            scanCode,
+            outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis,
+            outAxisInfo->splitValue, outAxisInfo->flatOverride);
 #endif
     return NO_ERROR;
 }
@@ -249,19 +252,89 @@
         return BAD_VALUE;
     }
 
+    AxisInfo axisInfo;
+
     mTokenizer->skipDelimiters(WHITESPACE);
-    String8 axisToken = mTokenizer->nextToken(WHITESPACE);
-    int32_t axis = getAxisByLabel(axisToken.string());
-    if (axis < 0) {
-        LOGE("%s: Expected axis label, got '%s'.", mTokenizer->getLocation().string(),
-                axisToken.string());
-        return BAD_VALUE;
+    String8 token = mTokenizer->nextToken(WHITESPACE);
+    if (token == "invert") {
+        axisInfo.mode = AxisInfo::MODE_INVERT;
+
+        mTokenizer->skipDelimiters(WHITESPACE);
+        String8 axisToken = mTokenizer->nextToken(WHITESPACE);
+        axisInfo.axis = getAxisByLabel(axisToken.string());
+        if (axisInfo.axis < 0) {
+            LOGE("%s: Expected inverted axis label, got '%s'.",
+                    mTokenizer->getLocation().string(), axisToken.string());
+            return BAD_VALUE;
+        }
+    } else if (token == "split") {
+        axisInfo.mode = AxisInfo::MODE_SPLIT;
+
+        mTokenizer->skipDelimiters(WHITESPACE);
+        String8 splitToken = mTokenizer->nextToken(WHITESPACE);
+        axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0));
+        if (*end) {
+            LOGE("%s: Expected split value, got '%s'.",
+                    mTokenizer->getLocation().string(), splitToken.string());
+            return BAD_VALUE;
+        }
+
+        mTokenizer->skipDelimiters(WHITESPACE);
+        String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE);
+        axisInfo.axis = getAxisByLabel(lowAxisToken.string());
+        if (axisInfo.axis < 0) {
+            LOGE("%s: Expected low axis label, got '%s'.",
+                    mTokenizer->getLocation().string(), lowAxisToken.string());
+            return BAD_VALUE;
+        }
+
+        mTokenizer->skipDelimiters(WHITESPACE);
+        String8 highAxisToken = mTokenizer->nextToken(WHITESPACE);
+        axisInfo.highAxis = getAxisByLabel(highAxisToken.string());
+        if (axisInfo.highAxis < 0) {
+            LOGE("%s: Expected high axis label, got '%s'.",
+                    mTokenizer->getLocation().string(), highAxisToken.string());
+            return BAD_VALUE;
+        }
+    } else {
+        axisInfo.axis = getAxisByLabel(token.string());
+        if (axisInfo.axis < 0) {
+            LOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.",
+                    mTokenizer->getLocation().string(), token.string());
+            return BAD_VALUE;
+        }
+    }
+
+    for (;;) {
+        mTokenizer->skipDelimiters(WHITESPACE);
+        if (mTokenizer->isEol()) {
+            break;
+        }
+        String8 keywordToken = mTokenizer->nextToken(WHITESPACE);
+        if (keywordToken == "flat") {
+            mTokenizer->skipDelimiters(WHITESPACE);
+            String8 flatToken = mTokenizer->nextToken(WHITESPACE);
+            axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0));
+            if (*end) {
+                LOGE("%s: Expected flat value, got '%s'.",
+                        mTokenizer->getLocation().string(), flatToken.string());
+                return BAD_VALUE;
+            }
+        } else {
+            LOGE("%s: Expected keyword 'flat', got '%s'.",
+                    mTokenizer->getLocation().string(), keywordToken.string());
+            return BAD_VALUE;
+        }
     }
 
 #if DEBUG_PARSER
-    LOGD("Parsed axis: scanCode=%d, axis=%d.", scanCode, axis);
+    LOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, "
+            "splitValue=%d, flatOverride=%d.",
+            scanCode,
+            axisInfo.mode, axisInfo.axis, axisInfo.highAxis,
+            axisInfo.splitValue, axisInfo.flatOverride);
 #endif
-    mMap->mAxes.add(scanCode, axis);
+    mMap->mAxes.add(scanCode, axisInfo);
     return NO_ERROR;
 }
 
diff --git a/media/java/android/media/videoeditor/AudioTrack.java b/media/java/android/media/videoeditor/AudioTrack.java
index b2f547b..7069b23 100755
--- a/media/java/android/media/videoeditor/AudioTrack.java
+++ b/media/java/android/media/videoeditor/AudioTrack.java
@@ -140,7 +140,7 @@
         try {
           properties = mMANativeHelper.getMediaProperties(filename);
         } catch (Exception e) {
-            throw new IllegalArgumentException("Unsupported file or file not found");
+            throw new IllegalArgumentException(e.getMessage() + " : " + filename);
         }
         switch (mMANativeHelper.getFileType(properties.fileType)) {
             case MediaProperties.FILE_3GP:
diff --git a/media/java/android/media/videoeditor/MediaVideoItem.java b/media/java/android/media/videoeditor/MediaVideoItem.java
index c91d796..4758de6 100755
--- a/media/java/android/media/videoeditor/MediaVideoItem.java
+++ b/media/java/android/media/videoeditor/MediaVideoItem.java
@@ -115,7 +115,7 @@
         try {
              properties = mMANativeHelper.getMediaProperties(filename);
         } catch ( Exception e) {
-            throw new IllegalArgumentException("Unsupported file or file not found: " + filename);
+            throw new IllegalArgumentException(e.getMessage() + " : " + filename);
         }
 
         switch (mMANativeHelper.getFileType(properties.fileType)) {
diff --git a/media/java/android/mtp/MtpClient.java b/media/java/android/mtp/MtpClient.java
index c4ee19e..40e2f9b 100644
--- a/media/java/android/mtp/MtpClient.java
+++ b/media/java/android/mtp/MtpClient.java
@@ -29,6 +29,7 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -43,7 +44,10 @@
     private final Context mContext;
     private final UsbManager mUsbManager;
     private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
-    private final ArrayList<MtpDevice> mDeviceList = new ArrayList<MtpDevice>();
+    // mDevices contains all MtpDevices that have been seen by our client,
+    // so we can inform when the device has been detached.
+    // mDevices is also used for synchronization in this class.
+    private final HashMap<String, MtpDevice> mDevices = new HashMap<String, MtpDevice>();
 
     private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
         @Override
@@ -51,21 +55,21 @@
             UsbDevice usbDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
             String deviceName = usbDevice.getDeviceName();
 
-            synchronized (mDeviceList) {
-                MtpDevice mtpDevice = getDeviceLocked(deviceName);
+            synchronized (mDevices) {
+                MtpDevice mtpDevice = mDevices.get(deviceName);
 
                 if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
                     if (mtpDevice == null) {
-                        mtpDevice = openDevice(usbDevice);
+                        mtpDevice = openDeviceLocked(usbDevice);
                     }
                     if (mtpDevice != null) {
-                        mDeviceList.add(mtpDevice);
+                        mDevices.put(deviceName, mtpDevice);
                         for (Listener listener : mListeners) {
                             listener.deviceAdded(mtpDevice);
                         }
                     }
                 } else if (mtpDevice != null) {
-                    mDeviceList.remove(mtpDevice);
+                    mDevices.remove(deviceName);
                     for (Listener listener : mListeners) {
                         listener.deviceRemoved(mtpDevice);
                     }
@@ -127,16 +131,6 @@
         filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
         filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
         context.registerReceiver(mUsbReceiver, filter);
-
-        for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
-            MtpDevice mtpDevice = getDeviceLocked(usbDevice.getDeviceName());
-            if (mtpDevice == null) {
-                mtpDevice = openDevice(usbDevice);
-            }
-            if (mtpDevice != null) {
-                mDeviceList.add(mtpDevice);
-            }
-        }
     }
 
     /**
@@ -146,7 +140,7 @@
      * @param device the device to open
      * @return an MtpDevice for the device.
      */
-    private MtpDevice openDevice(UsbDevice usbDevice) {
+    private MtpDevice openDeviceLocked(UsbDevice usbDevice) {
         if (isCamera(usbDevice)) {
             MtpDevice mtpDevice = new MtpDevice(usbDevice);
             if (mtpDevice.open(mUsbManager)) {
@@ -170,7 +164,7 @@
      * @param listener the listener to register
      */
     public void addListener(Listener listener) {
-        synchronized (mDeviceList) {
+        synchronized (mDevices) {
             if (!mListeners.contains(listener)) {
                 mListeners.add(listener);
             }
@@ -183,7 +177,7 @@
      * @param listener the listener to unregister
      */
     public void removeListener(Listener listener) {
-        synchronized (mDeviceList) {
+        synchronized (mDevices) {
             mListeners.remove(listener);
         }
     }
@@ -196,8 +190,8 @@
      * @return the MtpDevice, or null if it does not exist
      */
     public MtpDevice getDevice(String deviceName) {
-        synchronized (mDeviceList) {
-            return getDeviceLocked(deviceName);
+        synchronized (mDevices) {
+            return mDevices.get(deviceName);
         }
     }
 
@@ -209,28 +203,32 @@
      * @return the MtpDevice, or null if it does not exist
      */
     public MtpDevice getDevice(int id) {
-        synchronized (mDeviceList) {
-            return getDeviceLocked(UsbDevice.getDeviceName(id));
+        synchronized (mDevices) {
+            return mDevices.get(UsbDevice.getDeviceName(id));
         }
     }
 
-    private MtpDevice getDeviceLocked(String deviceName) {
-        for (MtpDevice device : mDeviceList) {
-            if (device.getDeviceName().equals(deviceName)) {
-                return device;
-            }
-        }
-        return null;
-    }
-
     /**
      * Retrieves a list of all currently connected {@link android.mtp.MtpDevice}.
      *
      * @return the list of MtpDevices
      */
     public List<MtpDevice> getDeviceList() {
-        synchronized (mDeviceList) {
-            return new ArrayList<MtpDevice>(mDeviceList);
+        synchronized (mDevices) {
+            // Query the USB manager since devices might have attached
+            // before we added our listener.
+            for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
+                String deviceName = usbDevice.getDeviceName();
+                MtpDevice mtpDevice = mDevices.get(deviceName);
+                if (mtpDevice == null) {
+                   mtpDevice = openDeviceLocked(usbDevice);
+                }
+                if (mtpDevice != null) {
+                    mDevices.put(deviceName, mtpDevice);
+                }
+            }
+
+            return new ArrayList<MtpDevice>(mDevices.values());
         }
     }
 
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 98de2f7..b4a4689 100644
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -20,6 +20,7 @@
 import android.content.ContentValues;
 import android.content.IContentProvider;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.media.MediaScanner;
@@ -62,8 +63,8 @@
     // true if the database has been modified in the current MTP session
     private boolean mDatabaseModified;
 
-    // database for writable MTP device properties
-    private SQLiteDatabase mDevicePropDb;
+    // SharedPreferences for writable MTP device properties
+    private SharedPreferences mDeviceProperties;
     private static final int DEVICE_PROPERTIES_DATABASE_VERSION = 1;
 
     // FIXME - this should be passed in via the constructor
@@ -96,9 +97,6 @@
     private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
                                             + Files.FileColumns.FORMAT + "=?";
 
-    private static final String[] DEVICE_PROPERTY_PROJECTION = new String[] { "_id", "value" };
-    private  static final String DEVICE_PROPERTY_WHERE = "code=?";
-
     private final MediaScanner mMediaScanner;
 
     static {
@@ -114,7 +112,7 @@
         mMediaStoragePath = storagePath;
         mObjectsUri = Files.getMtpObjectsUri(volumeName);
         mMediaScanner = new MediaScanner(context);
-        openDevicePropertiesDatabase(context);
+        initDeviceProperties(context);
     }
 
     @Override
@@ -126,19 +124,38 @@
         }
     }
 
-    private void openDevicePropertiesDatabase(Context context) {
-        mDevicePropDb = context.openOrCreateDatabase("device-properties", Context.MODE_PRIVATE, null);
-        int version = mDevicePropDb.getVersion();
+    private void initDeviceProperties(Context context) {
+        final String devicePropertiesName = "device-properties";
+        mDeviceProperties = context.getSharedPreferences(devicePropertiesName, Context.MODE_PRIVATE);
+        File databaseFile = context.getDatabasePath(devicePropertiesName);
 
-        // initialize if necessary
-        if (version != DEVICE_PROPERTIES_DATABASE_VERSION) {
-            mDevicePropDb.execSQL("CREATE TABLE properties (" +
-                    "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
-                    "code INTEGER UNIQUE ON CONFLICT REPLACE," +
-                    "value TEXT" +
-                    ");");
-            mDevicePropDb.execSQL("CREATE INDEX property_index ON properties (code);");
-            mDevicePropDb.setVersion(DEVICE_PROPERTIES_DATABASE_VERSION);
+        if (databaseFile.exists()) {
+            // for backward compatibility - read device properties from sqlite database
+            // and migrate them to shared prefs
+            SQLiteDatabase db = null;
+            Cursor c = null;
+            try {
+                db = context.openOrCreateDatabase("device-properties", Context.MODE_PRIVATE, null);
+                if (db != null) {
+                    c = db.query("properties", new String[] { "_id", "code", "value" },
+                            null, null, null, null, null);
+                    if (c != null) {
+                        SharedPreferences.Editor e = mDeviceProperties.edit();
+                        while (c.moveToNext()) {
+                            String name = c.getString(1);
+                            String value = c.getString(2);
+                            e.putString(name, value);
+                        }
+                        e.commit();
+                    }
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "failed to migrate device properties", e);
+            } finally {
+                if (c != null) c.close();
+                if (db != null) db.close();
+            }
+            databaseFile.delete();
         }
     }
 
@@ -567,30 +584,15 @@
         switch (property) {
             case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
             case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
-                // writable string properties kept in our device property database
-                Cursor c = null;
-                try {
-                    c = mDevicePropDb.query("properties", DEVICE_PROPERTY_PROJECTION,
-                        DEVICE_PROPERTY_WHERE, new String[] {  Integer.toString(property) },
-                        null, null, null);
-
-                    if (c != null && c.moveToNext()) {
-                        String value = c.getString(1);
-                        int length = value.length();
-                        if (length > 255) {
-                            length = 255;
-                        }
-                        value.getChars(0, length, outStringValue, 0);
-                        outStringValue[length] = 0;
-                    } else {
-                        outStringValue[0] = 0;
-                    }
-                    return MtpConstants.RESPONSE_OK;
-                } finally {
-                    if (c != null) {
-                        c.close();
-                    }
+                // writable string properties kept in shared preferences
+                String value = mDeviceProperties.getString(Integer.toString(property), "");
+                int length = value.length();
+                if (length > 255) {
+                    length = 255;
                 }
+                value.getChars(0, length, outStringValue, 0);
+                outStringValue[length] = 0;
+                return MtpConstants.RESPONSE_OK;
 
             case MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE:
                 // use screen size as max image size
@@ -612,16 +614,11 @@
         switch (property) {
             case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
             case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
-                // writable string properties kept in our device property database
-                try {
-                    ContentValues values = new ContentValues();
-                    values.put("code", property);
-                    values.put("value", stringValue);
-                    mDevicePropDb.insert("properties", "code", values);
-                    return MtpConstants.RESPONSE_OK;
-                } catch (Exception e) {
-                    return MtpConstants.RESPONSE_GENERAL_ERROR;
-                }
+                // writable string properties kept in shared prefs
+                SharedPreferences.Editor e = mDeviceProperties.edit();
+                e.putString(Integer.toString(property), stringValue);
+                return (e.commit() ? MtpConstants.RESPONSE_OK
+                        : MtpConstants.RESPONSE_GENERAL_ERROR);
         }
 
         return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
diff --git a/media/jni/mediaeditor/VideoEditorOsal.cpp b/media/jni/mediaeditor/VideoEditorOsal.cpp
index 423e93f..035f59a 100755
--- a/media/jni/mediaeditor/VideoEditorOsal.cpp
+++ b/media/jni/mediaeditor/VideoEditorOsal.cpp
@@ -207,6 +207,7 @@
     VIDEOEDIT_OSAL_RESULT_INIT(M4MCS_ERR_AUDIOBITRATE_TOO_HIGH                        ),
     VIDEOEDIT_OSAL_RESULT_INIT(M4MCS_ERR_OUTPUT_FILE_SIZE_TOO_SMALL                   ),
     VIDEOEDIT_OSAL_RESULT_INIT(M4MCS_ERR_NOMORE_SPACE                                 ),
+    VIDEOEDIT_OSAL_RESULT_INIT(M4MCS_ERR_FILE_DRM_PROTECTED                           ),
 
     // M4READER_Common.h
     VIDEOEDIT_OSAL_RESULT_INIT(M4ERR_READER_UNKNOWN_STREAM_TYPE                       ),
diff --git a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
index 73a7c9c..3b795ce 100755
--- a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
@@ -204,10 +204,17 @@
                 result = getClipProperties(
                         pEnv, thiz, pFile, clipType, pClipProperties);
 
-                // Check if the creation succeeded.
-                videoEditJava_checkAndThrowIllegalArgumentException(
-                        &gotten, pEnv,(M4NO_ERROR != result),
-                        "Invalid File or File not found");
+                if (M4MCS_ERR_FILE_DRM_PROTECTED == result) {
+                    // Check if the creation succeeded.
+                    videoEditJava_checkAndThrowIllegalArgumentException(
+                            &gotten, pEnv,(M4NO_ERROR != result),
+                            "Invalid File - DRM Protected ");
+                } else {
+                    // Check if the creation succeeded.
+                    videoEditJava_checkAndThrowIllegalArgumentException(
+                            &gotten, pEnv,(M4NO_ERROR != result),
+                            "Invalid File or File not found ");
+                }
 
                 /**
                  * Max resolution supported is 1280 x 720.
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index dbbf3b4..b24343f 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -1,3 +1,19 @@
+/*
+ * 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.
+ */
+
 //#define LOG_NDEBUG 0
 #define LOG_TAG "NuHTTPDataSource"
 #include <utils/Log.h>
diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h
index 082b589..2569568 100644
--- a/media/libstagefright/include/NuHTTPDataSource.h
+++ b/media/libstagefright/include/NuHTTPDataSource.h
@@ -1,3 +1,19 @@
+/*
+ * 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.
+ */
+
 #ifndef NU_HTTP_DATA_SOURCE_H_
 
 #define NU_HTTP_DATA_SOURCE_H_
diff --git a/native/include/android/input.h b/native/include/android/input.h
index f19e8be..86be54a 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -359,6 +359,11 @@
     AMOTION_EVENT_AXIS_HAT_Y = 16,
     AMOTION_EVENT_AXIS_LTRIGGER = 17,
     AMOTION_EVENT_AXIS_RTRIGGER = 18,
+    AMOTION_EVENT_AXIS_THROTTLE = 19,
+    AMOTION_EVENT_AXIS_RUDDER = 20,
+    AMOTION_EVENT_AXIS_WHEEL = 21,
+    AMOTION_EVENT_AXIS_GAS = 22,
+    AMOTION_EVENT_AXIS_BRAKE = 23,
     AMOTION_EVENT_AXIS_GENERIC_1 = 32,
     AMOTION_EVENT_AXIS_GENERIC_2 = 33,
     AMOTION_EVENT_AXIS_GENERIC_3 = 34,
diff --git a/opengl/java/com/google/android/gles_jni/EGLContextImpl.java b/opengl/java/com/google/android/gles_jni/EGLContextImpl.java
index 9cf5de7..cd36099 100644
--- a/opengl/java/com/google/android/gles_jni/EGLContextImpl.java
+++ b/opengl/java/com/google/android/gles_jni/EGLContextImpl.java
@@ -32,4 +32,19 @@
     public GL getGL() {
         return mGLContext;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        EGLContextImpl that = (EGLContextImpl) o;
+
+        return mEGLContext == that.mEGLContext;
+    }
+
+    @Override
+    public int hashCode() {
+        return mEGLContext;
+    }
 }
diff --git a/opengl/java/com/google/android/gles_jni/EGLDisplayImpl.java b/opengl/java/com/google/android/gles_jni/EGLDisplayImpl.java
index cb94888..e6c9817 100644
--- a/opengl/java/com/google/android/gles_jni/EGLDisplayImpl.java
+++ b/opengl/java/com/google/android/gles_jni/EGLDisplayImpl.java
@@ -24,4 +24,20 @@
     public EGLDisplayImpl(int dpy) {
         mEGLDisplay = dpy;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        EGLDisplayImpl that = (EGLDisplayImpl) o;
+
+        return mEGLDisplay == that.mEGLDisplay;
+
+    }
+
+    @Override
+    public int hashCode() {
+        return mEGLDisplay;
+    }
 }
diff --git a/opengl/java/com/google/android/gles_jni/EGLSurfaceImpl.java b/opengl/java/com/google/android/gles_jni/EGLSurfaceImpl.java
index f6b90ab..e7f15dc 100644
--- a/opengl/java/com/google/android/gles_jni/EGLSurfaceImpl.java
+++ b/opengl/java/com/google/android/gles_jni/EGLSurfaceImpl.java
@@ -29,4 +29,20 @@
         mEGLSurface = surface;
         mNativePixelRef = 0;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        EGLSurfaceImpl that = (EGLSurfaceImpl) o;
+
+        return mEGLSurface == that.mEGLSurface;
+
+    }
+
+    @Override
+    public int hashCode() {
+        return mEGLSurface;
+    }
 }
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel_title.xml b/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel_title.xml
index 1afb2e3..543f4ed 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel_title.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_notification_panel_title.xml
@@ -22,7 +22,6 @@
     android:layout_height="0dp"
     android:orientation="vertical"
     android:background="@drawable/notify_panel_clock_bg"
-    android:clickable="true"
     >
     <LinearLayout
         android:id="@+id/icons"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPeekPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPeekPanel.java
index 744f667..8b68240 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPeekPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPeekPanel.java
@@ -18,11 +18,15 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.MotionEvent;
 import android.widget.RelativeLayout;
 
 import com.android.systemui.R;
 
 public class NotificationPeekPanel extends RelativeLayout implements StatusBarPanel {
+    TabletStatusBar mBar;
+
     public NotificationPeekPanel(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
@@ -39,5 +43,16 @@
         return x >= l && x < r && y >= t && y < b;
     }
 
+    public void setBar(TabletStatusBar bar) {
+        mBar = bar;
+    }
+
+    // We don't really want to intercept the touch event, but we *do* want to reset the fade timer
+    // in case the user is interacting with some custom controls or something.
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        mBar.resetNotificationPeekFadeTimer();
+        return false;
+    }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 4557105..58c4d5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -217,6 +217,8 @@
         // Notification preview window
         mNotificationPeekWindow = (NotificationPeekPanel) View.inflate(context,
                 R.layout.status_bar_notification_peek, null);
+        mNotificationPeekWindow.setBar(this);
+
         mNotificationPeekRow = (ViewGroup) mNotificationPeekWindow.findViewById(R.id.content);
         mNotificationPeekWindow.setVisibility(View.GONE);
         mNotificationPeekWindow.setOnTouchListener(
@@ -1211,10 +1213,20 @@
         }
     }
 
-    private class NotificationIconTouchListener implements View.OnTouchListener {
-        final static int NOTIFICATION_PEEK_HOLD_THRESH = 200; // ms
-        final static int NOTIFICATION_PEEK_FADE_DELAY = 5000; // ms
+    final static int NOTIFICATION_PEEK_HOLD_THRESH = 200; // ms
+    final static int NOTIFICATION_PEEK_FADE_DELAY = 3000; // ms
 
+    public void resetNotificationPeekFadeTimer() {
+        if (DEBUG) {
+            Slog.d(TAG, "setting peek fade timer for " + NOTIFICATION_PEEK_FADE_DELAY
+                + "ms from now");
+        }
+        mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PEEK);
+        mHandler.sendEmptyMessageDelayed(MSG_CLOSE_NOTIFICATION_PEEK,
+                NOTIFICATION_PEEK_FADE_DELAY);
+    }
+
+    private class NotificationIconTouchListener implements View.OnTouchListener {
         VelocityTracker mVT;
         int mPeekIndex;
         float mInitialTouchX, mInitialTouchY;
@@ -1303,8 +1315,7 @@
                     }
 
                     if (peeking) {
-                        mHandler.sendEmptyMessageDelayed(MSG_CLOSE_NOTIFICATION_PEEK,
-                                NOTIFICATION_PEEK_FADE_DELAY);
+                        resetNotificationPeekFadeTimer();
                     }
 
                     mVT.recycle();
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index ec89db3..64857ed 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2187,7 +2187,7 @@
             super.onDetachedFromWindow();
             
             final Callback cb = getCallback();
-            if (cb != null && !isDestroyed() && mFeatureId < 0) {
+            if (cb != null && mFeatureId < 0) {
                 cb.onDetachedFromWindow();
             }
 
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 2fe5980..e37a6b1 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -352,14 +352,13 @@
     return NAME_NOT_FOUND;
 }
 
-status_t EventHub::mapAxis(int32_t deviceId, int scancode,
-        int32_t* outAxis) const
+status_t EventHub::mapAxis(int32_t deviceId, int scancode, AxisInfo* outAxisInfo) const
 {
     AutoMutex _l(mLock);
     Device* device = getDeviceLocked(deviceId);
 
     if (device && device->keyMap.haveKeyLayout()) {
-        status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxis);
+        status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxisInfo);
         if (err == NO_ERROR) {
             return NO_ERROR;
         }
@@ -369,14 +368,13 @@
         device = getDeviceLocked(mBuiltInKeyboardId);
 
         if (device && device->keyMap.haveKeyLayout()) {
-            status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxis);
+            status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxisInfo);
             if (err == NO_ERROR) {
                 return NO_ERROR;
             }
         }
     }
 
-    *outAxis = -1;
     return NAME_NOT_FOUND;
 }
 
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 445c04b..ab42a1f 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -176,7 +176,7 @@
             int32_t* outKeycode, uint32_t* outFlags) const = 0;
 
     virtual status_t mapAxis(int32_t deviceId, int scancode,
-            int32_t* outAxis) const = 0;
+            AxisInfo* outAxisInfo) const = 0;
 
     // exclude a particular device from opening
     // this can be used to ignore input devices for sensors
@@ -235,7 +235,7 @@
             int32_t* outKeycode, uint32_t* outFlags) const;
 
     virtual status_t mapAxis(int32_t deviceId, int scancode,
-            int32_t* outAxis) const;
+            AxisInfo* outAxisInfo) const;
 
     virtual void addExcludedDevice(const char* deviceName);
 
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index b92c3b5..00c3eb7 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -3854,7 +3854,10 @@
 
     for (size_t i = 0; i < mAxes.size(); i++) {
         const Axis& axis = mAxes.valueAt(i);
-        info->addMotionRange(axis.axis, axis.min, axis.max, axis.flat, axis.fuzz);
+        info->addMotionRange(axis.axisInfo.axis, axis.min, axis.max, axis.flat, axis.fuzz);
+        if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+            info->addMotionRange(axis.axisInfo.highAxis, axis.min, axis.max, axis.flat, axis.fuzz);
+        }
     }
 }
 
@@ -3865,18 +3868,29 @@
     size_t numAxes = mAxes.size();
     for (size_t i = 0; i < numAxes; i++) {
         const Axis& axis = mAxes.valueAt(i);
-        const char* label = getAxisLabel(axis.axis);
-        char name[32];
+        const char* label = getAxisLabel(axis.axisInfo.axis);
         if (label) {
-            strncpy(name, label, sizeof(name));
-            name[sizeof(name) - 1] = '\0';
+            dump.appendFormat(INDENT4 "%s", label);
         } else {
-            snprintf(name, sizeof(name), "%d", axis.axis);
+            dump.appendFormat(INDENT4 "%d", axis.axisInfo.axis);
         }
-        dump.appendFormat(INDENT4 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, "
-                "scale=%0.3f, offset=%0.3f\n",
-                name, axis.min, axis.max, axis.flat, axis.fuzz,
-                axis.scale, axis.offset);
+        if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+            label = getAxisLabel(axis.axisInfo.highAxis);
+            if (label) {
+                dump.appendFormat(" / %s (split at %d)", label, axis.axisInfo.splitValue);
+            } else {
+                dump.appendFormat(" / %d (split at %d)", axis.axisInfo.highAxis,
+                        axis.axisInfo.splitValue);
+            }
+        } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) {
+            dump.append(" (invert)");
+        }
+
+        dump.appendFormat(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f\n",
+                axis.min, axis.max, axis.flat, axis.fuzz);
+        dump.appendFormat(INDENT4 "  scale=%0.5f, offset=%0.5f, "
+                "highScale=%0.5f, highOffset=%0.5f\n",
+                axis.scale, axis.offset, axis.highScale, axis.highOffset);
         dump.appendFormat(INDENT4 "  rawAxis=%d, rawMin=%d, rawMax=%d, rawFlat=%d, rawFuzz=%d\n",
                 mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue,
                 axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz);
@@ -3891,25 +3905,38 @@
         RawAbsoluteAxisInfo rawAxisInfo;
         getEventHub()->getAbsoluteAxisInfo(getDeviceId(), abs, &rawAxisInfo);
         if (rawAxisInfo.valid) {
-            int32_t axisId;
-            bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisId);
+            // Map axis.
+            AxisInfo axisInfo;
+            bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo);
             if (!explicitlyMapped) {
                 // Axis is not explicitly mapped, will choose a generic axis later.
-                axisId = -1;
+                axisInfo.mode = AxisInfo::MODE_NORMAL;
+                axisInfo.axis = -1;
             }
 
+            // Apply flat override.
+            int32_t rawFlat = axisInfo.flatOverride < 0
+                    ? rawAxisInfo.flat : axisInfo.flatOverride;
+
+            // Calculate scaling factors and limits.
             Axis axis;
-            if (isCenteredAxis(axisId)) {
+            if (axisInfo.mode == AxisInfo::MODE_SPLIT) {
+                float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue);
+                float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue);
+                axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
+                        scale, 0.0f, highScale, 0.0f,
+                        0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
+            } else if (isCenteredAxis(axisInfo.axis)) {
                 float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
                 float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale;
-                axis.initialize(rawAxisInfo, axisId, explicitlyMapped,
-                        scale, offset, -1.0f, 1.0f,
-                        rawAxisInfo.flat * scale, rawAxisInfo.fuzz * scale);
+                axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
+                        scale, offset, scale, offset,
+                        -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
             } else {
                 float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
-                axis.initialize(rawAxisInfo, axisId, explicitlyMapped,
-                        scale, 0.0f, 0.0f, 1.0f,
-                        rawAxisInfo.flat * scale, rawAxisInfo.fuzz * scale);
+                axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
+                        scale, 0.0f, scale, 0.0f,
+                        0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
             }
 
             // To eliminate noise while the joystick is at rest, filter out small variations
@@ -3934,14 +3961,14 @@
     size_t numAxes = mAxes.size();
     for (size_t i = 0; i < numAxes; i++) {
         Axis& axis = mAxes.editValueAt(i);
-        if (axis.axis < 0) {
+        if (axis.axisInfo.axis < 0) {
             while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16
                     && haveAxis(nextGenericAxisId)) {
                 nextGenericAxisId += 1;
             }
 
             if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) {
-                axis.axis = nextGenericAxisId;
+                axis.axisInfo.axis = nextGenericAxisId;
                 nextGenericAxisId += 1;
             } else {
                 LOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids "
@@ -3954,10 +3981,13 @@
     }
 }
 
-bool JoystickInputMapper::haveAxis(int32_t axis) {
+bool JoystickInputMapper::haveAxis(int32_t axisId) {
     size_t numAxes = mAxes.size();
     for (size_t i = 0; i < numAxes; i++) {
-        if (mAxes.valueAt(i).axis == axis) {
+        const Axis& axis = mAxes.valueAt(i);
+        if (axis.axisInfo.axis == axisId
+                || (axis.axisInfo.mode == AxisInfo::MODE_SPLIT
+                        && axis.axisInfo.highAxis == axisId)) {
             return true;
         }
     }
@@ -3987,6 +4017,8 @@
     case AMOTION_EVENT_AXIS_HAT_X:
     case AMOTION_EVENT_AXIS_HAT_Y:
     case AMOTION_EVENT_AXIS_ORIENTATION:
+    case AMOTION_EVENT_AXIS_RUDDER:
+    case AMOTION_EVENT_AXIS_WHEEL:
         return true;
     default:
         return false;
@@ -4000,7 +4032,7 @@
     size_t numAxes = mAxes.size();
     for (size_t i = 0; i < numAxes; i++) {
         Axis& axis = mAxes.editValueAt(i);
-        axis.newValue = 0;
+        axis.resetValue();
     }
 
     sync(when, true /*force*/);
@@ -4014,10 +4046,34 @@
         ssize_t index = mAxes.indexOfKey(rawEvent->scanCode);
         if (index >= 0) {
             Axis& axis = mAxes.editValueAt(index);
-            float newValue = rawEvent->value * axis.scale + axis.offset;
-            if (newValue != axis.newValue) {
-                axis.newValue = newValue;
+            float newValue, highNewValue;
+            switch (axis.axisInfo.mode) {
+            case AxisInfo::MODE_INVERT:
+                newValue = (axis.rawAxisInfo.maxValue - rawEvent->value)
+                        * axis.scale + axis.offset;
+                highNewValue = 0.0f;
+                break;
+            case AxisInfo::MODE_SPLIT:
+                if (rawEvent->value < axis.axisInfo.splitValue) {
+                    newValue = (axis.axisInfo.splitValue - rawEvent->value)
+                            * axis.scale + axis.offset;
+                    highNewValue = 0.0f;
+                } else if (rawEvent->value > axis.axisInfo.splitValue) {
+                    newValue = 0.0f;
+                    highNewValue = (rawEvent->value - axis.axisInfo.splitValue)
+                            * axis.highScale + axis.highOffset;
+                } else {
+                    newValue = 0.0f;
+                    highNewValue = 0.0f;
+                }
+                break;
+            default:
+                newValue = rawEvent->value * axis.scale + axis.offset;
+                highNewValue = 0.0f;
+                break;
             }
+            axis.newValue = newValue;
+            axis.highNewValue = highNewValue;
         }
         break;
     }
@@ -4033,7 +4089,7 @@
 }
 
 void JoystickInputMapper::sync(nsecs_t when, bool force) {
-    if (!force && !haveAxesChangedSignificantly()) {
+    if (!filterAxes(force)) {
         return;
     }
 
@@ -4044,9 +4100,11 @@
 
     size_t numAxes = mAxes.size();
     for (size_t i = 0; i < numAxes; i++) {
-        Axis& axis = mAxes.editValueAt(i);
-        pointerCoords.setAxisValue(axis.axis, axis.newValue);
-        axis.oldValue = axis.newValue;
+        const Axis& axis = mAxes.valueAt(i);
+        pointerCoords.setAxisValue(axis.axisInfo.axis, axis.currentValue);
+        if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+            pointerCoords.setAxisValue(axis.axisInfo.highAxis, axis.highCurrentValue);
+        }
     }
 
     // Moving a joystick axis should not wake the devide because joysticks can
@@ -4061,12 +4119,49 @@
             1, &pointerId, &pointerCoords, 0, 0, 0);
 }
 
-bool JoystickInputMapper::haveAxesChangedSignificantly() {
+bool JoystickInputMapper::filterAxes(bool force) {
+    bool atLeastOneSignificantChange = force;
     size_t numAxes = mAxes.size();
     for (size_t i = 0; i < numAxes; i++) {
-        const Axis& axis = mAxes.valueAt(i);
-        if (axis.newValue != axis.oldValue
-                && fabs(axis.newValue - axis.oldValue) > axis.filter) {
+        Axis& axis = mAxes.editValueAt(i);
+        if (force || hasValueChangedSignificantly(axis.filter,
+                axis.newValue, axis.currentValue, axis.min, axis.max)) {
+            axis.currentValue = axis.newValue;
+            atLeastOneSignificantChange = true;
+        }
+        if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
+            if (force || hasValueChangedSignificantly(axis.filter,
+                    axis.highNewValue, axis.highCurrentValue, axis.min, axis.max)) {
+                axis.highCurrentValue = axis.highNewValue;
+                atLeastOneSignificantChange = true;
+            }
+        }
+    }
+    return atLeastOneSignificantChange;
+}
+
+bool JoystickInputMapper::hasValueChangedSignificantly(
+        float filter, float newValue, float currentValue, float min, float max) {
+    if (newValue != currentValue) {
+        // Filter out small changes in value unless the value is converging on the axis
+        // bounds or center point.  This is intended to reduce the amount of information
+        // sent to applications by particularly noisy joysticks (such as PS3).
+        if (fabs(newValue - currentValue) > filter
+                || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min)
+                || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max)
+                || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange(
+        float filter, float newValue, float currentValue, float thresholdValue) {
+    float newDistance = fabs(newValue - thresholdValue);
+    if (newDistance < filter) {
+        float oldDistance = fabs(currentValue - thresholdValue);
+        if (newDistance < oldDistance) {
             return true;
         }
     }
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 655f0f0..e2cf08e7 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -1011,38 +1011,50 @@
 private:
     struct Axis {
         RawAbsoluteAxisInfo rawAxisInfo;
+        AxisInfo axisInfo;
 
-        int32_t axis;  // axis id
         bool explicitlyMapped; // true if the axis was explicitly assigned an axis id
 
         float scale;   // scale factor from raw to normalized values
         float offset;  // offset to add after scaling for normalization
+        float highScale;  // scale factor from raw to normalized values of high split
+        float highOffset; // offset to add after scaling for normalization of high split
 
         float min;     // normalized inclusive minimum
         float max;     // normalized inclusive maximum
         float flat;    // normalized flat region size
         float fuzz;    // normalized error tolerance
 
-        float oldValue; // previous value
-        float newValue; // most recent value
-
         float filter;  // filter out small variations of this size
+        float currentValue; // current value
+        float newValue; // most recent value
+        float highCurrentValue; // current value of high split
+        float highNewValue; // most recent value of high split
 
-        void initialize(const RawAbsoluteAxisInfo& rawAxisInfo,
-                int32_t axis, bool explicitlyMapped, float scale, float offset,
+        void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo,
+                bool explicitlyMapped, float scale, float offset,
+                float highScale, float highOffset,
                 float min, float max, float flat, float fuzz) {
             this->rawAxisInfo = rawAxisInfo;
-            this->axis = axis;
+            this->axisInfo = axisInfo;
             this->explicitlyMapped = explicitlyMapped;
             this->scale = scale;
             this->offset = offset;
+            this->highScale = highScale;
+            this->highOffset = highOffset;
             this->min = min;
             this->max = max;
             this->flat = flat;
             this->fuzz = fuzz;
             this->filter = 0;
-            this->oldValue = 0;
+            resetValue();
+        }
+
+        void resetValue() {
+            this->currentValue = 0;
             this->newValue = 0;
+            this->highCurrentValue = 0;
+            this->highNewValue = 0;
         }
     };
 
@@ -1051,9 +1063,14 @@
 
     void sync(nsecs_t when, bool force);
 
-    bool haveAxis(int32_t axis);
+    bool haveAxis(int32_t axisId);
     void pruneAxes(bool ignoreExplicitlyMappedAxes);
-    bool haveAxesChangedSignificantly();
+    bool filterAxes(bool force);
+
+    static bool hasValueChangedSignificantly(float filter,
+            float newValue, float currentValue, float min, float max);
+    static bool hasMovedNearerToValueWithinFilteredRange(float filter,
+            float newValue, float currentValue, float thresholdValue);
 
     static bool isCenteredAxis(int32_t axis);
 };
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 8404b3a..d77cb2f 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -592,7 +592,7 @@
     }
 
     virtual status_t mapAxis(int32_t deviceId, int scancode,
-            int32_t* outAxis) const {
+            AxisInfo* outAxisInfo) const {
         return NAME_NOT_FOUND;
     }
 
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java
index e7f431c..3c8432e 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextActivity.java
@@ -58,10 +58,12 @@
             mScaledPaint = new Paint();
             mScaledPaint.setAntiAlias(true);
             mScaledPaint.setTextSize(16.0f);
+            mScaledPaint.setShadowLayer(3.0f, 3.0f, 3.0f, 0xff00ff00);
 
             mSkewPaint = new Paint();
             mSkewPaint.setAntiAlias(true);
-            mSkewPaint.setTextSize(16.0f);            
+            mSkewPaint.setTextSize(16.0f);
+            mSkewPaint.setShadowLayer(3.0f, 3.0f, 3.0f, 0xff000000);
         }
 
         @Override
@@ -106,11 +108,11 @@
             mStrikePaint.setUnderlineText(true);
             
             mSkewPaint.setTextSkewX(-0.25f);
-            canvas.drawText("Hello OpenGL renderer!", 680, 200, mSkewPaint);
+            canvas.drawText("Hello OpenGL renderer!", 980, 200, mSkewPaint);
             mSkewPaint.setTextSkewX(0.5f);
-            canvas.drawText("Hello OpenGL renderer!", 680, 230, mSkewPaint);
+            canvas.drawText("Hello OpenGL renderer!", 980, 230, mSkewPaint);
             mSkewPaint.setTextSkewX(0.0f);
-            canvas.drawText("Hello OpenGL renderer!", 680, 260, mSkewPaint);
+            canvas.drawText("Hello OpenGL renderer!", 980, 260, mSkewPaint);
 
             mScaledPaint.setTextScaleX(0.5f);
             canvas.drawText("Hello OpenGL renderer!", 500, 200, mScaledPaint);
@@ -125,4 +127,4 @@
             canvas.restore();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/voip/java/android/net/rtp/AudioCodec.java b/voip/java/android/net/rtp/AudioCodec.java
index 3877aeb..85255c8 100644
--- a/voip/java/android/net/rtp/AudioCodec.java
+++ b/voip/java/android/net/rtp/AudioCodec.java
@@ -33,7 +33,6 @@
  * </pre>
  *
  * @see AudioStream
- * @hide
  */
 public class AudioCodec {
     /**
diff --git a/voip/java/android/net/rtp/AudioGroup.java b/voip/java/android/net/rtp/AudioGroup.java
index 20c8969..3e7ace8 100644
--- a/voip/java/android/net/rtp/AudioGroup.java
+++ b/voip/java/android/net/rtp/AudioGroup.java
@@ -59,7 +59,6 @@
  * the AudioGroups is in use.</p>
  *
  * @see AudioStream
- * @hide
  */
 public class AudioGroup {
     /**
diff --git a/voip/java/android/net/rtp/AudioStream.java b/voip/java/android/net/rtp/AudioStream.java
index b45cc5e..d761214 100644
--- a/voip/java/android/net/rtp/AudioStream.java
+++ b/voip/java/android/net/rtp/AudioStream.java
@@ -41,7 +41,6 @@
  *
  * @see RtpStream
  * @see AudioGroup
- * @hide
  */
 public class AudioStream extends RtpStream {
     private AudioCodec mCodec;
diff --git a/voip/java/android/net/rtp/RtpStream.java b/voip/java/android/net/rtp/RtpStream.java
index 87d8bc6..e94ac42 100644
--- a/voip/java/android/net/rtp/RtpStream.java
+++ b/voip/java/android/net/rtp/RtpStream.java
@@ -27,7 +27,6 @@
  *
  * <p class="note">Using this class requires
  * {@link android.Manifest.permission#INTERNET} permission.</p>
- * @hide
  */
 public class RtpStream {
     /**