Merge "Support unique calibration per orientation"
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 4214115..465d142 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -41,8 +41,8 @@
     boolean injectInputEvent(in InputEvent ev, int mode);
 
     // Calibrate input device position
-    TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor);
-    void setTouchCalibrationForInputDevice(String inputDeviceDescriptor,
+    TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor, int rotation);
+    void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int rotation,
             in TouchCalibration calibration);
 
     // Keyboard layouts configuration.
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index ece5d82..e3a3830 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -508,9 +508,9 @@
      *
      * @hide
      */
-    public TouchCalibration getTouchCalibration(String inputDeviceDescriptor) {
+    public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
         try {
-            return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor);
+            return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation);
         } catch (RemoteException ex) {
             Log.w(TAG, "Could not get calibration matrix for input device.", ex);
             return TouchCalibration.IDENTITY;
@@ -529,9 +529,10 @@
      *
      * @hide
      */
-    public void setTouchCalibration(String inputDeviceDescriptor, TouchCalibration calibration) {
+    public void setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation,
+            TouchCalibration calibration) {
         try {
-            mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, calibration);
+            mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation, calibration);
         } catch (RemoteException ex) {
             Log.w(TAG, "Could not set calibration matrix for input device.", ex);
         }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 69d8442..316bd57 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -77,6 +77,7 @@
 import android.view.InputEvent;
 import android.view.KeyEvent;
 import android.view.PointerIcon;
+import android.view.Surface;
 import android.view.ViewConfiguration;
 import android.view.WindowManagerPolicy;
 import android.widget.Toast;
@@ -703,18 +704,19 @@
     }
 
     @Override // Binder call & native callback
-    public TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor) {
+    public TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor,
+            int surfaceRotation) {
         if (inputDeviceDescriptor == null) {
             throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
         }
 
         synchronized (mDataStore) {
-            return mDataStore.getTouchCalibration(inputDeviceDescriptor);
+            return mDataStore.getTouchCalibration(inputDeviceDescriptor, surfaceRotation);
         }
     }
 
     @Override // Binder call
-    public void setTouchCalibrationForInputDevice(String inputDeviceDescriptor,
+    public void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int surfaceRotation,
             TouchCalibration calibration) {
         if (!checkCallingPermission(android.Manifest.permission.SET_INPUT_CALIBRATION,
                 "setTouchCalibrationForInputDevice()")) {
@@ -726,10 +728,14 @@
         if (calibration == null) {
             throw new IllegalArgumentException("calibration must not be null");
         }
+        if (surfaceRotation < Surface.ROTATION_0 || surfaceRotation > Surface.ROTATION_270) {
+            throw new IllegalArgumentException("surfaceRotation value out of bounds");
+        }
 
         synchronized (mDataStore) {
             try {
-                if (mDataStore.setTouchCalibration(inputDeviceDescriptor, calibration)) {
+                if (mDataStore.setTouchCalibration(inputDeviceDescriptor, surfaceRotation,
+                        calibration)) {
                     nativeReloadCalibration(mPtr);
                 }
             } finally {
diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java
index 9ea369d..92fa813 100644
--- a/services/core/java/com/android/server/input/PersistentDataStore.java
+++ b/services/core/java/com/android/server/input/PersistentDataStore.java
@@ -24,6 +24,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import android.view.Surface;
 import android.hardware.input.TouchCalibration;
 import android.util.AtomicFile;
 import android.util.Slog;
@@ -83,22 +84,27 @@
         }
     }
 
-    public TouchCalibration getTouchCalibration(String inputDeviceDescriptor) {
+    public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
         InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
         if (state == null) {
             return TouchCalibration.IDENTITY;
         }
-        else {
-            return state.getTouchCalibration();
+
+        TouchCalibration cal = state.getTouchCalibration(surfaceRotation);
+        if (cal == null) {
+            return TouchCalibration.IDENTITY;
         }
+        return cal;
     }
 
-    public boolean setTouchCalibration(String inputDeviceDescriptor, TouchCalibration calibration) {
+    public boolean setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration) {
         InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
-        if (state.setTouchCalibration(calibration)) {
+
+        if (state.setTouchCalibration(surfaceRotation, calibration)) {
             setDirty();
             return true;
         }
+
         return false;
     }
 
@@ -298,20 +304,30 @@
         private static final String[] CALIBRATION_NAME = { "x_scale",
                 "x_ymix", "x_offset", "y_xmix", "y_scale", "y_offset" };
 
-        private TouchCalibration mTouchCalibration = TouchCalibration.IDENTITY;
+        private TouchCalibration[] mTouchCalibration = new TouchCalibration[4];
         private String mCurrentKeyboardLayout;
         private ArrayList<String> mKeyboardLayouts = new ArrayList<String>();
 
-        public TouchCalibration getTouchCalibration() {
-            return mTouchCalibration;
+        public TouchCalibration getTouchCalibration(int surfaceRotation) {
+            try {
+                return mTouchCalibration[surfaceRotation];
+            } catch (ArrayIndexOutOfBoundsException ex) {
+                Slog.w(InputManagerService.TAG, "Cannot get touch calibration.", ex);
+                return null;
+            }
         }
 
-        public boolean setTouchCalibration(TouchCalibration calibration) {
-            if (calibration.equals(mTouchCalibration)) {
+        public boolean setTouchCalibration(int surfaceRotation, TouchCalibration calibration) {
+            try {
+                if (!calibration.equals(mTouchCalibration[surfaceRotation])) {
+                    mTouchCalibration[surfaceRotation] = calibration;
+                    return true;
+                }
+                return false;
+            } catch (ArrayIndexOutOfBoundsException ex) {
+                Slog.w(InputManagerService.TAG, "Cannot set touch calibration.", ex);
                 return false;
             }
-            mTouchCalibration = calibration;
-            return true;
         }
 
         public String getCurrentKeyboardLayout() {
@@ -427,28 +443,49 @@
                     }
                 } else if (parser.getName().equals("calibration")) {
                     String format = parser.getAttributeValue(null, "format");
+                    String rotation = parser.getAttributeValue(null, "rotation");
+                    int r = -1;
+
                     if (format == null) {
                         throw new XmlPullParserException(
                                 "Missing format attribute on calibration.");
                     }
-                    if (format.equals("affine")) {
-                        float[] matrix = TouchCalibration.IDENTITY.getAffineTransform();
-                        int depth = parser.getDepth();
-                        while (XmlUtils.nextElementWithin(parser, depth)) {
-                            String tag = parser.getName().toLowerCase();
-                            String value = parser.nextText();
+                    if (!format.equals("affine")) {
+                        throw new XmlPullParserException(
+                                "Unsupported format for calibration.");
+                    }
+                    if (rotation != null) {
+                        try {
+                            r = stringToSurfaceRotation(rotation);
+                        } catch (IllegalArgumentException e) {
+                            throw new XmlPullParserException(
+                                    "Unsupported rotation for calibration.");
+                        }
+                    }
 
-                            for (int i = 0; i < matrix.length && i < CALIBRATION_NAME.length; i++) {
-                                if (tag.equals(CALIBRATION_NAME[i])) {
-                                    matrix[i] = Float.parseFloat(value);
-                                    break;
-                                }
+                    float[] matrix = TouchCalibration.IDENTITY.getAffineTransform();
+                    int depth = parser.getDepth();
+                    while (XmlUtils.nextElementWithin(parser, depth)) {
+                        String tag = parser.getName().toLowerCase();
+                        String value = parser.nextText();
+
+                        for (int i = 0; i < matrix.length && i < CALIBRATION_NAME.length; i++) {
+                            if (tag.equals(CALIBRATION_NAME[i])) {
+                                matrix[i] = Float.parseFloat(value);
+                                break;
                             }
                         }
-                        mTouchCalibration = new TouchCalibration(matrix[0], matrix[1], matrix[2],
-                                matrix[3], matrix[4], matrix[5]);
+                    }
+
+                    if (r == -1) {
+                        // Assume calibration applies to all rotations
+                        for (r = 0; r < mTouchCalibration.length; r++) {
+                            mTouchCalibration[r] = new TouchCalibration(matrix[0],
+                                matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
+                        }
                     } else {
-                        throw new XmlPullParserException("Unsupported format for calibration.");
+                        mTouchCalibration[r] = new TouchCalibration(matrix[0],
+                            matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
                     }
                 }
             }
@@ -473,15 +510,48 @@
                 serializer.endTag(null, "keyboard-layout");
             }
 
-            serializer.startTag(null, "calibration");
-            serializer.attribute(null, "format", "affine");
-            float[] transform = mTouchCalibration.getAffineTransform();
-            for (int i = 0; i < transform.length && i < CALIBRATION_NAME.length; i++) {
-                serializer.startTag(null, CALIBRATION_NAME[i]);
-                serializer.text(Float.toString(transform[i]));
-                serializer.endTag(null, CALIBRATION_NAME[i]);
+            for (int i = 0; i < mTouchCalibration.length; i++) {
+                if (mTouchCalibration[i] != null) {
+                    String rotation = surfaceRotationToString(i);
+                    float[] transform = mTouchCalibration[i].getAffineTransform();
+
+                    serializer.startTag(null, "calibration");
+                    serializer.attribute(null, "format", "affine");
+                    serializer.attribute(null, "rotation", rotation);
+                    for (int j = 0; j < transform.length && j < CALIBRATION_NAME.length; j++) {
+                        serializer.startTag(null, CALIBRATION_NAME[j]);
+                        serializer.text(Float.toString(transform[j]));
+                        serializer.endTag(null, CALIBRATION_NAME[j]);
+                    }
+                    serializer.endTag(null, "calibration");
+                }
             }
-            serializer.endTag(null, "calibration");
+        }
+
+        private static String surfaceRotationToString(int surfaceRotation) {
+            switch (surfaceRotation) {
+                case Surface.ROTATION_0:   return "0";
+                case Surface.ROTATION_90:  return "90";
+                case Surface.ROTATION_180: return "180";
+                case Surface.ROTATION_270: return "270";
+            }
+            throw new IllegalArgumentException("Unsupported surface rotation value" + surfaceRotation);
+        }
+
+        private static int stringToSurfaceRotation(String s) {
+            if ("0".equals(s)) {
+                return Surface.ROTATION_0;
+            }
+            if ("90".equals(s)) {
+                return Surface.ROTATION_90;
+            }
+            if ("180".equals(s)) {
+                return Surface.ROTATION_180;
+            }
+            if ("270".equals(s)) {
+                return Surface.ROTATION_270;
+            }
+            throw new IllegalArgumentException("Unsupported surface rotation string '" + s + "'");
         }
     }
 }
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 35c6583..e1069ae 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -198,8 +198,10 @@
     virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices);
     virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier);
     virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier);
-    TouchAffineTransformation getTouchAffineTransformation(JNIEnv *env, jfloatArray matrixArr);
-    TouchAffineTransformation getTouchAffineTransformation(const String8& inputDeviceDescriptor);
+    virtual TouchAffineTransformation getTouchAffineTransformation(JNIEnv *env,
+            jfloatArray matrixArr);
+    virtual TouchAffineTransformation getTouchAffineTransformation(
+            const String8& inputDeviceDescriptor, int32_t surfaceRotation);
 
     /* --- InputDispatcherPolicyInterface implementation --- */
 
@@ -781,13 +783,14 @@
 }
 
 TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
-        const String8& inputDeviceDescriptor) {
+        const String8& inputDeviceDescriptor, int32_t surfaceRotation) {
     JNIEnv* env = jniEnv();
 
     ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.string()));
 
     jobject cal = env->CallObjectMethod(mServiceObj,
-            gServiceClassInfo.getTouchCalibrationForInputDevice, descriptorObj.get());
+            gServiceClassInfo.getTouchCalibrationForInputDevice, descriptorObj.get(),
+            surfaceRotation);
 
     jfloatArray matrixArr = jfloatArray(env->CallObjectMethod(cal,
             gTouchCalibrationClassInfo.getAffineTransform));
@@ -1506,7 +1509,7 @@
 
     GET_METHOD_ID(gServiceClassInfo.getTouchCalibrationForInputDevice, clazz,
             "getTouchCalibrationForInputDevice",
-            "(Ljava/lang/String;)Landroid/hardware/input/TouchCalibration;");
+            "(Ljava/lang/String;I)Landroid/hardware/input/TouchCalibration;");
 
     // InputDevice