Fix off by one errors in touch motion ranges.  (DO NOT MERGE)

Report inclusive minimum and maximum ranges for all
axes including X and Y.

Set mouse pointer bounds to 0..width-1, 0..height-1.

Rotate touch and mouse positions more carefully, paying attention
to the maximum bounds when calculating the complement of an axis.

Simplified the InputReader somewhat and removed support for a
couple of poorly defined input device configuration parameters.
We now assume that the touch device provides useful absolute axis
ranges for the X and Y axes since the alternative does not actually
make sense.

Bug: 3413541
Change-Id: I121d28a125c4f9618cb283dc460d33ff1a907023
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index ab42a1f..7053a94 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -85,8 +85,6 @@
     int32_t flat;      // center flat position, eg. flat == 8 means center is between -8 and 8
     int32_t fuzz;      // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise
 
-    inline int32_t getRange() const { return maxValue - minValue; }
-
     inline void clear() {
         valid = false;
         minValue = 0;
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index ab43a20..c620c44 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -88,6 +88,18 @@
     return value ? "true" : "false";
 }
 
+static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation,
+        const int32_t map[][4], size_t mapSize) {
+    if (orientation != DISPLAY_ORIENTATION_0) {
+        for (size_t i = 0; i < mapSize; i++) {
+            if (value == map[i][0]) {
+                return map[i][orientation];
+            }
+        }
+    }
+    return value;
+}
+
 static const int32_t keyCodeRotationMap[][4] = {
         // key codes enumerated counter-clockwise with the original (unrotated) key first
         // no rotation,        90 degree rotation,  180 degree rotation, 270 degree rotation
@@ -96,18 +108,32 @@
         { AKEYCODE_DPAD_UP,     AKEYCODE_DPAD_LEFT,   AKEYCODE_DPAD_DOWN,   AKEYCODE_DPAD_RIGHT },
         { AKEYCODE_DPAD_LEFT,   AKEYCODE_DPAD_DOWN,   AKEYCODE_DPAD_RIGHT,  AKEYCODE_DPAD_UP },
 };
-static const int keyCodeRotationMapSize =
+static const size_t keyCodeRotationMapSize =
         sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]);
 
 int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) {
-    if (orientation != DISPLAY_ORIENTATION_0) {
-        for (int i = 0; i < keyCodeRotationMapSize; i++) {
-            if (keyCode == keyCodeRotationMap[i][0]) {
-                return keyCodeRotationMap[i][orientation];
-            }
-        }
-    }
-    return keyCode;
+    return rotateValueUsingRotationMap(keyCode, orientation,
+            keyCodeRotationMap, keyCodeRotationMapSize);
+}
+
+static const int32_t edgeFlagRotationMap[][4] = {
+        // edge flags enumerated counter-clockwise with the original (unrotated) edge flag first
+        // no rotation,        90 degree rotation,  180 degree rotation, 270 degree rotation
+        { AMOTION_EVENT_EDGE_FLAG_BOTTOM,   AMOTION_EVENT_EDGE_FLAG_RIGHT,
+                AMOTION_EVENT_EDGE_FLAG_TOP,     AMOTION_EVENT_EDGE_FLAG_LEFT },
+        { AMOTION_EVENT_EDGE_FLAG_RIGHT,  AMOTION_EVENT_EDGE_FLAG_TOP,
+                AMOTION_EVENT_EDGE_FLAG_LEFT,   AMOTION_EVENT_EDGE_FLAG_BOTTOM },
+        { AMOTION_EVENT_EDGE_FLAG_TOP,     AMOTION_EVENT_EDGE_FLAG_LEFT,
+                AMOTION_EVENT_EDGE_FLAG_BOTTOM,   AMOTION_EVENT_EDGE_FLAG_RIGHT },
+        { AMOTION_EVENT_EDGE_FLAG_LEFT,   AMOTION_EVENT_EDGE_FLAG_BOTTOM,
+                AMOTION_EVENT_EDGE_FLAG_RIGHT,  AMOTION_EVENT_EDGE_FLAG_TOP },
+};
+static const size_t edgeFlagRotationMapSize =
+        sizeof(edgeFlagRotationMap) / sizeof(edgeFlagRotationMap[0]);
+
+static int32_t rotateEdgeFlag(int32_t edgeFlag, int32_t orientation) {
+    return rotateValueUsingRotationMap(edgeFlag, orientation,
+            edgeFlagRotationMap, edgeFlagRotationMapSize);
 }
 
 static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) {
@@ -1290,7 +1316,8 @@
         return; // no new state changes, so nothing to do
     }
 
-    int motionEventAction;
+    int32_t motionEventAction;
+    int32_t motionEventEdgeFlags;
     PointerCoords pointerCoords;
     nsecs_t downTime;
     float vscroll, hscroll;
@@ -1361,6 +1388,8 @@
 
         pointerCoords.clear();
 
+        motionEventEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
+
         if (mPointerController != NULL) {
             mPointerController->move(deltaX, deltaY);
             if (downChanged) {
@@ -1370,6 +1399,22 @@
             mPointerController->getPosition(&x, &y);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+
+            if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) {
+                float minX, minY, maxX, maxY;
+                if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
+                    if (x <= minX) {
+                        motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT;
+                    } else if (x >= maxX) {
+                        motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT;
+                    }
+                    if (y <= minY) {
+                        motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP;
+                    } else if (y >= maxY) {
+                        motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM;
+                    }
+                }
+            }
         } else {
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
             pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
@@ -1404,7 +1449,7 @@
     int32_t metaState = mContext->getGlobalMetaState();
     int32_t pointerId = 0;
     getDispatcher()->notifyMotion(when, getDeviceId(), mSources, policyFlags,
-            motionEventAction, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
+            motionEventAction, 0, metaState, motionEventEdgeFlags,
             1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
 
     mAccumulator.clear();
@@ -1507,8 +1552,6 @@
         dumpCalibration(dump);
         dumpSurfaceLocked(dump);
         dump.appendFormat(INDENT3 "Translation and Scaling Factors:\n");
-        dump.appendFormat(INDENT4 "XOrigin: %d\n", mLocked.xOrigin);
-        dump.appendFormat(INDENT4 "YOrigin: %d\n", mLocked.yOrigin);
         dump.appendFormat(INDENT4 "XScale: %0.3f\n", mLocked.xScale);
         dump.appendFormat(INDENT4 "YScale: %0.3f\n", mLocked.yScale);
         dump.appendFormat(INDENT4 "XPrecision: %0.3f\n", mLocked.xPrecision);
@@ -1654,10 +1697,17 @@
 }
 
 bool TouchInputMapper::configureSurfaceLocked() {
+    // Ensure we have valid X and Y axes.
+    if (!mRawAxes.x.valid || !mRawAxes.y.valid) {
+        LOGW(INDENT "Touch device '%s' did not report support for X or Y axis!  "
+                "The device will be inoperable.", getDeviceName().string());
+        return false;
+    }
+
     // Update orientation and dimensions if needed.
     int32_t orientation = DISPLAY_ORIENTATION_0;
-    int32_t width = mRawAxes.x.getRange();
-    int32_t height = mRawAxes.y.getRange();
+    int32_t width = mRawAxes.x.maxValue - mRawAxes.x.minValue + 1;
+    int32_t height = mRawAxes.y.maxValue - mRawAxes.y.minValue + 1;
 
     if (mParameters.associatedDisplayId >= 0) {
         bool wantSize = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN;
@@ -1685,32 +1735,12 @@
         mLocked.surfaceHeight = height;
 
         // Configure X and Y factors.
-        if (mRawAxes.x.valid && mRawAxes.y.valid) {
-            mLocked.xOrigin = mCalibration.haveXOrigin
-                    ? mCalibration.xOrigin
-                    : mRawAxes.x.minValue;
-            mLocked.yOrigin = mCalibration.haveYOrigin
-                    ? mCalibration.yOrigin
-                    : mRawAxes.y.minValue;
-            mLocked.xScale = mCalibration.haveXScale
-                    ? mCalibration.xScale
-                    : float(width) / mRawAxes.x.getRange();
-            mLocked.yScale = mCalibration.haveYScale
-                    ? mCalibration.yScale
-                    : float(height) / mRawAxes.y.getRange();
-            mLocked.xPrecision = 1.0f / mLocked.xScale;
-            mLocked.yPrecision = 1.0f / mLocked.yScale;
+        mLocked.xScale = float(width) / (mRawAxes.x.maxValue - mRawAxes.x.minValue + 1);
+        mLocked.yScale = float(height) / (mRawAxes.y.maxValue - mRawAxes.y.minValue + 1);
+        mLocked.xPrecision = 1.0f / mLocked.xScale;
+        mLocked.yPrecision = 1.0f / mLocked.yScale;
 
-            configureVirtualKeysLocked();
-        } else {
-            LOGW(INDENT "Touch device did not report support for X or Y axis!");
-            mLocked.xOrigin = 0;
-            mLocked.yOrigin = 0;
-            mLocked.xScale = 1.0f;
-            mLocked.yScale = 1.0f;
-            mLocked.xPrecision = 1.0f;
-            mLocked.yPrecision = 1.0f;
-        }
+        configureVirtualKeysLocked();
 
         // Scale factor for terms that are not oriented in a particular axis.
         // If the pixels are square then xScale == yScale otherwise we fake it
@@ -1844,38 +1874,51 @@
     }
 
     if (orientationChanged || sizeChanged) {
-        // Compute oriented surface dimensions, precision, and scales.
-        float orientedXScale, orientedYScale;
+        // Compute oriented surface dimensions, precision, scales and ranges.
+        // Note that the maximum value reported is an inclusive maximum value so it is one
+        // unit less than the total width or height of surface.
         switch (mLocked.surfaceOrientation) {
         case DISPLAY_ORIENTATION_90:
         case DISPLAY_ORIENTATION_270:
             mLocked.orientedSurfaceWidth = mLocked.surfaceHeight;
             mLocked.orientedSurfaceHeight = mLocked.surfaceWidth;
+
             mLocked.orientedXPrecision = mLocked.yPrecision;
             mLocked.orientedYPrecision = mLocked.xPrecision;
-            orientedXScale = mLocked.yScale;
-            orientedYScale = mLocked.xScale;
+
+            mLocked.orientedRanges.x.min = 0;
+            mLocked.orientedRanges.x.max = (mRawAxes.y.maxValue - mRawAxes.y.minValue)
+                    * mLocked.yScale;
+            mLocked.orientedRanges.x.flat = 0;
+            mLocked.orientedRanges.x.fuzz = mLocked.yScale;
+
+            mLocked.orientedRanges.y.min = 0;
+            mLocked.orientedRanges.y.max = (mRawAxes.x.maxValue - mRawAxes.x.minValue)
+                    * mLocked.xScale;
+            mLocked.orientedRanges.y.flat = 0;
+            mLocked.orientedRanges.y.fuzz = mLocked.xScale;
             break;
+
         default:
             mLocked.orientedSurfaceWidth = mLocked.surfaceWidth;
             mLocked.orientedSurfaceHeight = mLocked.surfaceHeight;
+
             mLocked.orientedXPrecision = mLocked.xPrecision;
             mLocked.orientedYPrecision = mLocked.yPrecision;
-            orientedXScale = mLocked.xScale;
-            orientedYScale = mLocked.yScale;
+
+            mLocked.orientedRanges.x.min = 0;
+            mLocked.orientedRanges.x.max = (mRawAxes.x.maxValue - mRawAxes.x.minValue)
+                    * mLocked.xScale;
+            mLocked.orientedRanges.x.flat = 0;
+            mLocked.orientedRanges.x.fuzz = mLocked.xScale;
+
+            mLocked.orientedRanges.y.min = 0;
+            mLocked.orientedRanges.y.max = (mRawAxes.y.maxValue - mRawAxes.y.minValue)
+                    * mLocked.yScale;
+            mLocked.orientedRanges.y.flat = 0;
+            mLocked.orientedRanges.y.fuzz = mLocked.yScale;
             break;
         }
-
-        // Configure position ranges.
-        mLocked.orientedRanges.x.min = 0;
-        mLocked.orientedRanges.x.max = mLocked.orientedSurfaceWidth;
-        mLocked.orientedRanges.x.flat = 0;
-        mLocked.orientedRanges.x.fuzz = orientedXScale;
-
-        mLocked.orientedRanges.y.min = 0;
-        mLocked.orientedRanges.y.max = mLocked.orientedSurfaceHeight;
-        mLocked.orientedRanges.y.flat = 0;
-        mLocked.orientedRanges.y.fuzz = orientedYScale;
     }
 
     return true;
@@ -1888,8 +1931,6 @@
 }
 
 void TouchInputMapper::configureVirtualKeysLocked() {
-    assert(mRawAxes.x.valid && mRawAxes.y.valid);
-
     Vector<VirtualKeyDefinition> virtualKeyDefinitions;
     getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions);
 
@@ -1903,8 +1944,8 @@
 
     int32_t touchScreenLeft = mRawAxes.x.minValue;
     int32_t touchScreenTop = mRawAxes.y.minValue;
-    int32_t touchScreenWidth = mRawAxes.x.getRange();
-    int32_t touchScreenHeight = mRawAxes.y.getRange();
+    int32_t touchScreenWidth = mRawAxes.x.maxValue - mRawAxes.x.minValue + 1;
+    int32_t touchScreenHeight = mRawAxes.y.maxValue - mRawAxes.y.minValue + 1;
 
     for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) {
         const VirtualKeyDefinition& virtualKeyDefinition =
@@ -1939,7 +1980,6 @@
                 * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
         virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight)
                 * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop;
-
     }
 }
 
@@ -1962,12 +2002,6 @@
     const PropertyMap& in = getDevice()->getConfiguration();
     Calibration& out = mCalibration;
 
-    // Position
-    out.haveXOrigin = in.tryGetProperty(String8("touch.position.xOrigin"), out.xOrigin);
-    out.haveYOrigin = in.tryGetProperty(String8("touch.position.yOrigin"), out.yOrigin);
-    out.haveXScale = in.tryGetProperty(String8("touch.position.xScale"), out.xScale);
-    out.haveYScale = in.tryGetProperty(String8("touch.position.yScale"), out.yScale);
-
     // Touch Size
     out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_DEFAULT;
     String8 touchSizeCalibrationString;
@@ -2179,20 +2213,6 @@
 void TouchInputMapper::dumpCalibration(String8& dump) {
     dump.append(INDENT3 "Calibration:\n");
 
-    // Position
-    if (mCalibration.haveXOrigin) {
-        dump.appendFormat(INDENT4 "touch.position.xOrigin: %d\n", mCalibration.xOrigin);
-    }
-    if (mCalibration.haveYOrigin) {
-        dump.appendFormat(INDENT4 "touch.position.yOrigin: %d\n", mCalibration.yOrigin);
-    }
-    if (mCalibration.haveXScale) {
-        dump.appendFormat(INDENT4 "touch.position.xScale: %0.3f\n", mCalibration.xScale);
-    }
-    if (mCalibration.haveYScale) {
-        dump.appendFormat(INDENT4 "touch.position.yScale: %0.3f\n", mCalibration.yScale);
-    }
-
     // Touch Size
     switch (mCalibration.touchSizeCalibration) {
     case Calibration::TOUCH_SIZE_CALIBRATION_NONE:
@@ -2620,7 +2640,7 @@
         int32_t motionEventAction) {
     int32_t pointerIds[MAX_POINTERS];
     PointerCoords pointerCoords[MAX_POINTERS];
-    int32_t motionEventEdgeFlags = 0;
+    int32_t motionEventEdgeFlags = AMOTION_EVENT_EDGE_FLAG_NONE;
     float xPrecision, yPrecision;
 
     { // acquire lock
@@ -2635,10 +2655,6 @@
 
             const PointerData& in = touch->pointers[inIndex];
 
-            // X and Y
-            float x = float(in.x - mLocked.xOrigin) * mLocked.xScale;
-            float y = float(in.y - mLocked.yOrigin) * mLocked.yScale;
-
             // ToolMajor and ToolMinor
             float toolMajor, toolMinor;
             switch (mCalibration.toolSizeCalibration) {
@@ -2776,33 +2792,34 @@
                 orientation = 0;
             }
 
-            // Adjust coords for orientation.
+            // X and Y
+            // Adjust coords for surface orientation.
+            float x, y;
             switch (mLocked.surfaceOrientation) {
-            case DISPLAY_ORIENTATION_90: {
-                float xTemp = x;
-                x = y;
-                y = mLocked.surfaceWidth - xTemp;
+            case DISPLAY_ORIENTATION_90:
+                x = float(in.y - mRawAxes.y.minValue) * mLocked.yScale;
+                y = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale;
                 orientation -= M_PI_2;
                 if (orientation < - M_PI_2) {
                     orientation += M_PI;
                 }
                 break;
-            }
-            case DISPLAY_ORIENTATION_180: {
-                x = mLocked.surfaceWidth - x;
-                y = mLocked.surfaceHeight - y;
+            case DISPLAY_ORIENTATION_180:
+                x = float(mRawAxes.x.maxValue - in.x) * mLocked.xScale;
+                y = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale;
                 break;
-            }
-            case DISPLAY_ORIENTATION_270: {
-                float xTemp = x;
-                x = mLocked.surfaceHeight - y;
-                y = xTemp;
+            case DISPLAY_ORIENTATION_270:
+                x = float(mRawAxes.y.maxValue - in.y) * mLocked.yScale;
+                y = float(in.x - mRawAxes.x.minValue) * mLocked.xScale;
                 orientation += M_PI_2;
                 if (orientation > M_PI_2) {
                     orientation -= M_PI;
                 }
                 break;
-            }
+            default:
+                x = float(in.x - mRawAxes.x.minValue) * mLocked.xScale;
+                y = float(in.y - mRawAxes.y.minValue) * mLocked.yScale;
+                break;
             }
 
             // Write output coords.
@@ -2828,18 +2845,22 @@
         // Check edge flags by looking only at the first pointer since the flags are
         // global to the event.
         if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) {
-            float x = pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X);
-            float y = pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y);
+            uint32_t inIndex = touch->idToIndex[pointerIds[0]];
+            const PointerData& in = touch->pointers[inIndex];
 
-            if (x <= 0) {
-                motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT;
-            } else if (x >= mLocked.orientedSurfaceWidth) {
-                motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT;
+            if (in.x <= mRawAxes.x.minValue) {
+                motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_LEFT,
+                        mLocked.surfaceOrientation);
+            } else if (in.x >= mRawAxes.x.maxValue) {
+                motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_RIGHT,
+                        mLocked.surfaceOrientation);
             }
-            if (y <= 0) {
-                motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP;
-            } else if (y >= mLocked.orientedSurfaceHeight) {
-                motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM;
+            if (in.y <= mRawAxes.y.minValue) {
+                motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_TOP,
+                        mLocked.surfaceOrientation);
+            } else if (in.y >= mRawAxes.y.maxValue) {
+                motionEventEdgeFlags |= rotateEdgeFlag(AMOTION_EVENT_EDGE_FLAG_BOTTOM,
+                        mLocked.surfaceOrientation);
             }
         }
 
@@ -2854,11 +2875,8 @@
 }
 
 bool TouchInputMapper::isPointInsideSurfaceLocked(int32_t x, int32_t y) {
-    if (mRawAxes.x.valid && mRawAxes.y.valid) {
-        return x >= mRawAxes.x.minValue && x <= mRawAxes.x.maxValue
-                && y >= mRawAxes.y.minValue && y <= mRawAxes.y.maxValue;
-    }
-    return true;
+    return x >= mRawAxes.x.minValue && x <= mRawAxes.x.maxValue
+            && y >= mRawAxes.y.minValue && y <= mRawAxes.y.maxValue;
 }
 
 const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHitLocked(
@@ -3066,11 +3084,6 @@
  * points has moved more than a screen height from the last position,
  * then drop it. */
 bool TouchInputMapper::applyBadTouchFilter() {
-    // This hack requires valid axis parameters.
-    if (! mRawAxes.y.valid) {
-        return false;
-    }
-
     uint32_t pointerCount = mCurrentTouch.pointerCount;
 
     // Nothing to do if there are no points.
@@ -3089,7 +3102,7 @@
     // the long size of the screen to be bad.  This was a magic value
     // determined by looking at the maximum distance it is feasible
     // to actually move in one sample.
-    int32_t maxDeltaY = mRawAxes.y.getRange() * 7 / 16;
+    int32_t maxDeltaY = (mRawAxes.y.maxValue - mRawAxes.y.minValue + 1) * 7 / 16;
 
     // XXX The original code in InputDevice.java included commented out
     //     code for testing the X axis.  Note that when we drop a point
@@ -3150,11 +3163,6 @@
  * the coordinate value for one axis has jumped to the other pointer's location.
  */
 bool TouchInputMapper::applyJumpyTouchFilter() {
-    // This hack requires valid axis parameters.
-    if (! mRawAxes.y.valid) {
-        return false;
-    }
-
     uint32_t pointerCount = mCurrentTouch.pointerCount;
     if (mLastTouch.pointerCount != pointerCount) {
 #if DEBUG_HACKS
@@ -3211,7 +3219,7 @@
     }
 
     if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) {
-        int jumpyEpsilon = mRawAxes.y.getRange() / JUMPY_EPSILON_DIVISOR;
+        int jumpyEpsilon = (mRawAxes.y.maxValue - mRawAxes.y.minValue + 1) / JUMPY_EPSILON_DIVISOR;
 
         // We only replace the single worst jumpy point as characterized by pointer distance
         // in a single axis.
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index e2cf08e7..b9e3494 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -650,16 +650,6 @@
 
     // Immutable calibration parameters in parsed form.
     struct Calibration {
-        // Position
-        bool haveXOrigin;
-        int32_t xOrigin;
-        bool haveYOrigin;
-        int32_t yOrigin;
-        bool haveXScale;
-        float xScale;
-        bool haveYScale;
-        float yScale;
-
         // Touch Size
         enum TouchSizeCalibration {
             TOUCH_SIZE_CALIBRATION_DEFAULT,
@@ -756,11 +746,9 @@
         int32_t surfaceWidth, surfaceHeight;
 
         // Translation and scaling factors, orientation-independent.
-        int32_t xOrigin;
         float xScale;
         float xPrecision;
 
-        int32_t yOrigin;
         float yScale;
         float yPrecision;
 
diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp
index 954872b..a4ee295 100644
--- a/services/input/PointerController.cpp
+++ b/services/input/PointerController.cpp
@@ -109,12 +109,12 @@
     switch (mLocked.displayOrientation) {
     case DISPLAY_ORIENTATION_90:
     case DISPLAY_ORIENTATION_270:
-        *outMaxX = mLocked.displayHeight;
-        *outMaxY = mLocked.displayWidth;
+        *outMaxX = mLocked.displayHeight - 1;
+        *outMaxY = mLocked.displayWidth - 1;
         break;
     default:
-        *outMaxX = mLocked.displayWidth;
-        *outMaxY = mLocked.displayHeight;
+        *outMaxX = mLocked.displayWidth - 1;
+        *outMaxY = mLocked.displayHeight - 1;
         break;
     }
     return true;
@@ -309,48 +309,53 @@
     AutoMutex _l(mLock);
 
     if (mLocked.displayOrientation != orientation) {
-        float absoluteX, absoluteY;
+        // Apply offsets to convert from the pixel top-left corner position to the pixel center.
+        // This creates an invariant frame of reference that we can easily rotate when
+        // taking into account that the pointer may be located at fractional pixel offsets.
+        float x = mLocked.pointerX + 0.5f;
+        float y = mLocked.pointerY + 0.5f;
+        float temp;
 
-        // Map from oriented display coordinates to absolute display coordinates.
+        // Undo the previous rotation.
         switch (mLocked.displayOrientation) {
         case DISPLAY_ORIENTATION_90:
-            absoluteX = mLocked.displayWidth - mLocked.pointerY;
-            absoluteY = mLocked.pointerX;
+            temp = x;
+            x = mLocked.displayWidth - y;
+            y = temp;
             break;
         case DISPLAY_ORIENTATION_180:
-            absoluteX = mLocked.displayWidth - mLocked.pointerX;
-            absoluteY = mLocked.displayHeight - mLocked.pointerY;
+            x = mLocked.displayWidth - x;
+            y = mLocked.displayHeight - y;
             break;
         case DISPLAY_ORIENTATION_270:
-            absoluteX = mLocked.pointerY;
-            absoluteY = mLocked.displayHeight - mLocked.pointerX;
-            break;
-        default:
-            absoluteX = mLocked.pointerX;
-            absoluteY = mLocked.pointerY;
+            temp = x;
+            x = y;
+            y = mLocked.displayHeight - temp;
             break;
         }
 
-        // Map from absolute display coordinates to oriented display coordinates.
+        // Perform the new rotation.
         switch (orientation) {
         case DISPLAY_ORIENTATION_90:
-            mLocked.pointerX = absoluteY;
-            mLocked.pointerY = mLocked.displayWidth - absoluteX;
+            temp = x;
+            x = y;
+            y = mLocked.displayWidth - x;
             break;
         case DISPLAY_ORIENTATION_180:
-            mLocked.pointerX = mLocked.displayWidth - absoluteX;
-            mLocked.pointerY = mLocked.displayHeight - absoluteY;
+            x = mLocked.displayWidth - x;
+            y = mLocked.displayHeight - y;
             break;
         case DISPLAY_ORIENTATION_270:
-            mLocked.pointerX = mLocked.displayHeight - absoluteY;
-            mLocked.pointerY = absoluteX;
-            break;
-        default:
-            mLocked.pointerX = absoluteX;
-            mLocked.pointerY = absoluteY;
+            temp = x;
+            x = mLocked.displayHeight - y;
+            y = temp;
             break;
         }
 
+        // Apply offsets to convert from the pixel center to the pixel top-left corner position
+        // and save the results.
+        mLocked.pointerX = x - 0.5f;
+        mLocked.pointerY = y - 0.5f;
         mLocked.displayOrientation = orientation;
 
         updateLocked();
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index d77cb2f..f7e1890 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -2007,15 +2007,15 @@
             0.0f, 1.0f, 0.0f, 0.0f));
 
     // When the bounds are set, then there should be a valid motion range.
-    mFakePointerController->setBounds(1, 2, 800, 480);
+    mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
 
     InputDeviceInfo info2;
     mapper->populateDeviceInfo(&info2);
 
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X,
-            1, 800, 0.0f, 0.0f));
+            1, 800 - 1, 0.0f, 0.0f));
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y,
-            2, 480, 0.0f, 0.0f));
+            2, 480 - 1, 0.0f, 0.0f));
     ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE,
             0.0f, 1.0f, 0.0f, 0.0f));
 }
@@ -2320,9 +2320,9 @@
 };
 
 const int32_t TouchInputMapperTest::RAW_X_MIN = 25;
-const int32_t TouchInputMapperTest::RAW_X_MAX = 1020;
+const int32_t TouchInputMapperTest::RAW_X_MAX = 1019;
 const int32_t TouchInputMapperTest::RAW_Y_MIN = 30;
-const int32_t TouchInputMapperTest::RAW_Y_MAX = 1010;
+const int32_t TouchInputMapperTest::RAW_Y_MAX = 1009;
 const int32_t TouchInputMapperTest::RAW_TOUCH_MIN = 0;
 const int32_t TouchInputMapperTest::RAW_TOUCH_MAX = 31;
 const int32_t TouchInputMapperTest::RAW_TOOL_MIN = 0;
@@ -2333,8 +2333,8 @@
 const int32_t TouchInputMapperTest::RAW_ORIENTATION_MAX = 7;
 const int32_t TouchInputMapperTest::RAW_ID_MIN = 0;
 const int32_t TouchInputMapperTest::RAW_ID_MAX = 9;
-const float TouchInputMapperTest::X_PRECISION = float(RAW_X_MAX - RAW_X_MIN) / DISPLAY_WIDTH;
-const float TouchInputMapperTest::Y_PRECISION = float(RAW_Y_MAX - RAW_Y_MIN) / DISPLAY_HEIGHT;
+const float TouchInputMapperTest::X_PRECISION = float(RAW_X_MAX - RAW_X_MIN + 1) / DISPLAY_WIDTH;
+const float TouchInputMapperTest::Y_PRECISION = float(RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_HEIGHT;
 
 const VirtualKeyDefinition TouchInputMapperTest::VIRTUAL_KEYS[2] = {
         { KEY_HOME, 60, DISPLAY_HEIGHT + 15, 20, 20 },
@@ -2353,19 +2353,19 @@
 }
 
 int32_t TouchInputMapperTest::toRawX(float displayX) {
-    return int32_t(displayX * (RAW_X_MAX - RAW_X_MIN) / DISPLAY_WIDTH + RAW_X_MIN);
+    return int32_t(displayX * (RAW_X_MAX - RAW_X_MIN + 1) / DISPLAY_WIDTH + RAW_X_MIN);
 }
 
 int32_t TouchInputMapperTest::toRawY(float displayY) {
-    return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN) / DISPLAY_HEIGHT + RAW_Y_MIN);
+    return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN + 1) / DISPLAY_HEIGHT + RAW_Y_MIN);
 }
 
 float TouchInputMapperTest::toDisplayX(int32_t rawX) {
-    return float(rawX - RAW_X_MIN) * DISPLAY_WIDTH / (RAW_X_MAX - RAW_X_MIN);
+    return float(rawX - RAW_X_MIN) * DISPLAY_WIDTH / (RAW_X_MAX - RAW_X_MIN + 1);
 }
 
 float TouchInputMapperTest::toDisplayY(int32_t rawY) {
-    return float(rawY - RAW_Y_MIN) * DISPLAY_HEIGHT / (RAW_Y_MAX - RAW_Y_MIN);
+    return float(rawY - RAW_Y_MIN) * DISPLAY_HEIGHT / (RAW_Y_MAX - RAW_Y_MIN + 1);
 }
 
 
@@ -2950,12 +2950,12 @@
 
     // Rotation 90.
     prepareDisplay(DISPLAY_ORIENTATION_90);
-    processDown(mapper, toRawX(50), toRawY(75));
+    processDown(mapper, RAW_X_MAX - toRawX(75) + RAW_X_MIN, toRawY(50));
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
-    ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
-    ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+    ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
 
     processUp(mapper);
     processSync(mapper);
@@ -2963,12 +2963,12 @@
 
     // Rotation 180.
     prepareDisplay(DISPLAY_ORIENTATION_180);
-    processDown(mapper, toRawX(50), toRawY(75));
+    processDown(mapper, RAW_X_MAX - toRawX(50) + RAW_X_MIN, RAW_Y_MAX - toRawY(75) + RAW_Y_MIN);
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
-    ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
-    ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+    ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
 
     processUp(mapper);
     processSync(mapper);
@@ -2976,12 +2976,12 @@
 
     // Rotation 270.
     prepareDisplay(DISPLAY_ORIENTATION_270);
-    processDown(mapper, toRawX(50), toRawY(75));
+    processDown(mapper, toRawX(75), RAW_Y_MAX - toRawY(50) + RAW_Y_MIN);
     processSync(mapper);
 
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args));
-    ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
-    ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
+    ASSERT_NEAR(50, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), 1);
+    ASSERT_NEAR(75, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y), 1);
 
     processUp(mapper);
     processSync(mapper);
@@ -3601,8 +3601,8 @@
     float y = toDisplayY(rawY);
     float pressure = float(rawTouchMajor) / RAW_TOUCH_MAX;
     float size = avg(rawToolMajor, rawToolMinor) / RAW_TOOL_MAX;
-    float scale = avg(float(DISPLAY_WIDTH) / (RAW_X_MAX - RAW_X_MIN),
-            float(DISPLAY_HEIGHT) / (RAW_Y_MAX - RAW_Y_MIN));
+    float scale = avg(float(DISPLAY_WIDTH) / (RAW_X_MAX - RAW_X_MIN + 1),
+            float(DISPLAY_HEIGHT) / (RAW_Y_MAX - RAW_Y_MIN + 1));
     float toolMajor = float(rawToolMajor) * scale;
     float toolMinor = float(rawToolMinor) * scale;
     float touchMajor = min(float(rawTouchMajor) * scale, toolMajor);