From 8d60866e2100db70ecf0502c14768a384514d7e9 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Mon, 30 Aug 2010 03:02:23 -0700 Subject: Input device calibration and capabilities. Finished the input device capability API. Added a mechanism for calibrating touch devices to obtain more accurate information about the touch contact area. Improved pointer location to show new coordinates and capabilities. Optimized pointer location display and formatting to avoid allocating large numbers of temporary objects. The GC churn was causing the application to stutter very badly when more than a couple of fingers were down). Added more diagnostics. Change-Id: Ie25380278ed6f16c5b04cd9df848015850383498 --- services/java/com/android/server/InputManager.java | 64 +++++++- .../com/android/server/WindowManagerService.java | 8 + services/jni/com_android_server_InputManager.cpp | 179 ++++++++++++++++++++- 3 files changed, 242 insertions(+), 9 deletions(-) (limited to 'services') diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java index f330d40..314dd8a 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/InputManager.java @@ -30,6 +30,7 @@ import android.os.SystemProperties; import android.util.Slog; import android.util.Xml; import android.view.InputChannel; +import android.view.InputDevice; import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; @@ -45,6 +46,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Properties; /* * Wraps the C++ InputManager and provides its callbacks. @@ -82,6 +84,8 @@ public class InputManager { private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen); private static native void nativeSetFocusedApplication(InputApplication application); private static native void nativePreemptInputDispatch(); + private static native InputDevice nativeGetInputDevice(int deviceId); + private static native int[] nativeGetInputDeviceIds(); private static native String nativeDump(); // Input event injection constants defined in InputDispatcher.h. @@ -302,6 +306,23 @@ public class InputManager { return nativeInjectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis); } + /** + * Gets information about the input device with the specified id. + * @param id The device id. + * @return The input device or null if not found. + */ + public InputDevice getInputDevice(int deviceId) { + return nativeGetInputDevice(deviceId); + } + + /** + * Gets the ids of all input devices in the system. + * @return The input device ids. + */ + public int[] getInputDeviceIds() { + return nativeGetInputDeviceIds(); + } + public void setInputWindows(InputWindow[] windows) { nativeSetInputWindows(windows); } @@ -335,6 +356,11 @@ public class InputManager { public int height; } + private static final class InputDeviceCalibration { + public String[] keys; + public String[] values; + } + /* * Callbacks from native. */ @@ -343,6 +369,7 @@ public class InputManager { private static final boolean DEBUG_VIRTUAL_KEYS = false; private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; + private static final String CALIBRATION_DIR_PATH = "usr/idc/"; @SuppressWarnings("unused") public void virtualKeyDownFeedback() { @@ -438,8 +465,8 @@ public class InputManager { final int N = it.length-6; for (int i=0; i<=N; i+=6) { if (!"0x01".equals(it[i])) { - Slog.w(TAG, "Unknown virtual key type at elem #" + i - + ": " + it[i]); + Slog.w(TAG, "Unknown virtual key type at elem #" + + i + ": " + it[i] + " for device " + deviceName); continue; } try { @@ -455,22 +482,47 @@ public class InputManager { + key.height); keys.add(key); } catch (NumberFormatException e) { - Slog.w(TAG, "Bad number at region " + i + " in: " - + str, e); + Slog.w(TAG, "Bad number in virtual key definition at region " + + i + " in: " + str + " for device " + deviceName, e); } } } br.close(); } catch (FileNotFoundException e) { - Slog.i(TAG, "No virtual keys found"); + Slog.i(TAG, "No virtual keys found for device " + deviceName + "."); } catch (IOException e) { - Slog.w(TAG, "Error reading virtual keys", e); + Slog.w(TAG, "Error reading virtual keys for device " + deviceName + ".", e); } return keys.toArray(new VirtualKeyDefinition[keys.size()]); } @SuppressWarnings("unused") + public InputDeviceCalibration getInputDeviceCalibration(String deviceName) { + // Calibration is specified as a sequence of colon-delimited key value pairs. + Properties properties = new Properties(); + File calibrationFile = new File(Environment.getRootDirectory(), + CALIBRATION_DIR_PATH + deviceName + ".idc"); + if (calibrationFile.exists()) { + try { + properties.load(new FileInputStream(calibrationFile)); + } catch (IOException ex) { + Slog.w(TAG, "Error reading input device calibration properties for device " + + deviceName + " from " + calibrationFile + ".", ex); + } + } else { + Slog.i(TAG, "No input device calibration properties found for device " + + deviceName + "."); + return null; + } + + InputDeviceCalibration calibration = new InputDeviceCalibration(); + calibration.keys = properties.keySet().toArray(new String[properties.size()]); + calibration.values = properties.values().toArray(new String[properties.size()]); + return calibration; + } + + @SuppressWarnings("unused") public String[] getExcludedDeviceNames() { ArrayList names = new ArrayList(); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 9b9d950..3841f75 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -4392,6 +4392,14 @@ public class WindowManagerService extends IWindowManager.Stub return mInputManager.monitorInput(inputChannelName); } + public InputDevice getInputDevice(int deviceId) { + return mInputManager.getInputDevice(deviceId); + } + + public int[] getInputDeviceIds() { + return mInputManager.getInputDeviceIds(); + } + public void enableScreenAfterBoot() { synchronized(mWindowMap) { if (mSystemBooted) { diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 3addc0d..a237ee9 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -138,6 +138,7 @@ static struct { jmethodID filterTouchEvents; jmethodID filterJumpyTouchEvents; jmethodID getVirtualKeyDefinitions; + jmethodID getInputDeviceCalibration; jmethodID getExcludedDeviceNames; jmethodID getMaxEventsPerSecond; } gCallbacksClassInfo; @@ -155,6 +156,13 @@ static struct { static struct { jclass clazz; + jfieldID keys; + jfieldID values; +} gInputDeviceCalibrationClassInfo; + +static struct { + jclass clazz; + jfieldID inputChannel; jfieldID layoutParamsFlags; jfieldID layoutParamsType; @@ -189,6 +197,19 @@ static struct { jclass clazz; } gMotionEventClassInfo; +static struct { + jclass clazz; + + jmethodID ctor; + jmethodID addMotionRange; + + jfieldID mId; + jfieldID mName; + jfieldID mSources; + jfieldID mKeyboardType; + jfieldID mMotionRanges; +} gInputDeviceClassInfo; + // ---------------------------------------------------------------------------- static inline nsecs_t now() { @@ -235,7 +256,9 @@ public: virtual bool filterTouchEvents(); virtual bool filterJumpyTouchEvents(); virtual void getVirtualKeyDefinitions(const String8& deviceName, - Vector& outVirtualKeyDefinitions); + Vector& outVirtualKeyDefinitions); + virtual void getInputDeviceCalibration(const String8& deviceName, + InputDeviceCalibration& outCalibration); virtual void getExcludedDeviceNames(Vector& outExcludedDeviceNames); /* --- InputDispatcherPolicyInterface implementation --- */ @@ -761,7 +784,9 @@ bool NativeInputManager::filterJumpyTouchEvents() { } void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName, - Vector& outVirtualKeyDefinitions) { + Vector& outVirtualKeyDefinitions) { + outVirtualKeyDefinitions.clear(); + JNIEnv* env = jniEnv(); jstring deviceNameStr = env->NewStringUTF(deviceName.string()); @@ -793,7 +818,51 @@ void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName, } } +void NativeInputManager::getInputDeviceCalibration(const String8& deviceName, + InputDeviceCalibration& outCalibration) { + outCalibration.clear(); + + JNIEnv* env = jniEnv(); + + jstring deviceNameStr = env->NewStringUTF(deviceName.string()); + if (! checkAndClearExceptionFromCallback(env, "getInputDeviceCalibration")) { + jobject result = env->CallObjectMethod(mCallbacksObj, + gCallbacksClassInfo.getInputDeviceCalibration, deviceNameStr); + if (! checkAndClearExceptionFromCallback(env, "getInputDeviceCalibration") && result) { + jobjectArray keys = jobjectArray(env->GetObjectField(result, + gInputDeviceCalibrationClassInfo.keys)); + jobjectArray values = jobjectArray(env->GetObjectField(result, + gInputDeviceCalibrationClassInfo.values)); + + jsize length = env->GetArrayLength(keys); + for (jsize i = 0; i < length; i++) { + jstring keyStr = jstring(env->GetObjectArrayElement(keys, i)); + jstring valueStr = jstring(env->GetObjectArrayElement(values, i)); + + const char* keyChars = env->GetStringUTFChars(keyStr, NULL); + String8 key(keyChars); + env->ReleaseStringUTFChars(keyStr, keyChars); + + const char* valueChars = env->GetStringUTFChars(valueStr, NULL); + String8 value(valueChars); + env->ReleaseStringUTFChars(valueStr, valueChars); + + outCalibration.addProperty(key, value); + + env->DeleteLocalRef(keyStr); + env->DeleteLocalRef(valueStr); + } + env->DeleteLocalRef(keys); + env->DeleteLocalRef(values); + env->DeleteLocalRef(result); + } + env->DeleteLocalRef(deviceNameStr); + } +} + void NativeInputManager::getExcludedDeviceNames(Vector& outExcludedDeviceNames) { + outExcludedDeviceNames.clear(); + JNIEnv* env = jniEnv(); jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, @@ -2199,6 +2268,66 @@ static void android_server_InputManager_nativePreemptInputDispatch(JNIEnv* env, gNativeInputManager->preemptInputDispatch(); } +static jobject android_server_InputManager_nativeGetInputDevice(JNIEnv* env, + jclass clazz, jint deviceId) { + if (checkInputManagerUnitialized(env)) { + return NULL; + } + + InputDeviceInfo deviceInfo; + status_t status = gNativeInputManager->getInputManager()->getInputDeviceInfo( + deviceId, & deviceInfo); + if (status) { + return NULL; + } + + jobject deviceObj = env->NewObject(gInputDeviceClassInfo.clazz, gInputDeviceClassInfo.ctor); + if (! deviceObj) { + return NULL; + } + + jstring deviceNameObj = env->NewStringUTF(deviceInfo.getName().string()); + if (! deviceNameObj) { + return NULL; + } + + env->SetIntField(deviceObj, gInputDeviceClassInfo.mId, deviceInfo.getId()); + env->SetObjectField(deviceObj, gInputDeviceClassInfo.mName, deviceNameObj); + env->SetIntField(deviceObj, gInputDeviceClassInfo.mSources, deviceInfo.getSources()); + env->SetIntField(deviceObj, gInputDeviceClassInfo.mKeyboardType, deviceInfo.getKeyboardType()); + + const KeyedVector& ranges = deviceInfo.getMotionRanges(); + for (size_t i = 0; i < ranges.size(); i++) { + int rangeType = ranges.keyAt(i); + const InputDeviceInfo::MotionRange& range = ranges.valueAt(i); + env->CallVoidMethod(deviceObj, gInputDeviceClassInfo.addMotionRange, + rangeType, range.min, range.max, range.flat, range.fuzz); + if (env->ExceptionCheck()) { + return NULL; + } + } + + return deviceObj; +} + +static jintArray android_server_InputManager_nativeGetInputDeviceIds(JNIEnv* env, + jclass clazz) { + if (checkInputManagerUnitialized(env)) { + return NULL; + } + + Vector deviceIds; + gNativeInputManager->getInputManager()->getInputDeviceIds(deviceIds); + + jintArray deviceIdsObj = env->NewIntArray(deviceIds.size()); + if (! deviceIdsObj) { + return NULL; + } + + env->SetIntArrayRegion(deviceIdsObj, 0, deviceIds.size(), deviceIds.array()); + return deviceIdsObj; +} + static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) { if (checkInputManagerUnitialized(env)) { return NULL; @@ -2242,6 +2371,10 @@ static JNINativeMethod gInputManagerMethods[] = { (void*) android_server_InputManager_nativeSetInputDispatchMode }, { "nativePreemptInputDispatch", "()V", (void*) android_server_InputManager_nativePreemptInputDispatch }, + { "nativeGetInputDevice", "(I)Landroid/view/InputDevice;", + (void*) android_server_InputManager_nativeGetInputDevice }, + { "nativeGetInputDeviceIds", "()[I", + (void*) android_server_InputManager_nativeGetInputDeviceIds }, { "nativeDump", "()Ljava/lang/String;", (void*) android_server_InputManager_nativeDump }, }; @@ -2311,6 +2444,10 @@ int register_android_server_InputManager(JNIEnv* env) { "getVirtualKeyDefinitions", "(Ljava/lang/String;)[Lcom/android/server/InputManager$VirtualKeyDefinition;"); + GET_METHOD_ID(gCallbacksClassInfo.getInputDeviceCalibration, gCallbacksClassInfo.clazz, + "getInputDeviceCalibration", + "(Ljava/lang/String;)Lcom/android/server/InputManager$InputDeviceCalibration;"); + GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz, "getExcludedDeviceNames", "()[Ljava/lang/String;"); @@ -2337,6 +2474,17 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz, "height", "I"); + // InputDeviceCalibration + + FIND_CLASS(gInputDeviceCalibrationClassInfo.clazz, + "com/android/server/InputManager$InputDeviceCalibration"); + + GET_FIELD_ID(gInputDeviceCalibrationClassInfo.keys, gInputDeviceCalibrationClassInfo.clazz, + "keys", "[Ljava/lang/String;"); + + GET_FIELD_ID(gInputDeviceCalibrationClassInfo.values, gInputDeviceCalibrationClassInfo.clazz, + "values", "[Ljava/lang/String;"); + // InputWindow FIND_CLASS(gInputWindowClassInfo.clazz, "com/android/server/InputWindow"); @@ -2407,10 +2555,35 @@ int register_android_server_InputManager(JNIEnv* env) { FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent"); - // MotionEVent + // MotionEvent FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent"); + // InputDevice + + FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice"); + + GET_METHOD_ID(gInputDeviceClassInfo.ctor, gInputDeviceClassInfo.clazz, + "", "()V"); + + GET_METHOD_ID(gInputDeviceClassInfo.addMotionRange, gInputDeviceClassInfo.clazz, + "addMotionRange", "(IFFFF)V"); + + GET_FIELD_ID(gInputDeviceClassInfo.mId, gInputDeviceClassInfo.clazz, + "mId", "I"); + + GET_FIELD_ID(gInputDeviceClassInfo.mName, gInputDeviceClassInfo.clazz, + "mName", "Ljava/lang/String;"); + + GET_FIELD_ID(gInputDeviceClassInfo.mSources, gInputDeviceClassInfo.clazz, + "mSources", "I"); + + GET_FIELD_ID(gInputDeviceClassInfo.mKeyboardType, gInputDeviceClassInfo.clazz, + "mKeyboardType", "I"); + + GET_FIELD_ID(gInputDeviceClassInfo.mMotionRanges, gInputDeviceClassInfo.clazz, + "mMotionRanges", "[Landroid/view/InputDevice$MotionRange;"); + return 0; } -- cgit v1.1