More work in progress on native events.
Refactored the code to eliminate potential deadlocks due to re-entrant
calls from the policy into the dispatcher. Also added some plumbing
that will be used to notify the framework about ANRs.
Change-Id: Iba7a10de0cb3c56cd7520d6ce716db52fdcc94ff
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 53262ae..a988a96 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -16,9 +16,20 @@
#define LOG_TAG "InputManager-JNI"
+//#define LOG_NDEBUG 0
+
+// Log debug messages about InputReaderPolicy
+#define DEBUG_INPUT_READER_POLICY 1
+
+// Log debug messages about InputDispatcherPolicy
+#define DEBUG_INPUT_DISPATCHER_POLICY 1
+
+
#include "JNIHelp.h"
#include "jni.h"
#include <android_runtime/AndroidRuntime.h>
+#include <ui/InputReader.h>
+#include <ui/InputDispatcher.h>
#include <ui/InputManager.h>
#include <ui/InputTransport.h>
#include <utils/Log.h>
@@ -30,73 +41,7 @@
namespace android {
-class InputDispatchPolicy : public InputDispatchPolicyInterface {
-public:
- InputDispatchPolicy(JNIEnv* env, jobject callbacks);
- virtual ~InputDispatchPolicy();
-
- void setDisplaySize(int32_t displayId, int32_t width, int32_t height);
- void setDisplayOrientation(int32_t displayId, int32_t orientation);
-
- virtual bool getDisplayInfo(int32_t displayId,
- int32_t* width, int32_t* height, int32_t* orientation);
-
- virtual void notifyConfigurationChanged(nsecs_t when,
- int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig);
-
- virtual void notifyLidSwitchChanged(nsecs_t when, bool lidOpen);
-
- virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId,
- int32_t action, int32_t flags, int32_t keyCode,
- int32_t scanCode, int32_t metaState, nsecs_t downTime);
-
- virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
- bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags);
- virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown,
- bool rolled);
- virtual int32_t interceptTouch(nsecs_t when);
-
- virtual bool filterTouchEvents();
- virtual bool filterJumpyTouchEvents();
- virtual void getVirtualKeyDefinitions(const String8& deviceName,
- Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions);
- virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
-
- virtual bool allowKeyRepeat();
- virtual nsecs_t getKeyRepeatTimeout();
-
- virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
- Vector<InputTarget>& outTargets);
- virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
- Vector<InputTarget>& outTargets);
-
-private:
- bool isScreenOn();
- bool isScreenBright();
-
-private:
- jobject mCallbacks;
-
- int32_t mFilterTouchEvents;
- int32_t mFilterJumpyTouchEvents;
-
- Mutex mDisplayLock;
- int32_t mDisplayWidth, mDisplayHeight;
- int32_t mDisplayOrientation;
-
- inline JNIEnv* threadEnv() const {
- return AndroidRuntime::getJNIEnv();
- }
-};
-
-
-// globals
-
-static sp<EventHub> gEventHub;
-static sp<InputDispatchPolicy> gInputDispatchPolicy;
-static sp<InputManager> gInputManager;
-
-// JNI
+// ----------------------------------------------------------------------------
static struct {
jclass clazz;
@@ -128,8 +73,498 @@
jfieldID height;
} gVirtualKeyDefinitionClassInfo;
+// ----------------------------------------------------------------------------
+
+class NativeInputManager : public virtual RefBase,
+ public virtual InputReaderPolicyInterface,
+ public virtual InputDispatcherPolicyInterface {
+protected:
+ virtual ~NativeInputManager();
+
+public:
+ NativeInputManager(jobject callbacksObj);
+
+ inline sp<InputManager> getInputManager() const { return mInputManager; }
+
+ void setDisplaySize(int32_t displayId, int32_t width, int32_t height);
+ void setDisplayOrientation(int32_t displayId, int32_t orientation);
+
+ /* --- InputReaderPolicyInterface implementation --- */
+
+ virtual bool getDisplayInfo(int32_t displayId,
+ int32_t* width, int32_t* height, int32_t* orientation);
+ virtual void virtualKeyFeedback(nsecs_t when, int32_t deviceId,
+ int32_t action, int32_t flags, int32_t keyCode,
+ int32_t scanCode, int32_t metaState, nsecs_t downTime);
+ virtual int32_t interceptKey(nsecs_t when, int32_t deviceId,
+ bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags);
+ virtual int32_t interceptTrackball(nsecs_t when, bool buttonChanged, bool buttonDown,
+ bool rolled);
+ virtual int32_t interceptTouch(nsecs_t when);
+ virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue);
+ virtual bool filterTouchEvents();
+ virtual bool filterJumpyTouchEvents();
+ virtual void getVirtualKeyDefinitions(const String8& deviceName,
+ Vector<InputReaderPolicyInterface::VirtualKeyDefinition>& outVirtualKeyDefinitions);
+ virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames);
+
+ /* --- InputDispatcherPolicyInterface implementation --- */
+
+ virtual void notifyConfigurationChanged(nsecs_t when);
+ virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel);
+ virtual void notifyInputChannelANR(const sp<InputChannel>& inputChannel);
+ virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel);
+ virtual nsecs_t getKeyRepeatTimeout();
+ virtual void getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+ Vector<InputTarget>& outTargets);
+ virtual void getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ Vector<InputTarget>& outTargets);
+
+private:
+ sp<InputManager> mInputManager;
+
+ jobject mCallbacksObj;
+
+ // Cached filtering policies.
+ int32_t mFilterTouchEvents;
+ int32_t mFilterJumpyTouchEvents;
+
+ // Cached display state. (lock mDisplayLock)
+ Mutex mDisplayLock;
+ int32_t mDisplayWidth, mDisplayHeight;
+ int32_t mDisplayOrientation;
+
+ // Callbacks.
+ bool isScreenOn();
+ bool isScreenBright();
+
+ static inline JNIEnv* jniEnv() {
+ return AndroidRuntime::getJNIEnv();
+ }
+
+ static bool isAppSwitchKey(int32_t keyCode);
+ static bool checkExceptionFromCallback(JNIEnv* env, const char* methodName);
+};
+
+// ----------------------------------------------------------------------------
+
+NativeInputManager::NativeInputManager(jobject callbacksObj) :
+ mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
+ mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1) {
+ JNIEnv* env = jniEnv();
+
+ mCallbacksObj = env->NewGlobalRef(callbacksObj);
+
+ sp<EventHub> eventHub = new EventHub();
+ mInputManager = new InputManager(eventHub, this, this);
+}
+
+NativeInputManager::~NativeInputManager() {
+ JNIEnv* env = jniEnv();
+
+ env->DeleteGlobalRef(mCallbacksObj);
+}
+
+bool NativeInputManager::isAppSwitchKey(int32_t keyCode) {
+ return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL;
+}
+
+bool NativeInputManager::checkExceptionFromCallback(JNIEnv* env, const char* methodName) {
+ if (env->ExceptionCheck()) {
+ LOGE("An exception was thrown by callback '%s'.", methodName);
+ LOGE_EX(env);
+ env->ExceptionClear();
+ return true;
+ }
+ return false;
+}
+
+void NativeInputManager::setDisplaySize(int32_t displayId, int32_t width, int32_t height) {
+ if (displayId == 0) {
+ AutoMutex _l(mDisplayLock);
+
+ mDisplayWidth = width;
+ mDisplayHeight = height;
+ }
+}
+
+void NativeInputManager::setDisplayOrientation(int32_t displayId, int32_t orientation) {
+ if (displayId == 0) {
+ AutoMutex _l(mDisplayLock);
+
+ mDisplayOrientation = orientation;
+ }
+}
+
+bool NativeInputManager::getDisplayInfo(int32_t displayId,
+ int32_t* width, int32_t* height, int32_t* orientation) {
+ bool result = false;
+ if (displayId == 0) {
+ AutoMutex _l(mDisplayLock);
+
+ if (mDisplayWidth > 0) {
+ *width = mDisplayWidth;
+ *height = mDisplayHeight;
+ *orientation = mDisplayOrientation;
+ result = true;
+ }
+ }
+ return result;
+}
+
+bool NativeInputManager::isScreenOn() {
+ JNIEnv* env = jniEnv();
+
+ jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenOn);
+ if (checkExceptionFromCallback(env, "isScreenOn")) {
+ return true;
+ }
+ return result;
+}
+
+bool NativeInputManager::isScreenBright() {
+ JNIEnv* env = jniEnv();
+
+ jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.isScreenBright);
+ if (checkExceptionFromCallback(env, "isScreenBright")) {
+ return true;
+ }
+ return result;
+}
+
+void NativeInputManager::virtualKeyFeedback(nsecs_t when, int32_t deviceId,
+ int32_t action, int32_t flags, int32_t keyCode,
+ int32_t scanCode, int32_t metaState, nsecs_t downTime) {
+#if DEBUG_INPUT_READER_POLICY
+ LOGD("virtualKeyFeedback - when=%lld, deviceId=%d, action=%d, flags=%d, keyCode=%d, "
+ "scanCode=%d, metaState=%d, downTime=%lld",
+ when, deviceId, action, flags, keyCode, scanCode, metaState, downTime);
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyFeedback,
+ when, deviceId, action, flags, keyCode, scanCode, metaState, downTime);
+ checkExceptionFromCallback(env, "virtualKeyFeedback");
+}
+
+int32_t NativeInputManager::interceptKey(nsecs_t when,
+ int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) {
+#if DEBUG_INPUT_READER_POLICY
+ LOGD("interceptKey - when=%lld, deviceId=%d, down=%d, keyCode=%d, scanCode=%d, "
+ "policyFlags=%d",
+ when, deviceId, down, keyCode, scanCode, policyFlags);
+#endif
+
+ const int32_t WM_ACTION_PASS_TO_USER = 1;
+ const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
+ const int32_t WM_ACTION_GO_TO_SLEEP = 4;
+
+ JNIEnv* env = jniEnv();
+
+ bool isScreenOn = this->isScreenOn();
+ bool isScreenBright = this->isScreenBright();
+
+ jint wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.hackInterceptKey,
+ deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn);
+ if (checkExceptionFromCallback(env, "hackInterceptKey")) {
+ wmActions = 0;
+ }
+
+ int32_t actions = InputReaderPolicyInterface::ACTION_NONE;
+ if (! isScreenOn) {
+ // Key presses and releases wake the device.
+ actions |= InputReaderPolicyInterface::ACTION_WOKE_HERE;
+ }
+
+ if (! isScreenBright) {
+ // Key presses and releases brighten the screen if dimmed.
+ actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE;
+ }
+
+ if (wmActions & WM_ACTION_GO_TO_SLEEP) {
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.goToSleep, when);
+ checkExceptionFromCallback(env, "goToSleep");
+ }
+
+ if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivityForKey, when);
+ checkExceptionFromCallback(env, "pokeUserActivityForKey");
+ }
+
+ if (wmActions & WM_ACTION_PASS_TO_USER) {
+ actions |= InputReaderPolicyInterface::ACTION_DISPATCH;
+ }
+
+ if (! (wmActions & WM_ACTION_PASS_TO_USER)) {
+ if (down && isAppSwitchKey(keyCode)) {
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing);
+ checkExceptionFromCallback(env, "notifyAppSwitchComing");
+
+ actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING;
+ }
+ }
+ return actions;
+}
+
+int32_t NativeInputManager::interceptTouch(nsecs_t when) {
+#if DEBUG_INPUT_READER_POLICY
+ LOGD("interceptTouch - when=%lld", when);
+#endif
+
+ if (! isScreenOn()) {
+ // Touch events do not wake the device.
+ return InputReaderPolicyInterface::ACTION_NONE;
+ }
+
+ return InputReaderPolicyInterface::ACTION_DISPATCH;
+}
+
+int32_t NativeInputManager::interceptTrackball(nsecs_t when,
+ bool buttonChanged, bool buttonDown, bool rolled) {
+#if DEBUG_INPUT_READER_POLICY
+ LOGD("interceptTrackball - when=%lld, buttonChanged=%d, buttonDown=%d, rolled=%d",
+ when, buttonChanged, buttonDown, rolled);
+#endif
+
+ if (! isScreenOn()) {
+ // Trackball motions and button presses do not wake the device.
+ return InputReaderPolicyInterface::ACTION_NONE;
+ }
+
+ return InputReaderPolicyInterface::ACTION_DISPATCH;
+}
+
+int32_t NativeInputManager::interceptSwitch(nsecs_t when, int32_t switchCode,
+ int32_t switchValue) {
+#if DEBUG_INPUT_READER_POLICY
+ LOGD("interceptSwitch - when=%lld, switchCode=%d, switchValue=%d",
+ when, switchCode, switchValue);
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ switch (switchCode) {
+ case SW_LID:
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged,
+ when, switchValue == 0);
+ checkExceptionFromCallback(env, "notifyLidSwitchChanged");
+ break;
+ }
+
+ return InputReaderPolicyInterface::ACTION_NONE;
+}
+
+bool NativeInputManager::filterTouchEvents() {
+ if (mFilterTouchEvents < 0) {
+ JNIEnv* env = jniEnv();
+
+ jboolean result = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.filterTouchEvents);
+ if (checkExceptionFromCallback(env, "filterTouchEvents")) {
+ result = false;
+ }
+
+ mFilterTouchEvents = result ? 1 : 0;
+ }
+ return mFilterTouchEvents;
+}
+
+bool NativeInputManager::filterJumpyTouchEvents() {
+ if (mFilterJumpyTouchEvents < 0) {
+ JNIEnv* env = jniEnv();
+
+ jboolean result = env->CallBooleanMethod(mCallbacksObj,
+ gCallbacksClassInfo.filterJumpyTouchEvents);
+ if (checkExceptionFromCallback(env, "filterJumpyTouchEvents")) {
+ result = false;
+ }
+
+ mFilterJumpyTouchEvents = result ? 1 : 0;
+ }
+ return mFilterJumpyTouchEvents;
+}
+
+void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName,
+ Vector<InputReaderPolicyInterface::VirtualKeyDefinition>& outVirtualKeyDefinitions) {
+ JNIEnv* env = jniEnv();
+
+ jstring deviceNameStr = env->NewStringUTF(deviceName.string());
+ if (! checkExceptionFromCallback(env, "getVirtualKeyDefinitions")) {
+ jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
+ gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr));
+ if (! checkExceptionFromCallback(env, "getVirtualKeyDefinitions") && result) {
+ jsize length = env->GetArrayLength(result);
+ for (jsize i = 0; i < length; i++) {
+ jobject item = env->GetObjectArrayElement(result, i);
+
+ outVirtualKeyDefinitions.add();
+ outVirtualKeyDefinitions.editTop().scanCode =
+ int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.scanCode));
+ outVirtualKeyDefinitions.editTop().centerX =
+ int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerX));
+ outVirtualKeyDefinitions.editTop().centerY =
+ int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerY));
+ outVirtualKeyDefinitions.editTop().width =
+ int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.width));
+ outVirtualKeyDefinitions.editTop().height =
+ int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.height));
+
+ env->DeleteLocalRef(item);
+ }
+ env->DeleteLocalRef(result);
+ }
+ env->DeleteLocalRef(deviceNameStr);
+ }
+}
+
+void NativeInputManager::getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
+ JNIEnv* env = jniEnv();
+
+ jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
+ gCallbacksClassInfo.getExcludedDeviceNames));
+ if (! checkExceptionFromCallback(env, "getExcludedDeviceNames") && result) {
+ jsize length = env->GetArrayLength(result);
+ for (jsize i = 0; i < length; i++) {
+ jstring item = jstring(env->GetObjectArrayElement(result, i));
+
+ const char* deviceNameChars = env->GetStringUTFChars(item, NULL);
+ outExcludedDeviceNames.add(String8(deviceNameChars));
+ env->ReleaseStringUTFChars(item, deviceNameChars);
+
+ env->DeleteLocalRef(item);
+ }
+ env->DeleteLocalRef(result);
+ }
+}
+
+void NativeInputManager::notifyConfigurationChanged(nsecs_t when) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("notifyConfigurationChanged - when=%lld", when);
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ InputConfiguration config;
+ mInputManager->getInputConfiguration(& config);
+
+ env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyConfigurationChanged,
+ when, config.touchScreen, config.keyboard, config.navigation);
+ checkExceptionFromCallback(env, "notifyConfigurationChanged");
+}
+
+void NativeInputManager::notifyInputChannelBroken(const sp<InputChannel>& inputChannel) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("notifyInputChannelBroken - inputChannel='%s'", inputChannel->getName().string());
+#endif
+
+ // TODO
+}
+
+void NativeInputManager::notifyInputChannelANR(const sp<InputChannel>& inputChannel) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("notifyInputChannelANR - inputChannel='%s'",
+ inputChannel->getName().string());
+#endif
+
+ // TODO
+}
+
+void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("notifyInputChannelRecoveredFromANR - inputChannel='%s'",
+ inputChannel->getName().string());
+#endif
+
+ // TODO
+}
+
+nsecs_t NativeInputManager::getKeyRepeatTimeout() {
+ if (! isScreenOn()) {
+ // Disable key repeat when the screen is off.
+ return -1;
+ } else {
+ // TODO use ViewConfiguration.getLongPressTimeout()
+ return milliseconds_to_nanoseconds(500);
+ }
+}
+
+void NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
+ Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("getKeyEventTargets - policyFlags=%d", policyFlags);
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
+ if (! keyEventObj) {
+ LOGE("Could not obtain DVM KeyEvent object to get key event targets.");
+ } else {
+ jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
+ gCallbacksClassInfo.getKeyEventTargets,
+ keyEventObj, jint(keyEvent->getNature()), jint(policyFlags)));
+ if (! checkExceptionFromCallback(env, "getKeyEventTargets") && result) {
+ jsize length = env->GetArrayLength(result);
+ for (jsize i = 0; i < length; i++) {
+ jobject item = env->GetObjectArrayElement(result, i);
+ if (! item) {
+ break; // found null element indicating end of used portion of the array
+ }
+
+ outTargets.add();
+ android_view_InputTarget_toNative(env, item, & outTargets.editTop());
+
+ env->DeleteLocalRef(item);
+ }
+ env->DeleteLocalRef(result);
+ }
+ env->DeleteLocalRef(keyEventObj);
+ }
+}
+
+void NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
+ Vector<InputTarget>& outTargets) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ LOGD("getMotionEventTargets - policyFlags=%d", policyFlags);
+#endif
+
+ JNIEnv* env = jniEnv();
+
+ jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent);
+ if (! motionEventObj) {
+ LOGE("Could not obtain DVM MotionEvent object to get key event targets.");
+ } else {
+ jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj,
+ gCallbacksClassInfo.getMotionEventTargets,
+ motionEventObj, jint(motionEvent->getNature()), jint(policyFlags)));
+ if (! checkExceptionFromCallback(env, "getMotionEventTargets") && result) {
+ jsize length = env->GetArrayLength(result);
+ for (jsize i = 0; i < length; i++) {
+ jobject item = env->GetObjectArrayElement(result, i);
+ if (! item) {
+ break; // found null element indicating end of used portion of the array
+ }
+
+ outTargets.add();
+ android_view_InputTarget_toNative(env, item, & outTargets.editTop());
+
+ env->DeleteLocalRef(item);
+ }
+ env->DeleteLocalRef(result);
+ }
+ android_view_MotionEvent_recycle(env, motionEventObj);
+ env->DeleteLocalRef(motionEventObj);
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+
+static sp<NativeInputManager> gNativeInputManager;
+
static bool checkInputManagerUnitialized(JNIEnv* env) {
- if (gInputManager == NULL) {
+ if (gNativeInputManager == NULL) {
LOGE("Input manager not initialized.");
jniThrowRuntimeException(env, "Input manager not initialized.");
return true;
@@ -139,16 +574,11 @@
static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,
jobject callbacks) {
- if (gEventHub == NULL) {
- gEventHub = new EventHub();
- }
-
- if (gInputDispatchPolicy == NULL) {
- gInputDispatchPolicy = new InputDispatchPolicy(env, callbacks);
- }
-
- if (gInputManager == NULL) {
- gInputManager = new InputManager(gEventHub, gInputDispatchPolicy);
+ if (gNativeInputManager == NULL) {
+ gNativeInputManager = new NativeInputManager(callbacks);
+ } else {
+ LOGE("Input manager already initialized.");
+ jniThrowRuntimeException(env, "Input manager already initialized.");
}
}
@@ -157,7 +587,7 @@
return;
}
- status_t result = gInputManager->start();
+ status_t result = gNativeInputManager->getInputManager()->start();
if (result) {
jniThrowRuntimeException(env, "Input manager could not be started.");
}
@@ -173,7 +603,7 @@
// to be passed in like this, not sure which is better but leaving it like this
// keeps the window manager in direct control of when display transitions propagate down
// to the input dispatcher
- gInputDispatchPolicy->setDisplaySize(displayId, width, height);
+ gNativeInputManager->setDisplaySize(displayId, width, height);
}
static void android_server_InputManager_nativeSetDisplayOrientation(JNIEnv* env, jclass clazz,
@@ -182,7 +612,7 @@
return;
}
- gInputDispatchPolicy->setDisplayOrientation(displayId, orientation);
+ gNativeInputManager->setDisplayOrientation(displayId, orientation);
}
static jint android_server_InputManager_nativeGetScanCodeState(JNIEnv* env, jclass clazz,
@@ -191,7 +621,8 @@
return KEY_STATE_UNKNOWN;
}
- return gInputManager->getScanCodeState(deviceId, deviceClasses, scanCode);
+ return gNativeInputManager->getInputManager()->getScanCodeState(
+ deviceId, deviceClasses, scanCode);
}
static jint android_server_InputManager_nativeGetKeyCodeState(JNIEnv* env, jclass clazz,
@@ -200,7 +631,8 @@
return KEY_STATE_UNKNOWN;
}
- return gInputManager->getKeyCodeState(deviceId, deviceClasses, keyCode);
+ return gNativeInputManager->getInputManager()->getKeyCodeState(
+ deviceId, deviceClasses, keyCode);
}
static jint android_server_InputManager_nativeGetSwitchState(JNIEnv* env, jclass clazz,
@@ -209,7 +641,7 @@
return KEY_STATE_UNKNOWN;
}
- return gInputManager->getSwitchState(deviceId, deviceClasses, sw);
+ return gNativeInputManager->getInputManager()->getSwitchState(deviceId, deviceClasses, sw);
}
static jboolean android_server_InputManager_nativeHasKeys(JNIEnv* env, jclass clazz,
@@ -223,7 +655,7 @@
jsize numCodes = env->GetArrayLength(keyCodes);
jboolean result;
if (numCodes == env->GetArrayLength(outFlags)) {
- result = gInputManager->hasKeys(numCodes, codes, flags);
+ result = gNativeInputManager->getInputManager()->hasKeys(numCodes, codes, flags);
} else {
result = JNI_FALSE;
}
@@ -243,7 +675,9 @@
LOGW("Input channel object '%s' was disposed without first being unregistered with "
"the input manager!", inputChannel->getName().string());
- gInputManager->unregisterInputChannel(inputChannel);
+ if (gNativeInputManager != NULL) {
+ gNativeInputManager->getInputManager()->unregisterInputChannel(inputChannel);
+ }
}
static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
@@ -259,7 +693,7 @@
return;
}
- status_t status = gInputManager->registerInputChannel(inputChannel);
+ status_t status = gNativeInputManager->getInputManager()->registerInputChannel(inputChannel);
if (status) {
jniThrowRuntimeException(env, "Failed to register input channel. "
"Check logs for details.");
@@ -285,13 +719,15 @@
android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
- status_t status = gInputManager->unregisterInputChannel(inputChannel);
+ status_t status = gNativeInputManager->getInputManager()->unregisterInputChannel(inputChannel);
if (status) {
jniThrowRuntimeException(env, "Failed to unregister input channel. "
"Check logs for details.");
}
}
+// ----------------------------------------------------------------------------
+
static JNINativeMethod gInputManagerMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "(Lcom/android/server/InputManager$Callbacks;)V",
@@ -334,7 +770,7 @@
gInputManagerMethods, NELEM(gInputManagerMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
- // Policy
+ // Callbacks
FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/InputManager$Callbacks");
@@ -407,340 +843,4 @@
return 0;
}
-// static functions
-
-static bool isAppSwitchKey(int32_t keyCode) {
- return keyCode == KEYCODE_HOME || keyCode == KEYCODE_ENDCALL;
-}
-
-static bool checkException(JNIEnv* env, const char* methodName) {
- if (env->ExceptionCheck()) {
- LOGE("An exception was thrown by an InputDispatchPolicy callback '%s'.", methodName);
- LOGE_EX(env);
- env->ExceptionClear();
- return true;
- }
- return false;
-}
-
-
-// InputDispatchPolicy implementation
-
-InputDispatchPolicy::InputDispatchPolicy(JNIEnv* env, jobject callbacks) :
- mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
- mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1) {
- mCallbacks = env->NewGlobalRef(callbacks);
-}
-
-InputDispatchPolicy::~InputDispatchPolicy() {
- JNIEnv* env = threadEnv();
-
- env->DeleteGlobalRef(mCallbacks);
-}
-
-void InputDispatchPolicy::setDisplaySize(int32_t displayId, int32_t width, int32_t height) {
- if (displayId == 0) {
- AutoMutex _l(mDisplayLock);
-
- mDisplayWidth = width;
- mDisplayHeight = height;
- }
-}
-
-void InputDispatchPolicy::setDisplayOrientation(int32_t displayId, int32_t orientation) {
- if (displayId == 0) {
- AutoMutex _l(mDisplayLock);
-
- mDisplayOrientation = orientation;
- }
-}
-
-bool InputDispatchPolicy::getDisplayInfo(int32_t displayId,
- int32_t* width, int32_t* height, int32_t* orientation) {
- bool result = false;
- if (displayId == 0) {
- AutoMutex _l(mDisplayLock);
-
- if (mDisplayWidth > 0) {
- *width = mDisplayWidth;
- *height = mDisplayHeight;
- *orientation = mDisplayOrientation;
- result = true;
- }
- }
- return result;
-}
-
-bool InputDispatchPolicy::isScreenOn() {
- JNIEnv* env = threadEnv();
-
- jboolean result = env->CallBooleanMethod(mCallbacks, gCallbacksClassInfo.isScreenOn);
- if (checkException(env, "isScreenOn")) {
- return true;
- }
- return result;
-}
-
-bool InputDispatchPolicy::isScreenBright() {
- JNIEnv* env = threadEnv();
-
- jboolean result = env->CallBooleanMethod(mCallbacks, gCallbacksClassInfo.isScreenBright);
- if (checkException(env, "isScreenBright")) {
- return true;
- }
- return result;
-}
-
-void InputDispatchPolicy::notifyConfigurationChanged(nsecs_t when,
- int32_t touchScreenConfig, int32_t keyboardConfig, int32_t navigationConfig) {
- JNIEnv* env = threadEnv();
-
- env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.notifyConfigurationChanged,
- when, touchScreenConfig, keyboardConfig, navigationConfig);
- checkException(env, "notifyConfigurationChanged");
-}
-
-void InputDispatchPolicy::notifyLidSwitchChanged(nsecs_t when, bool lidOpen) {
- JNIEnv* env = threadEnv();
- env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.notifyLidSwitchChanged,
- when, lidOpen);
- checkException(env, "notifyLidSwitchChanged");
-}
-
-void InputDispatchPolicy::virtualKeyFeedback(nsecs_t when, int32_t deviceId,
- int32_t action, int32_t flags, int32_t keyCode,
- int32_t scanCode, int32_t metaState, nsecs_t downTime) {
- JNIEnv* env = threadEnv();
-
- env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.virtualKeyFeedback,
- when, deviceId, action, flags, keyCode, scanCode, metaState, downTime);
- checkException(env, "virtualKeyFeedback");
-}
-
-int32_t InputDispatchPolicy::interceptKey(nsecs_t when,
- int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t policyFlags) {
- const int32_t WM_ACTION_PASS_TO_USER = 1;
- const int32_t WM_ACTION_POKE_USER_ACTIVITY = 2;
- const int32_t WM_ACTION_GO_TO_SLEEP = 4;
-
- JNIEnv* env = threadEnv();
-
- bool isScreenOn = this->isScreenOn();
- bool isScreenBright = this->isScreenBright();
-
- jint wmActions = env->CallIntMethod(mCallbacks, gCallbacksClassInfo.hackInterceptKey,
- deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn);
- if (checkException(env, "hackInterceptKey")) {
- wmActions = 0;
- }
-
- int32_t actions = ACTION_NONE;
- if (! isScreenOn) {
- // Key presses and releases wake the device.
- actions |= ACTION_WOKE_HERE;
- }
-
- if (! isScreenBright) {
- // Key presses and releases brighten the screen if dimmed.
- actions |= ACTION_BRIGHT_HERE;
- }
-
- if (wmActions & WM_ACTION_GO_TO_SLEEP) {
- env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.goToSleep, when);
- checkException(env, "goToSleep");
- }
-
- if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) {
- env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.pokeUserActivityForKey, when);
- checkException(env, "pokeUserActivityForKey");
- }
-
- if (wmActions & WM_ACTION_PASS_TO_USER) {
- actions |= ACTION_DISPATCH;
- }
-
- if (! (wmActions & WM_ACTION_PASS_TO_USER)) {
- if (down && isAppSwitchKey(keyCode)) {
- env->CallVoidMethod(mCallbacks, gCallbacksClassInfo.notifyAppSwitchComing);
- checkException(env, "notifyAppSwitchComing");
-
- actions |= ACTION_APP_SWITCH_COMING;
- }
- }
- return actions;
-}
-
-int32_t InputDispatchPolicy::interceptTouch(nsecs_t when) {
- if (! isScreenOn()) {
- // Touch events do not wake the device.
- return ACTION_NONE;
- }
-
- return ACTION_DISPATCH;
-}
-
-int32_t InputDispatchPolicy::interceptTrackball(nsecs_t when,
- bool buttonChanged, bool buttonDown, bool rolled) {
- if (! isScreenOn()) {
- // Trackball motions and button presses do not wake the device.
- return ACTION_NONE;
- }
-
- return ACTION_DISPATCH;
-}
-
-bool InputDispatchPolicy::filterTouchEvents() {
- if (mFilterTouchEvents < 0) {
- JNIEnv* env = threadEnv();
-
- jboolean result = env->CallBooleanMethod(mCallbacks,
- gCallbacksClassInfo.filterTouchEvents);
- if (checkException(env, "filterTouchEvents")) {
- result = false;
- }
-
- mFilterTouchEvents = result ? 1 : 0;
- }
- return mFilterTouchEvents;
-}
-
-bool InputDispatchPolicy::filterJumpyTouchEvents() {
- if (mFilterJumpyTouchEvents < 0) {
- JNIEnv* env = threadEnv();
-
- jboolean result = env->CallBooleanMethod(mCallbacks,
- gCallbacksClassInfo.filterJumpyTouchEvents);
- if (checkException(env, "filterJumpyTouchEvents")) {
- result = false;
- }
-
- mFilterJumpyTouchEvents = result ? 1 : 0;
- }
- return mFilterJumpyTouchEvents;
-}
-
-void InputDispatchPolicy::getVirtualKeyDefinitions(const String8& deviceName,
- Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) {
- JNIEnv* env = threadEnv();
-
- jstring deviceNameStr = env->NewStringUTF(deviceName.string());
- if (! checkException(env, "getVirtualKeyDefinitions")) {
- jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks,
- gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr));
- if (! checkException(env, "getVirtualKeyDefinitions") && result) {
- jsize length = env->GetArrayLength(result);
- for (jsize i = 0; i < length; i++) {
- jobject item = env->GetObjectArrayElement(result, i);
-
- outVirtualKeyDefinitions.add();
- outVirtualKeyDefinitions.editTop().scanCode =
- int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.scanCode));
- outVirtualKeyDefinitions.editTop().centerX =
- int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerX));
- outVirtualKeyDefinitions.editTop().centerY =
- int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.centerY));
- outVirtualKeyDefinitions.editTop().width =
- int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.width));
- outVirtualKeyDefinitions.editTop().height =
- int32_t(env->GetIntField(item, gVirtualKeyDefinitionClassInfo.height));
-
- env->DeleteLocalRef(item);
- }
- env->DeleteLocalRef(result);
- }
- env->DeleteLocalRef(deviceNameStr);
- }
-}
-
-void InputDispatchPolicy::getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
- JNIEnv* env = threadEnv();
-
- jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks,
- gCallbacksClassInfo.getExcludedDeviceNames));
- if (! checkException(env, "getExcludedDeviceNames") && result) {
- jsize length = env->GetArrayLength(result);
- for (jsize i = 0; i < length; i++) {
- jstring item = jstring(env->GetObjectArrayElement(result, i));
-
- const char* deviceNameChars = env->GetStringUTFChars(item, NULL);
- outExcludedDeviceNames.add(String8(deviceNameChars));
- env->ReleaseStringUTFChars(item, deviceNameChars);
-
- env->DeleteLocalRef(item);
- }
- env->DeleteLocalRef(result);
- }
-}
-
-bool InputDispatchPolicy::allowKeyRepeat() {
- // Disable key repeat when the screen is off.
- return isScreenOn();
-}
-
-nsecs_t InputDispatchPolicy::getKeyRepeatTimeout() {
- // TODO use ViewConfiguration.getLongPressTimeout()
- return milliseconds_to_nanoseconds(500);
-}
-
-void InputDispatchPolicy::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags,
- Vector<InputTarget>& outTargets) {
- JNIEnv* env = threadEnv();
-
- jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);
- if (! keyEventObj) {
- LOGE("Could not obtain DVM KeyEvent object to get key event targets.");
- } else {
- jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks,
- gCallbacksClassInfo.getKeyEventTargets,
- keyEventObj, jint(keyEvent->getNature()), jint(policyFlags)));
- if (! checkException(env, "getKeyEventTargets") && result) {
- jsize length = env->GetArrayLength(result);
- for (jsize i = 0; i < length; i++) {
- jobject item = env->GetObjectArrayElement(result, i);
- if (! item) {
- break; // found null element indicating end of used portion of the array
- }
-
- outTargets.add();
- android_view_InputTarget_toNative(env, item, & outTargets.editTop());
-
- env->DeleteLocalRef(item);
- }
- env->DeleteLocalRef(result);
- }
- env->DeleteLocalRef(keyEventObj);
- }
-}
-
-void InputDispatchPolicy::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
- Vector<InputTarget>& outTargets) {
- JNIEnv* env = threadEnv();
-
- jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent);
- if (! motionEventObj) {
- LOGE("Could not obtain DVM MotionEvent object to get key event targets.");
- } else {
- jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacks,
- gCallbacksClassInfo.getMotionEventTargets,
- motionEventObj, jint(motionEvent->getNature()), jint(policyFlags)));
- if (! checkException(env, "getMotionEventTargets") && result) {
- jsize length = env->GetArrayLength(result);
- for (jsize i = 0; i < length; i++) {
- jobject item = env->GetObjectArrayElement(result, i);
- if (! item) {
- break; // found null element indicating end of used portion of the array
- }
-
- outTargets.add();
- android_view_InputTarget_toNative(env, item, & outTargets.editTop());
-
- env->DeleteLocalRef(item);
- }
- env->DeleteLocalRef(result);
- }
- android_view_MotionEvent_recycle(env, motionEventObj);
- env->DeleteLocalRef(motionEventObj);
- }
-}
-
} /* namespace android */