diff options
author | Michael Wright <michaelwr@google.com> | 2014-03-10 23:39:52 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-03-10 23:39:52 +0000 |
commit | 35514312e5516acbc61f4b8b013173a9f82d8823 (patch) | |
tree | 7fde1cec20c651d5a317336e4c7b8808692b8037 | |
parent | 005c0324c52b7affd8a5fac1a4925c23fc22f3db (diff) | |
parent | 857aa7be880c53d78f72a9f227cbdf72ee68d587 (diff) | |
download | frameworks_base-35514312e5516acbc61f4b8b013173a9f82d8823.zip frameworks_base-35514312e5516acbc61f4b8b013173a9f82d8823.tar.gz frameworks_base-35514312e5516acbc61f4b8b013173a9f82d8823.tar.bz2 |
Merge changes I81c76e8c,I4daac2b1
* changes:
Initialize mAffineTransform and update on modification
Allow persistence of input device calibration
9 files changed, 381 insertions, 1 deletions
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index f1e7e98..4214115 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -19,6 +19,7 @@ package android.hardware.input; import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.KeyboardLayout; import android.hardware.input.IInputDevicesChangedListener; +import android.hardware.input.TouchCalibration; import android.os.IBinder; import android.view.InputDevice; import android.view.InputEvent; @@ -39,6 +40,11 @@ interface IInputManager { // applications, the caller must have the INJECT_EVENTS permission. boolean injectInputEvent(in InputEvent ev, int mode); + // Calibrate input device position + TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor); + void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, + in TouchCalibration calibration); + // Keyboard layouts configuration. KeyboardLayout[] getKeyboardLayouts(); KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor); diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index a2aeafb..ece5d82 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -500,6 +500,44 @@ public final class InputManager { } /** + * Gets the TouchCalibration applied to the specified input device's coordinates. + * + * @param inputDeviceDescriptor The input device descriptor. + * @return The TouchCalibration currently assigned for use with the given + * input device. If none is set, an identity TouchCalibration is returned. + * + * @hide + */ + public TouchCalibration getTouchCalibration(String inputDeviceDescriptor) { + try { + return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor); + } catch (RemoteException ex) { + Log.w(TAG, "Could not get calibration matrix for input device.", ex); + return TouchCalibration.IDENTITY; + } + } + + /** + * Sets the TouchCalibration to apply to the specified input device's coordinates. + * <p> + * This method may have the side-effect of causing the input device in question + * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}. + * </p> + * + * @param inputDeviceDescriptor The input device descriptor. + * @param calibration The calibration to be applied + * + * @hide + */ + public void setTouchCalibration(String inputDeviceDescriptor, TouchCalibration calibration) { + try { + mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, calibration); + } catch (RemoteException ex) { + Log.w(TAG, "Could not set calibration matrix for input device.", ex); + } + } + + /** * Gets the mouse pointer speed. * <p> * Only returns the permanent mouse pointer speed. Ignores any temporary pointer diff --git a/core/java/android/hardware/input/TouchCalibration.aidl b/core/java/android/hardware/input/TouchCalibration.aidl new file mode 100644 index 0000000..2c28774 --- /dev/null +++ b/core/java/android/hardware/input/TouchCalibration.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.input; + +parcelable TouchCalibration; diff --git a/core/java/android/hardware/input/TouchCalibration.java b/core/java/android/hardware/input/TouchCalibration.java new file mode 100644 index 0000000..025fad0 --- /dev/null +++ b/core/java/android/hardware/input/TouchCalibration.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.input; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Encapsulates calibration data for input devices. + * + * @hide + */ +public class TouchCalibration implements Parcelable { + + public static final TouchCalibration IDENTITY = new TouchCalibration(); + + public static final Parcelable.Creator<TouchCalibration> CREATOR + = new Parcelable.Creator<TouchCalibration>() { + public TouchCalibration createFromParcel(Parcel in) { + return new TouchCalibration(in); + } + + public TouchCalibration[] newArray(int size) { + return new TouchCalibration[size]; + } + }; + + private final float mXScale, mXYMix, mXOffset; + private final float mYXMix, mYScale, mYOffset; + + /** + * Create a new TouchCalibration initialized to the identity transformation. + */ + public TouchCalibration() { + this(1,0,0,0,1,0); + } + + /** + * Create a new TouchCalibration from affine transformation paramters. + * @param xScale Influence of input x-axis value on output x-axis value. + * @param xyMix Influence of input y-axis value on output x-axis value. + * @param xOffset Constant offset to be applied to output x-axis value. + * @param yXMix Influence of input x-axis value on output y-axis value. + * @param yScale Influence of input y-axis value on output y-axis value. + * @param yOffset Constant offset to be applied to output y-axis value. + */ + public TouchCalibration(float xScale, float xyMix, float xOffset, + float yxMix, float yScale, float yOffset) { + mXScale = xScale; + mXYMix = xyMix; + mXOffset = xOffset; + mYXMix = yxMix; + mYScale = yScale; + mYOffset = yOffset; + } + + public TouchCalibration(Parcel in) { + mXScale = in.readFloat(); + mXYMix = in.readFloat(); + mXOffset = in.readFloat(); + mYXMix = in.readFloat(); + mYScale = in.readFloat(); + mYOffset = in.readFloat(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeFloat(mXScale); + dest.writeFloat(mXYMix); + dest.writeFloat(mXOffset); + dest.writeFloat(mYXMix); + dest.writeFloat(mYScale); + dest.writeFloat(mYOffset); + } + + @Override + public int describeContents() { + return 0; + } + + public float[] getAffineTransform() { + return new float[] { mXScale, mXYMix, mXOffset, mYXMix, mYScale, mYOffset }; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof TouchCalibration) { + TouchCalibration cal = (TouchCalibration)obj; + + return (cal.mXScale == mXScale) && + (cal.mXYMix == mXYMix) && + (cal.mXOffset == mXOffset) && + (cal.mYXMix == mYXMix) && + (cal.mYScale == mYScale) && + (cal.mYOffset == mYOffset); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Float.floatToIntBits(mXScale) ^ + Float.floatToIntBits(mXYMix) ^ + Float.floatToIntBits(mXOffset) ^ + Float.floatToIntBits(mYXMix) ^ + Float.floatToIntBits(mYScale) ^ + Float.floatToIntBits(mYOffset); + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 4c0ddeb..b99cb90 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2069,6 +2069,14 @@ android:description="@string/permdesc_setPointerSpeed" android:protectionLevel="signature" /> + <!-- Allows low-level access to setting input device calibration. + <p>Not for use by normal applications. + @hide --> + <permission android:name="android.permission.SET_INPUT_CALIBRATION" + android:label="@string/permlab_setInputCalibration" + android:description="@string/permdesc_setInputCalibration" + android:protectionLevel="signature" /> + <!-- Allows low-level access to setting the keyboard layout. <p>Not for use by third-party applications. @hide --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index afb7085..f1bcf65 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2012,6 +2012,10 @@ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_accessNetworkConditions">Allows an application to listen for observations on network conditions. Should never be needed for normal apps.</string> + <string name="permlab_setInputCalibration">change input device calibration</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_setInputCalibration">Allows the app to modify the calibration parameters of the touch screen. Should never be needed for normal apps.</string> + <!-- Policy administration --> <!-- Title of policy access to limiting the user's password choices --> diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index e49382e..69d8442 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -52,6 +52,7 @@ import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.InputManager; import android.hardware.input.InputManagerInternal; import android.hardware.input.KeyboardLayout; +import android.hardware.input.TouchCalibration; import android.os.Binder; import android.os.Bundle; import android.os.Environment; @@ -183,6 +184,7 @@ public class InputManagerService extends IInputManager.Stub InputChannel fromChannel, InputChannel toChannel); private static native void nativeSetPointerSpeed(long ptr, int speed); private static native void nativeSetShowTouches(long ptr, boolean enabled); + private static native void nativeReloadCalibration(long ptr); private static native void nativeVibrate(long ptr, int deviceId, long[] pattern, int repeat, int token); private static native void nativeCancelVibrate(long ptr, int deviceId, int token); @@ -700,6 +702,42 @@ public class InputManagerService extends IInputManager.Stub mTempFullKeyboards.clear(); } + @Override // Binder call & native callback + public TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor) { + if (inputDeviceDescriptor == null) { + throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); + } + + synchronized (mDataStore) { + return mDataStore.getTouchCalibration(inputDeviceDescriptor); + } + } + + @Override // Binder call + public void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, + TouchCalibration calibration) { + if (!checkCallingPermission(android.Manifest.permission.SET_INPUT_CALIBRATION, + "setTouchCalibrationForInputDevice()")) { + throw new SecurityException("Requires SET_INPUT_CALIBRATION permission"); + } + if (inputDeviceDescriptor == null) { + throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); + } + if (calibration == null) { + throw new IllegalArgumentException("calibration must not be null"); + } + + synchronized (mDataStore) { + try { + if (mDataStore.setTouchCalibration(inputDeviceDescriptor, calibration)) { + nativeReloadCalibration(mPtr); + } + } finally { + mDataStore.saveIfNeeded(); + } + } + } + // Must be called on handler. private void showMissingKeyboardLayoutNotification() { if (!mKeyboardLayoutNotificationShown) { diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java index 71de776..9ea369d 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.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import android.hardware.input.TouchCalibration; import android.util.AtomicFile; import android.util.Slog; import android.util.Xml; @@ -82,6 +83,25 @@ final class PersistentDataStore { } } + public TouchCalibration getTouchCalibration(String inputDeviceDescriptor) { + InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false); + if (state == null) { + return TouchCalibration.IDENTITY; + } + else { + return state.getTouchCalibration(); + } + } + + public boolean setTouchCalibration(String inputDeviceDescriptor, TouchCalibration calibration) { + InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true); + if (state.setTouchCalibration(calibration)) { + setDirty(); + return true; + } + return false; + } + public String getCurrentKeyboardLayout(String inputDeviceDescriptor) { InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false); return state != null ? state.getCurrentKeyboardLayout() : null; @@ -275,9 +295,25 @@ final class PersistentDataStore { } private static final class InputDeviceState { + private static final String[] CALIBRATION_NAME = { "x_scale", + "x_ymix", "x_offset", "y_xmix", "y_scale", "y_offset" }; + + private TouchCalibration mTouchCalibration = TouchCalibration.IDENTITY; private String mCurrentKeyboardLayout; private ArrayList<String> mKeyboardLayouts = new ArrayList<String>(); + public TouchCalibration getTouchCalibration() { + return mTouchCalibration; + } + + public boolean setTouchCalibration(TouchCalibration calibration) { + if (calibration.equals(mTouchCalibration)) { + return false; + } + mTouchCalibration = calibration; + return true; + } + public String getCurrentKeyboardLayout() { return mCurrentKeyboardLayout; } @@ -389,6 +425,31 @@ final class PersistentDataStore { } mCurrentKeyboardLayout = descriptor; } + } else if (parser.getName().equals("calibration")) { + String format = parser.getAttributeValue(null, "format"); + 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(); + + 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]); + } else { + throw new XmlPullParserException("Unsupported format for calibration."); + } } } @@ -411,6 +472,16 @@ final class PersistentDataStore { } 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]); + } + serializer.endTag(null, "calibration"); } } -}
\ No newline at end of file +} diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 0207c55..35c6583 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -49,6 +49,7 @@ #include <android/graphics/GraphicsJNI.h> #include <ScopedLocalRef.h> +#include <ScopedPrimitiveArray.h> #include <ScopedUtfChars.h> #include "com_android_server_power_PowerManagerService.h" @@ -86,6 +87,7 @@ static struct { jmethodID getPointerIcon; jmethodID getKeyboardLayoutOverlay; jmethodID getDeviceAlias; + jmethodID getTouchCalibrationForInputDevice; } gServiceClassInfo; static struct { @@ -105,6 +107,11 @@ static struct { jmethodID constructor; } gInputDeviceIdentifierInfo; +static struct { + jclass clazz; + jmethodID getAffineTransform; +} gTouchCalibrationClassInfo; + // --- Global functions --- @@ -182,6 +189,7 @@ public: void setSystemUiVisibility(int32_t visibility); void setPointerSpeed(int32_t speed); void setShowTouches(bool enabled); + void reloadCalibration(); /* --- InputReaderPolicyInterface implementation --- */ @@ -190,6 +198,8 @@ public: 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); /* --- InputDispatcherPolicyInterface implementation --- */ @@ -741,6 +751,11 @@ void NativeInputManager::setShowTouches(bool enabled) { InputReaderConfiguration::CHANGE_SHOW_TOUCHES); } +void NativeInputManager::reloadCalibration() { + mInputManager->getReader()->requestRefreshConfiguration( + InputReaderConfiguration::TOUCH_AFFINE_TRANSFORMATION); +} + bool NativeInputManager::isScreenOn() { return android_server_PowerManagerService_isScreenOn(); } @@ -749,6 +764,42 @@ bool NativeInputManager::isScreenBright() { return android_server_PowerManagerService_isScreenBright(); } +TouchAffineTransformation NativeInputManager::getTouchAffineTransformation( + JNIEnv *env, jfloatArray matrixArr) { + ScopedFloatArrayRO matrix(env, matrixArr); + assert(matrix.size() == 6); + + TouchAffineTransformation transform; + transform.x_scale = matrix[0]; + transform.x_ymix = matrix[1]; + transform.x_offset = matrix[2]; + transform.y_xmix = matrix[3]; + transform.y_scale = matrix[4]; + transform.y_offset = matrix[5]; + + return transform; +} + +TouchAffineTransformation NativeInputManager::getTouchAffineTransformation( + const String8& inputDeviceDescriptor) { + JNIEnv* env = jniEnv(); + + ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.string())); + + jobject cal = env->CallObjectMethod(mServiceObj, + gServiceClassInfo.getTouchCalibrationForInputDevice, descriptorObj.get()); + + jfloatArray matrixArr = jfloatArray(env->CallObjectMethod(cal, + gTouchCalibrationClassInfo.getAffineTransform)); + + TouchAffineTransformation transform = getTouchAffineTransformation(env, matrixArr); + + env->DeleteLocalRef(matrixArr); + env->DeleteLocalRef(cal); + + return transform; +} + bool NativeInputManager::filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) { jobject inputEventObj; @@ -1231,6 +1282,11 @@ static void nativeSetShowTouches(JNIEnv* env, im->setShowTouches(enabled); } +static void nativeReloadCalibration(JNIEnv* env, jclass clazz, jlong ptr) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + im->reloadCalibration(); +} + static void nativeVibrate(JNIEnv* env, jclass clazz, jlong ptr, jint deviceId, jlongArray patternObj, jint repeat, jint token) { @@ -1336,6 +1392,8 @@ static JNINativeMethod gInputManagerMethods[] = { (void*) nativeSetPointerSpeed }, { "nativeSetShowTouches", "(JZ)V", (void*) nativeSetShowTouches }, + { "nativeReloadCalibration", "(J)V", + (void*) nativeReloadCalibration }, { "nativeVibrate", "(JI[JII)V", (void*) nativeVibrate }, { "nativeCancelVibrate", "(JII)V", @@ -1446,6 +1504,10 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gServiceClassInfo.getDeviceAlias, clazz, "getDeviceAlias", "(Ljava/lang/String;)Ljava/lang/String;"); + GET_METHOD_ID(gServiceClassInfo.getTouchCalibrationForInputDevice, clazz, + "getTouchCalibrationForInputDevice", + "(Ljava/lang/String;)Landroid/hardware/input/TouchCalibration;"); + // InputDevice FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice"); @@ -1468,6 +1530,14 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gInputDeviceIdentifierInfo.constructor, gInputDeviceIdentifierInfo.clazz, "<init>", "(Ljava/lang/String;II)V"); + // TouchCalibration + + FIND_CLASS(gTouchCalibrationClassInfo.clazz, "android/hardware/input/TouchCalibration"); + gTouchCalibrationClassInfo.clazz = jclass(env->NewGlobalRef(gTouchCalibrationClassInfo.clazz)); + + GET_METHOD_ID(gTouchCalibrationClassInfo.getAffineTransform, gTouchCalibrationClassInfo.clazz, + "getAffineTransform", "()[F"); + return 0; } |