Merge "Fix crash in ConfigUpdater when the target dir doesn't exist." into jb-mr2-dev
diff --git a/api/current.txt b/api/current.txt
index 6456ce9..085a960 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24523,6 +24523,7 @@
     method public float getMax();
     method public float getMin();
     method public float getRange();
+    method public float getResolution();
     method public int getSource();
     method public boolean isFromSource(int);
   }
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 16b4835..e9e7551 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -152,7 +152,15 @@
      * not return until the current process is exiting.
      */
     public static final native void joinThreadPool();
-    
+
+    /**
+     * Returns true if the specified interface is a proxy.
+     * @hide
+     */
+    public static final boolean isProxy(IInterface iface) {
+        return iface.asBinder() != iface;
+    }
+
     /**
      * Default constructor initializes the object.
      */
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index a797176..40ee1ad 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -54,6 +54,7 @@
     private native void nativeTransferTo(InputChannel other);
     private native void nativeReadFromParcel(Parcel parcel);
     private native void nativeWriteToParcel(Parcel parcel);
+    private native void nativeDup(InputChannel target);
     
     private native String nativeGetName();
 
@@ -64,7 +65,7 @@
      */
     public InputChannel() {
     }
-    
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -125,6 +126,15 @@
         nativeTransferTo(outParameter);
     }
 
+    /**
+     * Duplicates the input channel.
+     */
+    public InputChannel dup() {
+        InputChannel target = new InputChannel();
+        nativeDup(target);
+        return target;
+    }
+
     @Override
     public int describeContents() {
         return Parcelable.CONTENTS_FILE_DESCRIPTOR;
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index dd523d2..2595ee5f 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -371,8 +371,8 @@
             if (axis < 0) {
                 break;
             }
-            addMotionRange(axis, in.readInt(),
-                    in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat());
+            addMotionRange(axis, in.readInt(), in.readFloat(), in.readFloat(), in.readFloat(),
+                    in.readFloat(), in.readFloat());
         }
     }
 
@@ -584,8 +584,8 @@
 
     // Called from native code.
     private void addMotionRange(int axis, int source,
-            float min, float max, float flat, float fuzz) {
-        mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz));
+            float min, float max, float flat, float fuzz, float resolution) {
+        mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz, resolution));
     }
 
     /**
@@ -625,14 +625,17 @@
         private float mMax;
         private float mFlat;
         private float mFuzz;
+        private float mResolution;
 
-        private MotionRange(int axis, int source, float min, float max, float flat, float fuzz) {
+        private MotionRange(int axis, int source, float min, float max, float flat, float fuzz,
+                float resolution) {
             mAxis = axis;
             mSource = source;
             mMin = min;
             mMax = max;
             mFlat = flat;
             mFuzz = fuzz;
+            mResolution = resolution;
         }
 
         /**
@@ -711,6 +714,14 @@
         public float getFuzz() {
             return mFuzz;
         }
+
+        /**
+         * Gets the resolution for input device measurements with respect to this axis.
+         * @return The resolution in units per millimeter, or units per radian for rotational axes.
+         */
+        public float getResolution() {
+            return mResolution;
+        }
     }
 
     @Override
@@ -734,6 +745,7 @@
             out.writeFloat(range.mMax);
             out.writeFloat(range.mFlat);
             out.writeFloat(range.mFuzz);
+            out.writeFloat(range.mResolution);
         }
         out.writeInt(-1);
     }
@@ -788,6 +800,7 @@
             description.append(" max=").append(range.mMax);
             description.append(" flat=").append(range.mFlat);
             description.append(" fuzz=").append(range.mFuzz);
+            description.append(" resolution=").append(range.mResolution);
             description.append("\n");
         }
         return description.toString();
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 9143c61..14afe21 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -84,7 +84,7 @@
         dest.writeStrongInterface(method);
         if (channel != null) {
             dest.writeInt(1);
-            channel.writeToParcel(dest, 0);
+            channel.writeToParcel(dest, flags);
         } else {
             dest.writeInt(0);
         }
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 9c44a59..9fa9fe4 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -246,6 +246,15 @@
     return name;
 }
 
+static void android_view_InputChannel_nativeDup(JNIEnv* env, jobject obj, jobject otherObj) {
+    NativeInputChannel* nativeInputChannel =
+            android_view_InputChannel_getNativeInputChannel(env, obj);
+    if (nativeInputChannel) {
+        android_view_InputChannel_setNativeInputChannel(env, otherObj,
+                new NativeInputChannel(nativeInputChannel->getInputChannel()->dup()));
+    }
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gInputChannelMethods[] = {
@@ -262,6 +271,8 @@
             (void*)android_view_InputChannel_nativeWriteToParcel },
     { "nativeGetName", "()Ljava/lang/String;",
             (void*)android_view_InputChannel_nativeGetName },
+    { "nativeDup", "(Landroid/view/InputChannel;)V",
+            (void*)android_view_InputChannel_nativeDup },
 };
 
 #define FIND_CLASS(var, className) \
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 576f831..e3a54a8 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -62,8 +62,8 @@
     const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
     for (size_t i = 0; i < ranges.size(); i++) {
         const InputDeviceInfo::MotionRange& range = ranges.itemAt(i);
-        env->CallVoidMethod(inputDeviceObj.get(), gInputDeviceClassInfo.addMotionRange,
-                range.axis, range.source, range.min, range.max, range.flat, range.fuzz);
+        env->CallVoidMethod(inputDeviceObj.get(), gInputDeviceClassInfo.addMotionRange, range.axis,
+                range.source, range.min, range.max, range.flat, range.fuzz, range.resolution);
         if (env->ExceptionCheck()) {
             return NULL;
         }
@@ -90,7 +90,7 @@
             "<init>", "(IILjava/lang/String;Ljava/lang/String;ZIILandroid/view/KeyCharacterMap;Z)V");
 
     GET_METHOD_ID(gInputDeviceClassInfo.addMotionRange, gInputDeviceClassInfo.clazz,
-            "addMotionRange", "(IIFFFF)V");
+            "addMotionRange", "(IIFFFFF)V");
 
     return 0;
 }
diff --git a/core/res/res/layout/select_dialog.xml b/core/res/res/layout/select_dialog.xml
index 80d22f6..eb4d8d9 100644
--- a/core/res/res/layout/select_dialog.xml
+++ b/core/res/res/layout/select_dialog.xml
@@ -32,4 +32,5 @@
     android:cacheColorHint="@null"
     android:divider="?android:attr/listDividerAlertDialog"
     android:scrollbars="vertical"
-    android:overScrollMode="ifContentScrolls" />
+    android:overScrollMode="ifContentScrolls"
+    android:textAlignment="viewStart" />
diff --git a/core/res/res/layout/select_dialog_holo.xml b/core/res/res/layout/select_dialog_holo.xml
index 06a5d96..8a92283 100644
--- a/core/res/res/layout/select_dialog_holo.xml
+++ b/core/res/res/layout/select_dialog_holo.xml
@@ -30,4 +30,5 @@
     android:cacheColorHint="@null"
     android:divider="?android:attr/listDividerAlertDialog"
     android:scrollbars="vertical"
-    android:overScrollMode="ifContentScrolls" />
+    android:overScrollMode="ifContentScrolls"
+    android:textAlignment="viewStart" />
diff --git a/include/androidfw/InputDevice.h b/include/androidfw/InputDevice.h
index 1aecf80..45dc2db 100644
--- a/include/androidfw/InputDevice.h
+++ b/include/androidfw/InputDevice.h
@@ -64,6 +64,7 @@
         float max;
         float flat;
         float fuzz;
+        float resolution;
     };
 
     void initialize(int32_t id, int32_t generation, const InputDeviceIdentifier& identifier,
@@ -83,7 +84,7 @@
 
     void addSource(uint32_t source);
     void addMotionRange(int32_t axis, uint32_t source,
-            float min, float max, float flat, float fuzz);
+            float min, float max, float flat, float fuzz, float resolution);
     void addMotionRange(const MotionRange& range);
 
     inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; }
diff --git a/include/androidfw/InputTransport.h b/include/androidfw/InputTransport.h
index 5706bce..8712995 100644
--- a/include/androidfw/InputTransport.h
+++ b/include/androidfw/InputTransport.h
@@ -168,6 +168,9 @@
      */
     status_t receiveMessage(InputMessage* msg);
 
+    /* Returns a new object that has a duplicate of this channel's fd. */
+    sp<InputChannel> dup() const;
+
 private:
     String8 mName;
     int mFd;
diff --git a/libs/androidfw/InputDevice.cpp b/libs/androidfw/InputDevice.cpp
index fe891cb..f742052 100644
--- a/libs/androidfw/InputDevice.cpp
+++ b/libs/androidfw/InputDevice.cpp
@@ -172,8 +172,8 @@
 }
 
 void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max,
-        float flat, float fuzz) {
-    MotionRange range = { axis, source, min, max, flat, fuzz };
+        float flat, float fuzz, float resolution) {
+    MotionRange range = { axis, source, min, max, flat, fuzz, resolution };
     mMotionRanges.add(range);
 }
 
diff --git a/libs/androidfw/InputTransport.cpp b/libs/androidfw/InputTransport.cpp
index 351c666..498389ea 100644
--- a/libs/androidfw/InputTransport.cpp
+++ b/libs/androidfw/InputTransport.cpp
@@ -219,6 +219,11 @@
     return OK;
 }
 
+sp<InputChannel> InputChannel::dup() const {
+    int fd = ::dup(getFd());
+    return fd >= 0 ? new InputChannel(getName(), fd) : NULL;
+}
+
 
 // --- InputPublisher ---
 
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index e80b325..9bc5c14 100644
--- a/libs/hwui/Dither.cpp
+++ b/libs/hwui/Dither.cpp
@@ -21,38 +21,46 @@
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Must be a power of two
-#define DITHER_KERNEL_SIZE 4
-
-///////////////////////////////////////////////////////////////////////////////
 // Lifecycle
 ///////////////////////////////////////////////////////////////////////////////
 
 void Dither::bindDitherTexture() {
     if (!mInitialized) {
-        const uint8_t pattern[] = {
-             0,  8,  2, 10,
-            12,  4, 14,  6,
-             3, 11,  1,  9,
-            15,  7, 13,  5
-        };
+        bool useFloatTexture = Extensions::getInstance().getMajorGlVersion() >= 3;
 
         glGenTextures(1, &mDitherTexture);
         glBindTexture(GL_TEXTURE_2D, mDitherTexture);
 
-        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
 
-        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0,
-                GL_ALPHA, GL_UNSIGNED_BYTE, &pattern);
+        if (useFloatTexture) {
+            float dither = 1.0f / (255.0f * DITHER_KERNEL_SIZE * DITHER_KERNEL_SIZE);
+            const GLfloat pattern[] = {
+                 0 * dither,  8 * dither,  2 * dither, 10 * dither,
+                12 * dither,  4 * dither, 14 * dither,  6 * dither,
+                 3 * dither, 11 * dither,  1 * dither,  9 * dither,
+                15 * dither,  7 * dither, 13 * dither,  5 * dither
+            };
+
+            glPixelStorei(GL_UNPACK_ALIGNMENT, sizeof(GLfloat));
+            glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0,
+                    GL_RED, GL_FLOAT, &pattern);
+        } else {
+            const uint8_t pattern[] = {
+                 0,  8,  2, 10,
+                12,  4, 14,  6,
+                 3, 11,  1,  9,
+                15,  7, 13,  5
+            };
+
+            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+            glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0,
+                    GL_ALPHA, GL_UNSIGNED_BYTE, &pattern);
+        }
 
         mInitialized = true;
     } else {
@@ -76,10 +84,7 @@
 
     bindDitherTexture();
 
-    float ditherSize = 1.0f / DITHER_KERNEL_SIZE;
     glUniform1i(program->getUniform("ditherSampler"), textureSlot);
-    glUniform1f(program->getUniform("ditherSize"), ditherSize);
-    glUniform1f(program->getUniform("ditherSizeSquared"), ditherSize * ditherSize);
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h
index 34cf9bf..4d1f921 100644
--- a/libs/hwui/Dither.h
+++ b/libs/hwui/Dither.h
@@ -17,13 +17,23 @@
 #ifndef ANDROID_HWUI_DITHER_H
 #define ANDROID_HWUI_DITHER_H
 
-#include <GLES2/gl2.h>
+#include <GLES3/gl3.h>
 
 #include "Program.h"
 
 namespace android {
 namespace uirenderer {
 
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Must be a power of two
+#define DITHER_KERNEL_SIZE 4
+// These must not use the .0f notation as they are used from GLSL
+#define DITHER_KERNEL_SIZE_INV (1.0 / 4.0)
+#define DITHER_KERNEL_SIZE_INV_SQUARE (1.0 / 16.0)
+
 /**
  * Handles dithering for programs.
  */
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index d681609..eef366c 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -27,13 +27,6 @@
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#define GRADIENT_TEXTURE_HEIGHT 2
-#define GRADIENT_BYTES_PER_PIXEL 4
-
-///////////////////////////////////////////////////////////////////////////////
 // Functions
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -83,6 +76,10 @@
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
 
     mCache.setOnEntryRemovedListener(this);
+
+    const Extensions& extensions = Extensions::getInstance();
+    mUseFloatTexture = extensions.getMajorGlVersion() >= 3;
+    mHasNpot = extensions.hasNPot();
 }
 
 GradientCache::GradientCache(uint32_t maxByteSize):
@@ -120,7 +117,7 @@
 
 void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
     if (texture) {
-        const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
+        const uint32_t size = texture->width * texture->height * bytesPerPixel();
         mSize -= size;
 
         glDeleteTextures(1, &texture->id);
@@ -151,7 +148,7 @@
         GradientInfo& info) {
     uint32_t width = 256 * (count - 1);
 
-    if (!Extensions::getInstance().hasNPot()) {
+    if (!mHasNpot) {
         width = 1 << (31 - __builtin_clz(width));
     }
 
@@ -175,12 +172,12 @@
 
     Texture* texture = new Texture;
     texture->width = info.width;
-    texture->height = GRADIENT_TEXTURE_HEIGHT;
+    texture->height = 2;
     texture->blend = info.hasAlpha;
     texture->generation = 1;
 
     // Asume the cache is always big enough
-    const uint32_t size = texture->width * texture->height * GRADIENT_BYTES_PER_PIXEL;
+    const uint32_t size = texture->width * texture->height * bytesPerPixel();
     while (getSize() + size > mMaxSize) {
         mCache.removeOldest();
     }
@@ -193,69 +190,110 @@
     return texture;
 }
 
+size_t GradientCache::bytesPerPixel() const {
+    // We use 4 channels (RGBA)
+    return 4 * (mUseFloatTexture ? sizeof(float) : sizeof(uint8_t));
+}
+
+void GradientCache::splitToBytes(uint32_t inColor, GradientColor& outColor) const {
+    outColor.r = (inColor >> 16) & 0xff;
+    outColor.g = (inColor >>  8) & 0xff;
+    outColor.b = (inColor >>  0) & 0xff;
+    outColor.a = (inColor >> 24) & 0xff;
+}
+
+void GradientCache::splitToFloats(uint32_t inColor, GradientColor& outColor) const {
+    outColor.r = ((inColor >> 16) & 0xff) / 255.0f;
+    outColor.g = ((inColor >>  8) & 0xff) / 255.0f;
+    outColor.b = ((inColor >>  0) & 0xff) / 255.0f;
+    outColor.a = ((inColor >> 24) & 0xff) / 255.0f;
+}
+
+void GradientCache::mixBytes(GradientColor& start, GradientColor& end, float amount,
+        uint8_t*& dst) const {
+    float oppAmount = 1.0f - amount;
+    const float alpha = start.a * oppAmount + end.a * amount;
+    const float a = alpha / 255.0f;
+
+    *dst++ = uint8_t(a * (start.r * oppAmount + end.r * amount));
+    *dst++ = uint8_t(a * (start.g * oppAmount + end.g * amount));
+    *dst++ = uint8_t(a * (start.b * oppAmount + end.b * amount));
+    *dst++ = uint8_t(alpha);
+}
+
+void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float amount,
+        uint8_t*& dst) const {
+    float oppAmount = 1.0f - amount;
+    const float a = start.a * oppAmount + end.a * amount;
+
+    float* d = (float*) dst;
+    *d++ = a * (start.r * oppAmount + end.r * amount);
+    *d++ = a * (start.g * oppAmount + end.g * amount);
+    *d++ = a * (start.b * oppAmount + end.b * amount);
+    *d++ = a;
+
+    dst += 4 * sizeof(float);
+}
+
 void GradientCache::generateTexture(uint32_t* colors, float* positions,
         int count, Texture* texture) {
-
     const uint32_t width = texture->width;
-    const GLsizei rowBytes = width * GRADIENT_BYTES_PER_PIXEL;
-    uint32_t pixels[width * texture->height];
+    const GLsizei rowBytes = width * bytesPerPixel();
+    uint8_t pixels[rowBytes * texture->height];
+
+    static ChannelSplitter gSplitters[] = {
+            &android::uirenderer::GradientCache::splitToBytes,
+            &android::uirenderer::GradientCache::splitToFloats,
+    };
+    ChannelSplitter split = gSplitters[mUseFloatTexture];
+
+    static ChannelMixer gMixers[] = {
+            &android::uirenderer::GradientCache::mixBytes,
+            &android::uirenderer::GradientCache::mixFloats,
+    };
+    ChannelMixer mix = gMixers[mUseFloatTexture];
+
+    GradientColor start;
+    (this->*split)(colors[0], start);
+
+    GradientColor end;
+    (this->*split)(colors[1], end);
 
     int currentPos = 1;
+    float startPos = positions[0];
+    float distance = positions[1] - startPos;
 
-    float startA = (colors[0] >> 24) & 0xff;
-    float startR = (colors[0] >> 16) & 0xff;
-    float startG = (colors[0] >>  8) & 0xff;
-    float startB = (colors[0] >>  0) & 0xff;
-
-    float endA = (colors[1] >> 24) & 0xff;
-    float endR = (colors[1] >> 16) & 0xff;
-    float endG = (colors[1] >>  8) & 0xff;
-    float endB = (colors[1] >>  0) & 0xff;
-
-    float start = positions[0];
-    float distance = positions[1] - start;
-
-    uint8_t* p = (uint8_t*) pixels;
+    uint8_t* dst = pixels;
     for (uint32_t x = 0; x < width; x++) {
         float pos = x / float(width - 1);
         if (pos > positions[currentPos]) {
-            startA = endA;
-            startR = endR;
-            startG = endG;
-            startB = endB;
-            start = positions[currentPos];
+            start = end;
+            startPos = positions[currentPos];
 
             currentPos++;
 
-            endA = (colors[currentPos] >> 24) & 0xff;
-            endR = (colors[currentPos] >> 16) & 0xff;
-            endG = (colors[currentPos] >>  8) & 0xff;
-            endB = (colors[currentPos] >>  0) & 0xff;
-            distance = positions[currentPos] - start;
+            (this->*split)(colors[currentPos], end);
+            distance = positions[currentPos] - startPos;
         }
 
-        float amount = (pos - start) / distance;
-        float oppAmount = 1.0f - amount;
-
-        const float alpha = startA * oppAmount + endA * amount;
-        const float a = alpha / 255.0f;
-        *p++ = uint8_t(a * (startR * oppAmount + endR * amount));
-        *p++ = uint8_t(a * (startG * oppAmount + endG * amount));
-        *p++ = uint8_t(a * (startB * oppAmount + endB * amount));
-        *p++ = uint8_t(alpha);
+        float amount = (pos - startPos) / distance;
+        (this->*mix)(start, end, amount, dst);
     }
 
-    for (int i = 1; i < GRADIENT_TEXTURE_HEIGHT; i++) {
-        memcpy(pixels + width * i, pixels, rowBytes);
-    }
+    memcpy(pixels + rowBytes, pixels, rowBytes);
 
     glGenTextures(1, &texture->id);
-
     glBindTexture(GL_TEXTURE_2D, texture->id);
-    glPixelStorei(GL_UNPACK_ALIGNMENT, GRADIENT_BYTES_PER_PIXEL);
+    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
 
-    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
-            GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+    if (mUseFloatTexture) {
+        // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, texture->height, 0,
+                GL_RGBA, GL_FLOAT, pixels);
+    } else {
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0,
+                GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+    }
 
     texture->setFilter(GL_LINEAR);
     texture->setWrap(GL_CLAMP_TO_EDGE);
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 7dc5b8a..43934d9 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_HWUI_GRADIENT_CACHE_H
 #define ANDROID_HWUI_GRADIENT_CACHE_H
 
-#include <GLES2/gl2.h>
+#include <GLES3/gl3.h>
 
 #include <SkShader.h>
 
@@ -160,12 +160,35 @@
 
     void getGradientInfo(const uint32_t* colors, const int count, GradientInfo& info);
 
+    size_t bytesPerPixel() const;
+
+    struct GradientColor {
+        float r;
+        float g;
+        float b;
+        float a;
+    };
+
+    typedef void (GradientCache::*ChannelSplitter)(uint32_t inColor,
+            GradientColor& outColor) const;
+
+    void splitToBytes(uint32_t inColor, GradientColor& outColor) const;
+    void splitToFloats(uint32_t inColor, GradientColor& outColor) const;
+
+    typedef void (GradientCache::*ChannelMixer)(GradientColor& start, GradientColor& end,
+            float amount, uint8_t*& dst) const;
+
+    void mixBytes(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const;
+    void mixFloats(GradientColor& start, GradientColor& end, float amount, uint8_t*& dst) const;
+
     LruCache<GradientCacheEntry, Texture*> mCache;
 
     uint32_t mSize;
     uint32_t mMaxSize;
 
     GLint mMaxTextureSize;
+    bool mUseFloatTexture;
+    bool mHasNpot;
 
     Vector<SkShader*> mGarbage;
     mutable Mutex mLock;
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index f78fb2d..2479630 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -19,6 +19,7 @@
 #include <utils/String8.h>
 
 #include "Caches.h"
+#include "Dither.h"
 #include "ProgramCache.h"
 
 namespace android {
@@ -32,6 +33,9 @@
 #define MODULATE_OP_MODULATE 1
 #define MODULATE_OP_MODULATE_A8 2
 
+#define STR(x) STR1(x)
+#define STR1(x) #x
+
 ///////////////////////////////////////////////////////////////////////////////
 // Vertex shaders snippets
 ///////////////////////////////////////////////////////////////////////////////
@@ -51,17 +55,8 @@
         "uniform mat4 transform;\n";
 const char* gVS_Header_Uniforms_IsPoint =
         "uniform mediump float pointSize;\n";
-const char* gVS_Header_Uniforms_HasGradient[3] = {
-        // Linear
-        "uniform mat4 screenSpace;\n"
-        "uniform float ditherSize;\n",
-        // Circular
-        "uniform mat4 screenSpace;\n"
-        "uniform float ditherSize;\n",
-        // Sweep
-        "uniform mat4 screenSpace;\n"
-        "uniform float ditherSize;\n"
-};
+const char* gVS_Header_Uniforms_HasGradient =
+        "uniform mat4 screenSpace;\n";
 const char* gVS_Header_Uniforms_HasBitmap =
         "uniform mat4 textureTransform;\n"
         "uniform mediump vec2 textureDimension;\n";
@@ -105,21 +100,21 @@
 const char* gVS_Main_OutGradient[6] = {
         // Linear
         "    linear = vec2((screenSpace * position).x, 0.5);\n"
-        "    ditherTexCoords = (transform * position).xy * ditherSize;\n",
+        "    ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
         "    linear = (screenSpace * position).x;\n"
-        "    ditherTexCoords = (transform * position).xy * ditherSize;\n",
+        "    ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
 
         // Circular
         "    circular = (screenSpace * position).xy;\n"
-        "    ditherTexCoords = (transform * position).xy * ditherSize;\n",
+        "    ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
         "    circular = (screenSpace * position).xy;\n"
-        "    ditherTexCoords = (transform * position).xy * ditherSize;\n",
+        "    ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
 
         // Sweep
         "    sweep = (screenSpace * position).xy;\n"
-        "    ditherTexCoords = (transform * position).xy * ditherSize;\n",
+        "    ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
         "    sweep = (screenSpace * position).xy;\n"
-        "    ditherTexCoords = (transform * position).xy * ditherSize;\n",
+        "    ditherTexCoords = (transform * position).xy * " STR(DITHER_KERNEL_SIZE_INV) ";\n",
 };
 const char* gVS_Main_OutBitmapTexCoords =
         "    outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
@@ -153,24 +148,14 @@
         "uniform sampler2D baseSampler;\n";
 const char* gFS_Uniforms_ExternalTextureSampler =
         "uniform samplerExternalOES baseSampler;\n";
-#define FS_UNIFORMS_DITHER \
-        "uniform float ditherSizeSquared;\n" \
-        "uniform sampler2D ditherSampler;\n"
-#define FS_UNIFORMS_GRADIENT \
-        "uniform vec4 startColor;\n" \
+const char* gFS_Uniforms_Dither =
+        "uniform sampler2D ditherSampler;";
+const char* gFS_Uniforms_GradientSampler[2] = {
+        "%s\n"
+        "uniform sampler2D gradientSampler;\n",
+        "%s\n"
+        "uniform vec4 startColor;\n"
         "uniform vec4 endColor;\n"
-const char* gFS_Uniforms_GradientSampler[6] = {
-        // Linear
-        FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
-        FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT,
-
-        // Circular
-        FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
-        FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT,
-
-        // Sweep
-        FS_UNIFORMS_DITHER "uniform sampler2D gradientSampler;\n",
-        FS_UNIFORMS_DITHER FS_UNIFORMS_GRADIENT
 };
 const char* gFS_Uniforms_BitmapSampler =
         "uniform sampler2D bitmapSampler;\n";
@@ -197,10 +182,14 @@
         "    highp vec2 outBitmapTexCoords = outPointBitmapTexCoords + "
         "((gl_PointCoord - vec2(0.5, 0.5)) * textureDimension * vec2(pointSize, pointSize));\n";
 
-#define FS_MAIN_DITHER \
-        "texture2D(ditherSampler, ditherTexCoords).a * ditherSizeSquared"
+const char* gFS_Main_Dither[2] = {
+        // ES 2.0
+        "texture2D(ditherSampler, ditherTexCoords).a * " STR(DITHER_KERNEL_SIZE_INV_SQUARE),
+        // ES 3.0
+        "texture2D(ditherSampler, ditherTexCoords).r"
+};
 const char* gFS_Main_AddDitherToGradient =
-        "    gradientColor += " FS_MAIN_DITHER ";\n";
+        "    gradientColor += %s;\n";
 
 // Fast cases
 const char* gFS_Fast_SingleColor =
@@ -233,18 +222,18 @@
         "}\n\n";
 const char* gFS_Fast_SingleGradient[2] = {
         "\nvoid main(void) {\n"
-        "    gl_FragColor = " FS_MAIN_DITHER " + texture2D(gradientSampler, linear);\n"
+        "    gl_FragColor = %s + texture2D(gradientSampler, linear);\n"
         "}\n\n",
         "\nvoid main(void) {\n"
-        "    gl_FragColor = " FS_MAIN_DITHER " + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
-        "}\n\n"
+        "    gl_FragColor = %s + mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
+        "}\n\n",
 };
 const char* gFS_Fast_SingleModulateGradient[2] = {
         "\nvoid main(void) {\n"
-        "    gl_FragColor = " FS_MAIN_DITHER " + color.a * texture2D(gradientSampler, linear);\n"
+        "    gl_FragColor = %s + color.a * texture2D(gradientSampler, linear);\n"
         "}\n\n",
         "\nvoid main(void) {\n"
-        "    gl_FragColor = " FS_MAIN_DITHER " + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
+        "    gl_FragColor = %s + color.a * mix(startColor, endColor, clamp(linear, 0.0, 1.0));\n"
         "}\n\n"
 };
 
@@ -410,7 +399,7 @@
 // Constructors/destructors
 ///////////////////////////////////////////////////////////////////////////////
 
-ProgramCache::ProgramCache() {
+ProgramCache::ProgramCache(): mHasES3(Extensions::getInstance().getMajorGlVersion() >= 3) {
 }
 
 ProgramCache::~ProgramCache() {
@@ -484,7 +473,7 @@
         shader.append(gVS_Header_Uniforms_TextureTransform);
     }
     if (description.hasGradient) {
-        shader.append(gVS_Header_Uniforms_HasGradient[description.gradientType]);
+        shader.append(gVS_Header_Uniforms_HasGradient);
     }
     if (description.hasBitmap) {
         shader.append(gVS_Header_Uniforms_HasBitmap);
@@ -601,7 +590,8 @@
         shader.append(gFS_Uniforms_ExternalTextureSampler);
     }
     if (description.hasGradient) {
-        shader.append(gFS_Uniforms_GradientSampler[gradientIndex(description)]);
+        shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient],
+                gFS_Uniforms_Dither);
     }
     if (description.hasBitmap && description.isPoint) {
         shader.append(gFS_Header_Uniforms_PointHasBitmap);
@@ -652,9 +642,11 @@
             fast = true;
         } else if (singleGradient) {
             if (!description.modulate) {
-                shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]);
+                shader.appendFormat(gFS_Fast_SingleGradient[description.isSimpleGradient],
+                        gFS_Main_Dither[mHasES3]);
             } else {
-                shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]);
+                shader.appendFormat(gFS_Fast_SingleModulateGradient[description.isSimpleGradient],
+                        gFS_Main_Dither[mHasES3]);
             }
             fast = true;
         }
@@ -708,7 +700,7 @@
         }
         if (description.hasGradient) {
             shader.append(gFS_Main_FetchGradient[gradientIndex(description)]);
-            shader.append(gFS_Main_AddDitherToGradient);
+            shader.appendFormat(gFS_Main_AddDitherToGradient, gFS_Main_Dither[mHasES3]);
         }
         if (description.hasBitmap) {
             if (description.isPoint) {
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 1ca148d..38f6f99 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -57,6 +57,8 @@
     void printLongString(const String8& shader) const;
 
     KeyedVector<programid, Program*> mCache;
+
+    const bool mHasES3;
 }; // class ProgramCache
 
 }; // namespace uirenderer
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 43d76bb..602afd4 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -882,8 +882,9 @@
                 snprintf(name, sizeof(name), "%d", range.axis);
             }
             dump.appendFormat(INDENT3 "%s: source=0x%08x, "
-                    "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n",
-                    name, range.source, range.min, range.max, range.flat, range.fuzz);
+                    "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n",
+                    name, range.source, range.min, range.max, range.flat, range.fuzz,
+                    range.resolution);
         }
     }
 
@@ -2247,20 +2248,20 @@
     if (mParameters.mode == Parameters::MODE_POINTER) {
         float minX, minY, maxX, maxY;
         if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
-            info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f);
-            info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f);
+            info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
+            info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f);
         }
     } else {
-        info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale);
-        info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale);
+        info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
+        info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
     }
-    info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f);
+    info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
 
     if (mCursorScrollAccumulator.haveRelativeVWheel()) {
-        info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
+        info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
     }
     if (mCursorScrollAccumulator.haveRelativeHWheel()) {
-        info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
+        info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
     }
 }
 
@@ -2611,10 +2612,12 @@
         }
 
         if (mCursorScrollAccumulator.haveRelativeVWheel()) {
-            info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
+            info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
+                    0.0f);
         }
         if (mCursorScrollAccumulator.haveRelativeHWheel()) {
-            info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f);
+            info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
+                    0.0f);
         }
     }
 }
@@ -3063,6 +3066,7 @@
             mOrientedRanges.touchMajor.max = diagonalSize;
             mOrientedRanges.touchMajor.flat = 0;
             mOrientedRanges.touchMajor.fuzz = 0;
+            mOrientedRanges.touchMajor.resolution = 0;
 
             mOrientedRanges.touchMinor = mOrientedRanges.touchMajor;
             mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR;
@@ -3073,6 +3077,7 @@
             mOrientedRanges.toolMajor.max = diagonalSize;
             mOrientedRanges.toolMajor.flat = 0;
             mOrientedRanges.toolMajor.fuzz = 0;
+            mOrientedRanges.toolMajor.resolution = 0;
 
             mOrientedRanges.toolMinor = mOrientedRanges.toolMajor;
             mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR;
@@ -3083,6 +3088,7 @@
             mOrientedRanges.size.max = 1.0;
             mOrientedRanges.size.flat = 0;
             mOrientedRanges.size.fuzz = 0;
+            mOrientedRanges.size.resolution = 0;
         } else {
             mSizeScale = 0.0f;
         }
@@ -3106,6 +3112,7 @@
         mOrientedRanges.pressure.max = 1.0;
         mOrientedRanges.pressure.flat = 0;
         mOrientedRanges.pressure.fuzz = 0;
+        mOrientedRanges.pressure.resolution = 0;
 
         // Tilt
         mTiltXCenter = 0;
@@ -3129,6 +3136,7 @@
             mOrientedRanges.tilt.max = M_PI_2;
             mOrientedRanges.tilt.flat = 0;
             mOrientedRanges.tilt.fuzz = 0;
+            mOrientedRanges.tilt.resolution = 0;
         }
 
         // Orientation
@@ -3142,6 +3150,7 @@
             mOrientedRanges.orientation.max = M_PI;
             mOrientedRanges.orientation.flat = 0;
             mOrientedRanges.orientation.fuzz = 0;
+            mOrientedRanges.orientation.resolution = 0;
         } else if (mCalibration.orientationCalibration !=
                 Calibration::ORIENTATION_CALIBRATION_NONE) {
             if (mCalibration.orientationCalibration
@@ -3165,6 +3174,7 @@
             mOrientedRanges.orientation.max = M_PI_2;
             mOrientedRanges.orientation.flat = 0;
             mOrientedRanges.orientation.fuzz = 0;
+            mOrientedRanges.orientation.resolution = 0;
         }
 
         // Distance
@@ -3190,6 +3200,7 @@
             mOrientedRanges.distance.flat = 0;
             mOrientedRanges.distance.fuzz =
                     mRawPointerAxes.distance.fuzz * mDistanceScale;
+            mOrientedRanges.distance.resolution = 0;
         }
 
         // Compute oriented precision, scales and ranges.
@@ -3204,12 +3215,14 @@
             mOrientedRanges.x.min = mYTranslate;
             mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1;
             mOrientedRanges.x.flat = 0;
-            mOrientedRanges.x.fuzz = mYScale;
+            mOrientedRanges.x.fuzz = 0;
+            mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
 
             mOrientedRanges.y.min = mXTranslate;
             mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1;
             mOrientedRanges.y.flat = 0;
-            mOrientedRanges.y.fuzz = mXScale;
+            mOrientedRanges.y.fuzz = 0;
+            mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
             break;
 
         default:
@@ -3219,12 +3232,14 @@
             mOrientedRanges.x.min = mXTranslate;
             mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1;
             mOrientedRanges.x.flat = 0;
-            mOrientedRanges.x.fuzz = mXScale;
+            mOrientedRanges.x.fuzz = 0;
+            mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
 
             mOrientedRanges.y.min = mYTranslate;
             mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1;
             mOrientedRanges.y.flat = 0;
-            mOrientedRanges.y.fuzz = mYScale;
+            mOrientedRanges.y.fuzz = 0;
+            mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
             break;
         }
 
@@ -6045,10 +6060,10 @@
     for (size_t i = 0; i < mAxes.size(); i++) {
         const Axis& axis = mAxes.valueAt(i);
         info->addMotionRange(axis.axisInfo.axis, AINPUT_SOURCE_JOYSTICK,
-                axis.min, axis.max, axis.flat, axis.fuzz);
+                axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
         if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) {
             info->addMotionRange(axis.axisInfo.highAxis, AINPUT_SOURCE_JOYSTICK,
-                    axis.min, axis.max, axis.flat, axis.fuzz);
+                    axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
         }
     }
 }
@@ -6078,8 +6093,8 @@
             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(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n",
+                axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution);
         dump.appendFormat(INDENT4 "  scale=%0.5f, offset=%0.5f, "
                 "highScale=%0.5f, highOffset=%0.5f\n",
                 axis.scale, axis.offset, axis.highScale, axis.highOffset);
@@ -6125,18 +6140,21 @@
                     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);
+                            0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+                            rawAxisInfo.resolution * 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, axisInfo, explicitlyMapped,
                             scale, offset, scale, offset,
-                            -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
+                            -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+                            rawAxisInfo.resolution * scale);
                 } else {
                     float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue);
                     axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped,
                             scale, 0.0f, scale, 0.0f,
-                            0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale);
+                            0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale,
+                            rawAxisInfo.resolution * scale);
                 }
 
                 // To eliminate noise while the joystick is at rest, filter out small variations
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index c596b37..8a52c06 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -1730,10 +1730,11 @@
         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 min;        // normalized inclusive minimum
+        float max;        // normalized inclusive maximum
+        float flat;       // normalized flat region size
+        float fuzz;       // normalized error tolerance
+        float resolution; // normalized resolution in units/mm
 
         float filter;  // filter out small variations of this size
         float currentValue; // current value
@@ -1744,7 +1745,7 @@
         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) {
+                float min, float max, float flat, float fuzz, float resolution) {
             this->rawAxisInfo = rawAxisInfo;
             this->axisInfo = axisInfo;
             this->explicitlyMapped = explicitlyMapped;
@@ -1756,6 +1757,7 @@
             this->max = max;
             this->flat = flat;
             this->fuzz = fuzz;
+            this->resolution = resolution;
             this->filter = 0;
             resetValue();
         }
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 3b541ec..a28c387 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1070,7 +1070,8 @@
             if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
             showCurrentInputLocked(getAppShowFlags(), null);
         }
-        return new InputBindResult(session.session, session.channel, mCurId, mCurSeq);
+        return new InputBindResult(session.session,
+                session.channel != null ? session.channel.dup() : null, mCurId, mCurSeq);
     }
 
     InputBindResult startInputLocked(IInputMethodClient client,
@@ -2357,13 +2358,15 @@
                 return true;
             case MSG_CREATE_SESSION: {
                 args = (SomeArgs)msg.obj;
+                IInputMethod method = (IInputMethod)args.arg1;
                 InputChannel channel = (InputChannel)args.arg2;
                 try {
-                    ((IInputMethod)args.arg1).createSession(channel,
-                            (IInputSessionCallback)args.arg3);
+                    method.createSession(channel, (IInputSessionCallback)args.arg3);
                 } catch (RemoteException e) {
                 } finally {
-                    if (channel != null) {
+                    // Dispose the channel if the input method is not local to this process
+                    // because the remote proxy will get its own copy when unparceled.
+                    if (channel != null && Binder.isProxy(method)) {
                         channel.dispose();
                     }
                 }
@@ -2404,16 +2407,24 @@
                     // There is nothing interesting about the last client dying.
                 }
                 return true;
-            case MSG_BIND_METHOD:
+            case MSG_BIND_METHOD: {
                 args = (SomeArgs)msg.obj;
+                IInputMethodClient client = (IInputMethodClient)args.arg1;
+                InputBindResult res = (InputBindResult)args.arg2;
                 try {
-                    ((IInputMethodClient)args.arg1).onBindMethod(
-                            (InputBindResult)args.arg2);
+                    client.onBindMethod(res);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Client died receiving input method " + args.arg2);
+                } finally {
+                    // Dispose the channel if the input method is not local to this process
+                    // because the remote proxy will get its own copy when unparceled.
+                    if (res.channel != null && Binder.isProxy(client)) {
+                        res.channel.dispose();
+                    }
                 }
                 args.recycle();
                 return true;
+            }
             case MSG_SET_ACTIVE:
                 try {
                     ((ClientState)msg.obj).client.setActive(msg.arg1 != 0);