/* * Copyright (C) 2010 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. */ #define LOG_TAG "InputManager-JNI" //#define LOG_NDEBUG 0 // Log debug messages about InputReaderPolicy #define DEBUG_INPUT_READER_POLICY 0 // Log debug messages about InputDispatcherPolicy #define DEBUG_INPUT_DISPATCHER_POLICY 0 // Log debug messages about input focus tracking #define DEBUG_FOCUS 0 #include "JNIHelp.h" #include "jni.h" #include #include #include #include #include #include #include #include #include "../../core/jni/android_view_KeyEvent.h" #include "../../core/jni/android_view_MotionEvent.h" #include "../../core/jni/android_view_InputChannel.h" #include "com_android_server_PowerManagerService.h" namespace android { // Window flags from WindowManager.LayoutParams enum { FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001, FLAG_DIM_BEHIND = 0x00000002, FLAG_BLUR_BEHIND = 0x00000004, FLAG_NOT_FOCUSABLE = 0x00000008, FLAG_NOT_TOUCHABLE = 0x00000010, FLAG_NOT_TOUCH_MODAL = 0x00000020, FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040, FLAG_KEEP_SCREEN_ON = 0x00000080, FLAG_LAYOUT_IN_SCREEN = 0x00000100, FLAG_LAYOUT_NO_LIMITS = 0x00000200, FLAG_FULLSCREEN = 0x00000400, FLAG_FORCE_NOT_FULLSCREEN = 0x00000800, FLAG_DITHER = 0x00001000, FLAG_SECURE = 0x00002000, FLAG_SCALED = 0x00004000, FLAG_IGNORE_CHEEK_PRESSES = 0x00008000, FLAG_LAYOUT_INSET_DECOR = 0x00010000, FLAG_ALT_FOCUSABLE_IM = 0x00020000, FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000, FLAG_SHOW_WHEN_LOCKED = 0x00080000, FLAG_SHOW_WALLPAPER = 0x00100000, FLAG_TURN_SCREEN_ON = 0x00200000, FLAG_DISMISS_KEYGUARD = 0x00400000, FLAG_IMMERSIVE = 0x00800000, FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000, FLAG_COMPATIBLE_WINDOW = 0x20000000, FLAG_SYSTEM_ERROR = 0x40000000, }; // Window types from WindowManager.LayoutParams enum { FIRST_APPLICATION_WINDOW = 1, TYPE_BASE_APPLICATION = 1, TYPE_APPLICATION = 2, TYPE_APPLICATION_STARTING = 3, LAST_APPLICATION_WINDOW = 99, FIRST_SUB_WINDOW = 1000, TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW, TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1, TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2, TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3, TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4, LAST_SUB_WINDOW = 1999, FIRST_SYSTEM_WINDOW = 2000, TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW, TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1, TYPE_PHONE = FIRST_SYSTEM_WINDOW+2, TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3, TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4, TYPE_TOAST = FIRST_SYSTEM_WINDOW+5, TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6, TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7, TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8, TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9, TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10, TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11, TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12, TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13, TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14, LAST_SYSTEM_WINDOW = 2999, }; // Delay between reporting long touch events to the power manager. const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms // Default input dispatching timeout if there is no focused application or paused window // from which to determine an appropriate dispatching timeout. const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec // Minimum amount of time to provide to the input dispatcher for delivery of an event // regardless of how long the application window was paused. const nsecs_t MIN_INPUT_DISPATCHING_TIMEOUT = 1000 * 1000000LL; // 1 sec // ---------------------------------------------------------------------------- static struct { jclass clazz; jmethodID notifyConfigurationChanged; jmethodID notifyLidSwitchChanged; jmethodID notifyInputChannelBroken; jmethodID notifyInputChannelANR; jmethodID notifyInputChannelRecoveredFromANR; jmethodID notifyANR; jmethodID virtualKeyDownFeedback; jmethodID interceptKeyBeforeQueueing; jmethodID interceptKeyBeforeDispatching; jmethodID checkInjectEventsPermission; jmethodID notifyAppSwitchComing; jmethodID filterTouchEvents; jmethodID filterJumpyTouchEvents; jmethodID getVirtualKeyDefinitions; jmethodID getInputDeviceCalibration; jmethodID getExcludedDeviceNames; jmethodID getMaxEventsPerSecond; } gCallbacksClassInfo; static struct { jclass clazz; jfieldID scanCode; jfieldID centerX; jfieldID centerY; jfieldID width; jfieldID height; } gVirtualKeyDefinitionClassInfo; static struct { jclass clazz; jfieldID keys; jfieldID values; } gInputDeviceCalibrationClassInfo; static struct { jclass clazz; jfieldID inputChannel; jfieldID layoutParamsFlags; jfieldID layoutParamsType; jfieldID dispatchingTimeoutNanos; jfieldID frameLeft; jfieldID frameTop; jfieldID touchableAreaLeft; jfieldID touchableAreaTop; jfieldID touchableAreaRight; jfieldID touchableAreaBottom; jfieldID visible; jfieldID hasFocus; jfieldID hasWallpaper; jfieldID paused; jfieldID ownerPid; jfieldID ownerUid; } gInputWindowClassInfo; static struct { jclass clazz; jfieldID name; jfieldID dispatchingTimeoutNanos; jfieldID token; } gInputApplicationClassInfo; static struct { jclass clazz; } gKeyEventClassInfo; 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() { return systemTime(SYSTEM_TIME_MONOTONIC); } // ---------------------------------------------------------------------------- class NativeInputManager : public virtual RefBase, public virtual InputReaderPolicyInterface, public virtual InputDispatcherPolicyInterface { protected: virtual ~NativeInputManager(); public: NativeInputManager(jobject callbacksObj); inline sp getInputManager() const { return mInputManager; } String8 dump(); void setDisplaySize(int32_t displayId, int32_t width, int32_t height); void setDisplayOrientation(int32_t displayId, int32_t orientation); status_t registerInputChannel(JNIEnv* env, const sp& inputChannel, jweak inputChannelObjWeak, bool monitor); status_t unregisterInputChannel(JNIEnv* env, const sp& inputChannel); void setInputWindows(JNIEnv* env, jobjectArray windowObjArray); void setFocusedApplication(JNIEnv* env, jobject applicationObj); void setInputDispatchMode(bool enabled, bool frozen); void preemptInputDispatch(); /* --- InputReaderPolicyInterface implementation --- */ virtual bool getDisplayInfo(int32_t displayId, int32_t* width, int32_t* height, int32_t* orientation); virtual void virtualKeyDownFeedback(); virtual int32_t interceptKey(nsecs_t when, int32_t deviceId, bool down, int32_t keyCode, int32_t scanCode, uint32_t& policyFlags); virtual int32_t interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue, uint32_t& policyFlags); virtual int32_t interceptGeneric(nsecs_t when, uint32_t& policyFlags); virtual bool filterTouchEvents(); virtual bool filterJumpyTouchEvents(); virtual void getVirtualKeyDefinitions(const String8& deviceName, Vector& outVirtualKeyDefinitions); virtual void getInputDeviceCalibration(const String8& deviceName, InputDeviceCalibration& outCalibration); virtual void getExcludedDeviceNames(Vector& outExcludedDeviceNames); /* --- InputDispatcherPolicyInterface implementation --- */ virtual void notifyConfigurationChanged(nsecs_t when); virtual void notifyInputChannelBroken(const sp& inputChannel); virtual bool notifyInputChannelANR(const sp& inputChannel, nsecs_t& outNewTimeout); virtual void notifyInputChannelRecoveredFromANR(const sp& inputChannel); virtual nsecs_t getKeyRepeatTimeout(); virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector& outTargets); virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector& outTargets); virtual int32_t getMaxEventsPerSecond(); private: struct InputWindow { sp inputChannel; int32_t layoutParamsFlags; int32_t layoutParamsType; nsecs_t dispatchingTimeout; int32_t frameLeft; int32_t frameTop; int32_t touchableAreaLeft; int32_t touchableAreaTop; int32_t touchableAreaRight; int32_t touchableAreaBottom; bool visible; bool hasFocus; bool hasWallpaper; bool paused; int32_t ownerPid; int32_t ownerUid; inline bool touchableAreaContainsPoint(int32_t x, int32_t y) { return x >= touchableAreaLeft && x <= touchableAreaRight && y >= touchableAreaTop && y <= touchableAreaBottom; } }; struct InputApplication { String8 name; nsecs_t dispatchingTimeout; jweak tokenObjWeak; }; class ANRTimer { enum Budget { SYSTEM = 0, APPLICATION = 1 }; Budget mBudget; nsecs_t mStartTime; bool mFrozen; InputWindow* mPausedWindow; public: ANRTimer(); void dispatchFrozenBySystem(); void dispatchPausedByApplication(InputWindow* pausedWindow); bool waitForDispatchStateChangeLd(NativeInputManager* inputManager); nsecs_t getTimeSpentWaitingForApplication() const; }; sp mInputManager; jobject mCallbacksObj; // Cached filtering policies. int32_t mFilterTouchEvents; int32_t mFilterJumpyTouchEvents; // Cached throttling policy. int32_t mMaxEventsPerSecond; // Cached display state. (lock mDisplayLock) Mutex mDisplayLock; int32_t mDisplayWidth, mDisplayHeight; int32_t mDisplayOrientation; // Power manager interactions. bool isScreenOn(); bool isScreenBright(); // Weak references to all currently registered input channels by connection pointer. Mutex mInputChannelRegistryLock; KeyedVector mInputChannelObjWeakTable; jobject getInputChannelObjLocal(JNIEnv* env, const sp& inputChannel); // Input target and focus tracking. (lock mDispatchLock) Mutex mDispatchLock; Condition mDispatchStateChanged; bool mDispatchEnabled; bool mDispatchFrozen; bool mWindowsReady; Vector mWindows; Vector mWallpaperWindows; Vector > mMonitoringChannels; // Focus tracking for keys, trackball, etc. InputWindow* mFocusedWindow; // Focus tracking for touch. bool mTouchDown; InputWindow* mTouchedWindow; // primary target for current down Vector mTouchedWallpaperWindows; // wallpaper targets Vector mTempTouchedOutsideWindows; // temporary outside touch targets Vector > mTempTouchedWallpaperChannels; // temporary wallpaper targets // Focused application. InputApplication* mFocusedApplication; InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication void dumpDeviceInfo(String8& dump); void dumpDispatchStateLd(String8& dump); void logDispatchStateLd(); bool notifyANR(jobject tokenObj, nsecs_t& outNewTimeout); void releaseFocusedApplicationLd(JNIEnv* env); int32_t waitForFocusedWindowLd(uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector& outTargets, InputWindow*& outFocusedWindow); int32_t waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector& outTargets, InputWindow*& outTouchedWindow); void releaseTouchedWindowLd(); int32_t waitForNonTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector& outTargets); int32_t waitForTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector& outTargets); bool interceptKeyBeforeDispatching(const InputTarget& target, const KeyEvent* keyEvent, uint32_t policyFlags); void pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType); void pokeUserActivity(nsecs_t eventTime, int32_t eventType); bool checkInjectionPermission(const InputWindow* window, int32_t injectorPid, int32_t injectorUid); static bool populateWindow(JNIEnv* env, jobject windowObj, InputWindow& outWindow); static void addTarget(const InputWindow* window, int32_t targetFlags, nsecs_t timeSpentWaitingForApplication, Vector& outTargets); void registerMonitoringChannel(const sp& inputChannel); void unregisterMonitoringChannel(const sp& inputChannel); void addMonitoringTargetsLd(Vector& outTargets); static inline JNIEnv* jniEnv() { return AndroidRuntime::getJNIEnv(); } static bool isAppSwitchKey(int32_t keyCode); static bool isPolicyKey(int32_t keyCode, bool isScreenOn); static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); }; // ---------------------------------------------------------------------------- NativeInputManager::NativeInputManager(jobject callbacksObj) : mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mMaxEventsPerSecond(-1), mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0), mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true), mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL), mFocusedApplication(NULL) { JNIEnv* env = jniEnv(); mCallbacksObj = env->NewGlobalRef(callbacksObj); sp eventHub = new EventHub(); mInputManager = new InputManager(eventHub, this, this); } NativeInputManager::~NativeInputManager() { JNIEnv* env = jniEnv(); env->DeleteGlobalRef(mCallbacksObj); releaseFocusedApplicationLd(env); } String8 NativeInputManager::dump() { String8 dump; { // acquire lock AutoMutex _l(mDisplayLock); dump.append("Native Input Dispatcher State:\n"); dumpDispatchStateLd(dump); dump.append("\n"); } // release lock dump.append("Input Devices:\n"); dumpDeviceInfo(dump); return dump; } bool NativeInputManager::isAppSwitchKey(int32_t keyCode) { return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL; } bool NativeInputManager::isPolicyKey(int32_t keyCode, bool isScreenOn) { // Special keys that the WindowManagerPolicy might care about. switch (keyCode) { case AKEYCODE_VOLUME_UP: case AKEYCODE_VOLUME_DOWN: case AKEYCODE_ENDCALL: case AKEYCODE_POWER: case AKEYCODE_CALL: case AKEYCODE_HOME: case AKEYCODE_MENU: case AKEYCODE_SEARCH: // media keys case AKEYCODE_HEADSETHOOK: case AKEYCODE_MEDIA_PLAY_PAUSE: case AKEYCODE_MEDIA_STOP: case AKEYCODE_MEDIA_NEXT: case AKEYCODE_MEDIA_PREVIOUS: case AKEYCODE_MEDIA_REWIND: case AKEYCODE_MEDIA_FAST_FORWARD: return true; default: // We need to pass all keys to the policy in the following cases: // - screen is off // - keyguard is visible // - policy is performing key chording //return ! isScreenOn || keyguardVisible || chording; return true; // XXX stubbed out for now } } bool NativeInputManager::checkAndClearExceptionFromCallback(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; } } status_t NativeInputManager::registerInputChannel(JNIEnv* env, const sp& inputChannel, jobject inputChannelObj, bool monitor) { jweak inputChannelObjWeak = env->NewWeakGlobalRef(inputChannelObj); if (! inputChannelObjWeak) { LOGE("Could not create weak reference for input channel."); LOGE_EX(env); return NO_MEMORY; } status_t status; { AutoMutex _l(mInputChannelRegistryLock); ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get()); if (index >= 0) { LOGE("Input channel object '%s' has already been registered", inputChannel->getName().string()); status = INVALID_OPERATION; goto DeleteWeakRef; } mInputChannelObjWeakTable.add(inputChannel.get(), inputChannelObjWeak); } status = mInputManager->registerInputChannel(inputChannel); if (! status) { // Success. if (monitor) { registerMonitoringChannel(inputChannel); } return OK; } // Failed! { AutoMutex _l(mInputChannelRegistryLock); mInputChannelObjWeakTable.removeItem(inputChannel.get()); } DeleteWeakRef: env->DeleteWeakGlobalRef(inputChannelObjWeak); return status; } status_t NativeInputManager::unregisterInputChannel(JNIEnv* env, const sp& inputChannel) { jweak inputChannelObjWeak; { AutoMutex _l(mInputChannelRegistryLock); ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get()); if (index < 0) { LOGE("Input channel object '%s' is not currently registered", inputChannel->getName().string()); return INVALID_OPERATION; } inputChannelObjWeak = mInputChannelObjWeakTable.valueAt(index); mInputChannelObjWeakTable.removeItemsAt(index); } env->DeleteWeakGlobalRef(inputChannelObjWeak); unregisterMonitoringChannel(inputChannel); return mInputManager->unregisterInputChannel(inputChannel); } jobject NativeInputManager::getInputChannelObjLocal(JNIEnv* env, const sp& inputChannel) { { AutoMutex _l(mInputChannelRegistryLock); ssize_t index = mInputChannelObjWeakTable.indexOfKey(inputChannel.get()); if (index < 0) { return NULL; } jweak inputChannelObjWeak = mInputChannelObjWeakTable.valueAt(index); return env->NewLocalRef(inputChannelObjWeak); } } 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) { if (width) { *width = mDisplayWidth; } if (height) { *height = mDisplayHeight; } if (orientation) { *orientation = mDisplayOrientation; } result = true; } } return result; } bool NativeInputManager::isScreenOn() { return android_server_PowerManagerService_isScreenOn(); } bool NativeInputManager::isScreenBright() { return android_server_PowerManagerService_isScreenBright(); } void NativeInputManager::virtualKeyDownFeedback() { #if DEBUG_INPUT_READER_POLICY LOGD("virtualKeyDownFeedback"); #endif JNIEnv* env = jniEnv(); env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.virtualKeyDownFeedback); checkAndClearExceptionFromCallback(env, "virtualKeyDownFeedback"); } 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=0x%x", 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; bool isScreenOn = this->isScreenOn(); bool isScreenBright = this->isScreenBright(); jint wmActions = 0; if (isPolicyKey(keyCode, isScreenOn)) { JNIEnv* env = jniEnv(); wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.interceptKeyBeforeQueueing, when, keyCode, down, policyFlags, isScreenOn); if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) { wmActions = 0; } } else { wmActions = WM_ACTION_PASS_TO_USER; } int32_t actions = InputReaderPolicyInterface::ACTION_NONE; if (! isScreenOn) { // Key presses and releases wake the device. policyFlags |= POLICY_FLAG_WOKE_HERE; } if (! isScreenBright) { // Key presses and releases brighten the screen if dimmed. policyFlags |= POLICY_FLAG_BRIGHT_HERE; } if (wmActions & WM_ACTION_GO_TO_SLEEP) { android_server_PowerManagerService_goToSleep(when); } if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) { pokeUserActivity(when, POWER_MANAGER_BUTTON_EVENT); } if (wmActions & WM_ACTION_PASS_TO_USER) { actions |= InputReaderPolicyInterface::ACTION_DISPATCH; if (down && isAppSwitchKey(keyCode)) { JNIEnv* env = jniEnv(); env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyAppSwitchComing); checkAndClearExceptionFromCallback(env, "notifyAppSwitchComing"); actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING; } } return actions; } int32_t NativeInputManager::interceptGeneric(nsecs_t when, uint32_t& policyFlags) { #if DEBUG_INPUT_READER_POLICY LOGD("interceptGeneric - when=%lld, policyFlags=0x%x", when, policyFlags); #endif int32_t actions = InputReaderPolicyInterface::ACTION_NONE; if (isScreenOn()) { // Only dispatch events when the device is awake. // Do not wake the device. actions |= InputReaderPolicyInterface::ACTION_DISPATCH; if (! isScreenBright()) { // Brighten the screen if dimmed. policyFlags |= POLICY_FLAG_BRIGHT_HERE; } } return actions; } int32_t NativeInputManager::interceptSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue, uint32_t& policyFlags) { #if DEBUG_INPUT_READER_POLICY LOGD("interceptSwitch - when=%lld, switchCode=%d, switchValue=%d, policyFlags=0x%x", when, switchCode, switchValue, policyFlags); #endif JNIEnv* env = jniEnv(); switch (switchCode) { case SW_LID: env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyLidSwitchChanged, when, switchValue == 0); checkAndClearExceptionFromCallback(env, "notifyLidSwitchChanged"); break; } return InputReaderPolicyInterface::ACTION_NONE; } bool NativeInputManager::filterTouchEvents() { if (mFilterTouchEvents < 0) { JNIEnv* env = jniEnv(); jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.filterTouchEvents); if (checkAndClearExceptionFromCallback(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 (checkAndClearExceptionFromCallback(env, "filterJumpyTouchEvents")) { result = false; } mFilterJumpyTouchEvents = result ? 1 : 0; } return mFilterJumpyTouchEvents; } void NativeInputManager::getVirtualKeyDefinitions(const String8& deviceName, Vector& outVirtualKeyDefinitions) { outVirtualKeyDefinitions.clear(); JNIEnv* env = jniEnv(); jstring deviceNameStr = env->NewStringUTF(deviceName.string()); if (! checkAndClearExceptionFromCallback(env, "getVirtualKeyDefinitions")) { jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, gCallbacksClassInfo.getVirtualKeyDefinitions, deviceNameStr)); if (! checkAndClearExceptionFromCallback(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::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, gCallbacksClassInfo.getExcludedDeviceNames)); if (! checkAndClearExceptionFromCallback(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); checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged"); } void NativeInputManager::notifyInputChannelBroken(const sp& inputChannel) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("notifyInputChannelBroken - inputChannel='%s'", inputChannel->getName().string()); #endif JNIEnv* env = jniEnv(); jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel); if (inputChannelObjLocal) { env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelBroken, inputChannelObjLocal); checkAndClearExceptionFromCallback(env, "notifyInputChannelBroken"); env->DeleteLocalRef(inputChannelObjLocal); } unregisterMonitoringChannel(inputChannel); } bool NativeInputManager::notifyInputChannelANR(const sp& inputChannel, nsecs_t& outNewTimeout) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("notifyInputChannelANR - inputChannel='%s'", inputChannel->getName().string()); #endif JNIEnv* env = jniEnv(); jlong newTimeout; jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel); if (inputChannelObjLocal) { newTimeout = env->CallLongMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelANR, inputChannelObjLocal); if (checkAndClearExceptionFromCallback(env, "notifyInputChannelANR")) { newTimeout = -2; } env->DeleteLocalRef(inputChannelObjLocal); } else { newTimeout = -2; } if (newTimeout == -2) { return false; // abort } outNewTimeout = newTimeout; return true; // resume } void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp& inputChannel) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("notifyInputChannelRecoveredFromANR - inputChannel='%s'", inputChannel->getName().string()); #endif JNIEnv* env = jniEnv(); jobject inputChannelObjLocal = getInputChannelObjLocal(env, inputChannel); if (inputChannelObjLocal) { env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.notifyInputChannelRecoveredFromANR, inputChannelObjLocal); checkAndClearExceptionFromCallback(env, "notifyInputChannelRecoveredFromANR"); env->DeleteLocalRef(inputChannelObjLocal); } } bool NativeInputManager::notifyANR(jobject tokenObj, nsecs_t& outNewTimeout) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("notifyANR"); #endif JNIEnv* env = jniEnv(); jlong newTimeout = env->CallLongMethod(mCallbacksObj, gCallbacksClassInfo.notifyANR, tokenObj); if (checkAndClearExceptionFromCallback(env, "notifyANR")) { newTimeout = -2; } if (newTimeout == -2) { return false; // abort } outNewTimeout = newTimeout; return true; // resume } 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); } } int32_t NativeInputManager::getMaxEventsPerSecond() { if (mMaxEventsPerSecond < 0) { JNIEnv* env = jniEnv(); jint result = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getMaxEventsPerSecond); if (checkAndClearExceptionFromCallback(env, "getMaxEventsPerSecond")) { result = 60; } mMaxEventsPerSecond = result; } return mMaxEventsPerSecond; } void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) { #if DEBUG_FOCUS LOGD("setInputWindows"); #endif { // acquire lock AutoMutex _l(mDispatchLock); sp touchedWindowChannel; if (mTouchedWindow) { touchedWindowChannel = mTouchedWindow->inputChannel; mTouchedWindow = NULL; } size_t numTouchedWallpapers = mTouchedWallpaperWindows.size(); if (numTouchedWallpapers != 0) { for (size_t i = 0; i < numTouchedWallpapers; i++) { mTempTouchedWallpaperChannels.push(mTouchedWallpaperWindows[i]->inputChannel); } mTouchedWallpaperWindows.clear(); } bool hadFocusedWindow = mFocusedWindow != NULL; mWindows.clear(); mFocusedWindow = NULL; mWallpaperWindows.clear(); if (windowObjArray) { mWindowsReady = true; jsize length = env->GetArrayLength(windowObjArray); for (jsize i = 0; i < length; i++) { jobject inputTargetObj = env->GetObjectArrayElement(windowObjArray, i); if (! inputTargetObj) { break; // found null element indicating end of used portion of the array } mWindows.push(); InputWindow& window = mWindows.editTop(); bool valid = populateWindow(env, inputTargetObj, window); if (! valid) { mWindows.pop(); } env->DeleteLocalRef(inputTargetObj); } size_t numWindows = mWindows.size(); for (size_t i = 0; i < numWindows; i++) { InputWindow* window = & mWindows.editItemAt(i); if (window->hasFocus) { mFocusedWindow = window; } if (window->layoutParamsType == TYPE_WALLPAPER) { mWallpaperWindows.push(window); for (size_t j = 0; j < numTouchedWallpapers; j++) { if (window->inputChannel == mTempTouchedWallpaperChannels[i]) { mTouchedWallpaperWindows.push(window); } } } if (window->inputChannel == touchedWindowChannel) { mTouchedWindow = window; } } } else { mWindowsReady = false; } mTempTouchedWallpaperChannels.clear(); if ((hadFocusedWindow && ! mFocusedWindow) || (mFocusedWindow && ! mFocusedWindow->visible)) { preemptInputDispatch(); } mDispatchStateChanged.broadcast(); #if DEBUG_FOCUS logDispatchStateLd(); #endif } // release lock } bool NativeInputManager::populateWindow(JNIEnv* env, jobject windowObj, InputWindow& outWindow) { bool valid = false; jobject inputChannelObj = env->GetObjectField(windowObj, gInputWindowClassInfo.inputChannel); if (inputChannelObj) { sp inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); if (inputChannel != NULL) { jint layoutParamsFlags = env->GetIntField(windowObj, gInputWindowClassInfo.layoutParamsFlags); jint layoutParamsType = env->GetIntField(windowObj, gInputWindowClassInfo.layoutParamsType); jlong dispatchingTimeoutNanos = env->GetLongField(windowObj, gInputWindowClassInfo.dispatchingTimeoutNanos); jint frameLeft = env->GetIntField(windowObj, gInputWindowClassInfo.frameLeft); jint frameTop = env->GetIntField(windowObj, gInputWindowClassInfo.frameTop); jint touchableAreaLeft = env->GetIntField(windowObj, gInputWindowClassInfo.touchableAreaLeft); jint touchableAreaTop = env->GetIntField(windowObj, gInputWindowClassInfo.touchableAreaTop); jint touchableAreaRight = env->GetIntField(windowObj, gInputWindowClassInfo.touchableAreaRight); jint touchableAreaBottom = env->GetIntField(windowObj, gInputWindowClassInfo.touchableAreaBottom); jboolean visible = env->GetBooleanField(windowObj, gInputWindowClassInfo.visible); jboolean hasFocus = env->GetBooleanField(windowObj, gInputWindowClassInfo.hasFocus); jboolean hasWallpaper = env->GetBooleanField(windowObj, gInputWindowClassInfo.hasWallpaper); jboolean paused = env->GetBooleanField(windowObj, gInputWindowClassInfo.paused); jint ownerPid = env->GetIntField(windowObj, gInputWindowClassInfo.ownerPid); jint ownerUid = env->GetIntField(windowObj, gInputWindowClassInfo.ownerUid); outWindow.inputChannel = inputChannel; outWindow.layoutParamsFlags = layoutParamsFlags; outWindow.layoutParamsType = layoutParamsType; outWindow.dispatchingTimeout = dispatchingTimeoutNanos; outWindow.frameLeft = frameLeft; outWindow.frameTop = frameTop; outWindow.touchableAreaLeft = touchableAreaLeft; outWindow.touchableAreaTop = touchableAreaTop; outWindow.touchableAreaRight = touchableAreaRight; outWindow.touchableAreaBottom = touchableAreaBottom; outWindow.visible = visible; outWindow.hasFocus = hasFocus; outWindow.hasWallpaper = hasWallpaper; outWindow.paused = paused; outWindow.ownerPid = ownerPid; outWindow.ownerUid = ownerUid; valid = true; } else { LOGW("Dropping input target because its input channel is not initialized."); } env->DeleteLocalRef(inputChannelObj); } else { LOGW("Dropping input target because the input channel object was null."); } return valid; } void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationObj) { #if DEBUG_FOCUS LOGD("setFocusedApplication"); #endif { // acquire lock AutoMutex _l(mDispatchLock); releaseFocusedApplicationLd(env); if (applicationObj) { jstring nameObj = jstring(env->GetObjectField(applicationObj, gInputApplicationClassInfo.name)); jlong dispatchingTimeoutNanos = env->GetLongField(applicationObj, gInputApplicationClassInfo.dispatchingTimeoutNanos); jobject tokenObj = env->GetObjectField(applicationObj, gInputApplicationClassInfo.token); jweak tokenObjWeak = env->NewWeakGlobalRef(tokenObj); if (! tokenObjWeak) { LOGE("Could not create weak reference for application token."); LOGE_EX(env); env->ExceptionClear(); } env->DeleteLocalRef(tokenObj); mFocusedApplication = & mFocusedApplicationStorage; if (nameObj) { const char* nameStr = env->GetStringUTFChars(nameObj, NULL); mFocusedApplication->name.setTo(nameStr); env->ReleaseStringUTFChars(nameObj, nameStr); env->DeleteLocalRef(nameObj); } else { LOGE("InputApplication.name should not be null."); mFocusedApplication->name.setTo("unknown"); } mFocusedApplication->dispatchingTimeout = dispatchingTimeoutNanos; mFocusedApplication->tokenObjWeak = tokenObjWeak; } mDispatchStateChanged.broadcast(); #if DEBUG_FOCUS logDispatchStateLd(); #endif } // release lock } void NativeInputManager::releaseFocusedApplicationLd(JNIEnv* env) { if (mFocusedApplication) { env->DeleteWeakGlobalRef(mFocusedApplication->tokenObjWeak); mFocusedApplication = NULL; } } void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) { #if DEBUG_FOCUS LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen); #endif { // acquire lock AutoMutex _l(mDispatchLock); if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) { mDispatchEnabled = enabled; mDispatchFrozen = frozen; mDispatchStateChanged.broadcast(); } #if DEBUG_FOCUS logDispatchStateLd(); #endif } // release lock } void NativeInputManager::preemptInputDispatch() { #if DEBUG_FOCUS LOGD("preemptInputDispatch"); #endif mInputManager->preemptInputDispatch(); } int32_t NativeInputManager::waitForFocusedWindowLd(uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector& outTargets, InputWindow*& outFocusedWindow) { int32_t injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; bool firstIteration = true; ANRTimer anrTimer; for (;;) { if (firstIteration) { firstIteration = false; } else { if (! anrTimer.waitForDispatchStateChangeLd(this)) { LOGW("Dropping event because the dispatcher timed out waiting to identify " "the window that should receive it."); injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; break; } } // If dispatch is not enabled then fail. if (! mDispatchEnabled) { LOGI("Dropping event because input dispatch is disabled."); injectionResult = INPUT_EVENT_INJECTION_FAILED; break; } // If dispatch is frozen or we don't have valid window data yet then wait. if (mDispatchFrozen || ! mWindowsReady) { #if DEBUG_FOCUS LOGD("Waiting because dispatch is frozen or windows are not ready."); #endif anrTimer.dispatchFrozenBySystem(); continue; } // If there is no currently focused window and no focused application // then drop the event. if (! mFocusedWindow) { if (mFocusedApplication) { #if DEBUG_FOCUS LOGD("Waiting because there is no focused window but there is a " "focused application that may yet introduce a new target: '%s'.", mFocusedApplication->name.string()); #endif continue; } LOGI("Dropping event because there is no focused window or focused application."); injectionResult = INPUT_EVENT_INJECTION_FAILED; break; } // Check permissions. if (! checkInjectionPermission(mFocusedWindow, injectorPid, injectorUid)) { injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; break; } // If the currently focused window is paused then keep waiting. if (mFocusedWindow->paused) { #if DEBUG_FOCUS LOGD("Waiting because focused window is paused."); #endif anrTimer.dispatchPausedByApplication(mFocusedWindow); continue; } // Success! break; // done waiting, exit loop } // Output targets. if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) { addTarget(mFocusedWindow, InputTarget::FLAG_SYNC, anrTimer.getTimeSpentWaitingForApplication(), outTargets); outFocusedWindow = mFocusedWindow; } else { outFocusedWindow = NULL; } #if DEBUG_FOCUS LOGD("waitForFocusedWindow finished: injectionResult=%d", injectionResult); logDispatchStateLd(); #endif return injectionResult; } enum InjectionPermission { INJECTION_PERMISSION_UNKNOWN, INJECTION_PERMISSION_GRANTED, INJECTION_PERMISSION_DENIED }; int32_t NativeInputManager::waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector& outTargets, InputWindow*& outTouchedWindow) { nsecs_t startTime = now(); // For security reasons, we defer updating the touch state until we are sure that // event injection will be allowed. // // FIXME In the original code, screenWasOff could never be set to true. // The reason is that the POLICY_FLAG_WOKE_HERE // and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw // EV_KEY, EV_REL and EV_ABS events. As it happens, the touch event was // actually enqueued using the policyFlags that appeared in the final EV_SYN // events upon which no preprocessing took place. So policyFlags was always 0. // In the new native input dispatcher we're a bit more careful about event // preprocessing so the touches we receive can actually have non-zero policyFlags. // Unfortunately we obtain undesirable behavior. // // Here's what happens: // // When the device dims in anticipation of going to sleep, touches // in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause // the device to brighten and reset the user activity timer. // Touches on other windows (such as the launcher window) // are dropped. Then after a moment, the device goes to sleep. Oops. // // Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE // instead of POLICY_FLAG_WOKE_HERE... // bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE; int32_t action = motionEvent->getAction(); bool firstIteration = true; ANRTimer anrTimer; int32_t injectionResult; InjectionPermission injectionPermission; for (;;) { if (firstIteration) { firstIteration = false; } else { if (! anrTimer.waitForDispatchStateChangeLd(this)) { LOGW("Dropping event because the dispatcher timed out waiting to identify " "the window that should receive it."); injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; injectionPermission = INJECTION_PERMISSION_UNKNOWN; break; // timed out, exit wait loop } } // If dispatch is not enabled then fail. if (! mDispatchEnabled) { LOGI("Dropping event because input dispatch is disabled."); injectionResult = INPUT_EVENT_INJECTION_FAILED; injectionPermission = INJECTION_PERMISSION_UNKNOWN; break; // failed, exit wait loop } // If dispatch is frozen or we don't have valid window data yet then wait. if (mDispatchFrozen || ! mWindowsReady) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("Waiting because dispatch is frozen or windows are not ready."); #endif anrTimer.dispatchFrozenBySystem(); continue; } // Update the touch state as needed based on the properties of the touch event. if (action == AMOTION_EVENT_ACTION_DOWN) { /* Case 1: ACTION_DOWN */ InputWindow* newTouchedWindow = NULL; mTempTouchedOutsideWindows.clear(); int32_t x = int32_t(motionEvent->getX(0)); int32_t y = int32_t(motionEvent->getY(0)); InputWindow* topErrorWindow = NULL; // Traverse windows from front to back to find touched window and outside targets. size_t numWindows = mWindows.size(); for (size_t i = 0; i < numWindows; i++) { InputWindow* window = & mWindows.editItemAt(i); int32_t flags = window->layoutParamsFlags; if (flags & FLAG_SYSTEM_ERROR) { if (! topErrorWindow) { topErrorWindow = window; } } if (window->visible) { if (! (flags & FLAG_NOT_TOUCHABLE)) { bool isTouchModal = (flags & (FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL)) == 0; if (isTouchModal || window->touchableAreaContainsPoint(x, y)) { if (! screenWasOff || flags & FLAG_TOUCHABLE_WHEN_WAKING) { newTouchedWindow = window; } break; // found touched window, exit window loop } } if (flags & FLAG_WATCH_OUTSIDE_TOUCH) { mTempTouchedOutsideWindows.push(window); } } } // If there is an error window but it is not taking focus (typically because // it is invisible) then wait for it. Any other focused window may in // fact be in ANR state. if (topErrorWindow && newTouchedWindow != topErrorWindow) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("Waiting because system error window is pending."); #endif anrTimer.dispatchFrozenBySystem(); continue; // wait some more } // If we did not find a touched window then fail. if (! newTouchedWindow) { if (mFocusedApplication) { #if DEBUG_FOCUS LOGD("Waiting because there is no focused window but there is a " "focused application that may yet introduce a new target: '%s'.", mFocusedApplication->name.string()); #endif continue; } LOGI("Dropping event because there is no touched window or focused application."); injectionResult = INPUT_EVENT_INJECTION_FAILED; injectionPermission = INJECTION_PERMISSION_UNKNOWN; break; // failed, exit wait loop } // Check permissions. if (! checkInjectionPermission(newTouchedWindow, injectorPid, injectorUid)) { injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; injectionPermission = INJECTION_PERMISSION_DENIED; break; // failed, exit wait loop } // If the touched window is paused then keep waiting. if (newTouchedWindow->paused) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("Waiting because touched window is paused."); #endif anrTimer.dispatchPausedByApplication(newTouchedWindow); continue; // wait some more } // Success! Update the touch dispatch state for real. releaseTouchedWindowLd(); mTouchedWindow = newTouchedWindow; if (newTouchedWindow->hasWallpaper) { mTouchedWallpaperWindows.appendVector(mWallpaperWindows); } injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; injectionPermission = INJECTION_PERMISSION_GRANTED; break; // done } else { /* Case 2: Everything but ACTION_DOWN */ // Check permissions. if (! checkInjectionPermission(mTouchedWindow, injectorPid, injectorUid)) { injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; injectionPermission = INJECTION_PERMISSION_DENIED; break; // failed, exit wait loop } // If the pointer is not currently down, then ignore the event. if (! mTouchDown) { LOGI("Dropping event because the pointer is not down."); injectionResult = INPUT_EVENT_INJECTION_FAILED; injectionPermission = INJECTION_PERMISSION_GRANTED; break; // failed, exit wait loop } // If there is no currently touched window then fail. if (! mTouchedWindow) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("Dropping event because there is no touched window to receive it."); #endif injectionResult = INPUT_EVENT_INJECTION_FAILED; injectionPermission = INJECTION_PERMISSION_GRANTED; break; // failed, exit wait loop } // If the touched window is paused then keep waiting. if (mTouchedWindow->paused) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("Waiting because touched window is paused."); #endif anrTimer.dispatchPausedByApplication(mTouchedWindow); continue; // wait some more } // Success! injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; injectionPermission = INJECTION_PERMISSION_GRANTED; break; // done } } // Output targets. if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) { size_t numWallpaperWindows = mTouchedWallpaperWindows.size(); for (size_t i = 0; i < numWallpaperWindows; i++) { addTarget(mTouchedWallpaperWindows[i], 0, 0, outTargets); } size_t numOutsideWindows = mTempTouchedOutsideWindows.size(); for (size_t i = 0; i < numOutsideWindows; i++) { addTarget(mTempTouchedOutsideWindows[i], InputTarget::FLAG_OUTSIDE, 0, outTargets); } addTarget(mTouchedWindow, InputTarget::FLAG_SYNC, anrTimer.getTimeSpentWaitingForApplication(), outTargets); outTouchedWindow = mTouchedWindow; } else { outTouchedWindow = NULL; } mTempTouchedOutsideWindows.clear(); // Check injection permission once and for all. if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) { if (checkInjectionPermission(action == AMOTION_EVENT_ACTION_DOWN ? NULL : mTouchedWindow, injectorPid, injectorUid)) { injectionPermission = INJECTION_PERMISSION_GRANTED; } else { injectionPermission = INJECTION_PERMISSION_DENIED; } } // Update final pieces of touch state if the injector had permission. if (injectionPermission == INJECTION_PERMISSION_GRANTED) { if (action == AMOTION_EVENT_ACTION_DOWN) { if (mTouchDown) { // This is weird. We got a down but we thought it was already down! LOGW("Pointer down received while already down."); } else { mTouchDown = true; } if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { // Since we failed to identify a target for this touch down, we may still // be holding on to an earlier target from a previous touch down. Release it. releaseTouchedWindowLd(); } } else if (action == AMOTION_EVENT_ACTION_UP) { mTouchDown = false; releaseTouchedWindowLd(); } } else { LOGW("Not updating touch focus because injection was denied."); } #if DEBUG_FOCUS LOGD("waitForTouchedWindow finished: injectionResult=%d", injectionResult); logDispatchStateLd(); #endif return injectionResult; } void NativeInputManager::releaseTouchedWindowLd() { mTouchedWindow = NULL; mTouchedWallpaperWindows.clear(); } void NativeInputManager::addTarget(const InputWindow* window, int32_t targetFlags, nsecs_t timeSpentWaitingForApplication, Vector& outTargets) { nsecs_t timeout = window->dispatchingTimeout - timeSpentWaitingForApplication; if (timeout < MIN_INPUT_DISPATCHING_TIMEOUT) { timeout = MIN_INPUT_DISPATCHING_TIMEOUT; } outTargets.push(); InputTarget& target = outTargets.editTop(); target.inputChannel = window->inputChannel; target.flags = targetFlags; target.timeout = timeout; target.xOffset = - window->frameLeft; target.yOffset = - window->frameTop; } bool NativeInputManager::checkInjectionPermission(const InputWindow* window, int32_t injectorPid, int32_t injectorUid) { if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) { JNIEnv* env = jniEnv(); jboolean result = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.checkInjectEventsPermission, injectorPid, injectorUid); checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission"); if (! result) { if (window) { LOGW("Permission denied: injecting event from pid %d uid %d to window " "with input channel %s owned by uid %d", injectorPid, injectorUid, window->inputChannel->getName().string(), window->ownerUid); } else { LOGW("Permission denied: injecting event from pid %d uid %d", injectorPid, injectorUid); } return false; } } return true; } int32_t NativeInputManager::waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector& outTargets) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("waitForKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", policyFlags, injectorPid, injectorUid); #endif int32_t windowType; { // acquire lock AutoMutex _l(mDispatchLock); InputWindow* focusedWindow; int32_t injectionResult = waitForFocusedWindowLd(policyFlags, injectorPid, injectorUid, outTargets, /*out*/ focusedWindow); if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { return injectionResult; } windowType = focusedWindow->layoutParamsType; } // release lock if (isPolicyKey(keyEvent->getKeyCode(), isScreenOn())) { const InputTarget& target = outTargets.top(); bool consumed = interceptKeyBeforeDispatching(target, keyEvent, policyFlags); if (consumed) { outTargets.clear(); return INPUT_EVENT_INJECTION_SUCCEEDED; } addMonitoringTargetsLd(outTargets); } pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT); return INPUT_EVENT_INJECTION_SUCCEEDED; } int32_t NativeInputManager::waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector& outTargets) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("waitForMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", policyFlags, injectorPid, injectorUid); #endif int32_t source = motionEvent->getSource(); if (source & AINPUT_SOURCE_CLASS_POINTER) { return waitForTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid, outTargets); } else { return waitForNonTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid, outTargets); } } int32_t NativeInputManager::waitForNonTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector& outTargets) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("waitForNonTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", policyFlags, injectorPid, injectorUid); #endif int32_t windowType; { // acquire lock AutoMutex _l(mDispatchLock); InputWindow* focusedWindow; int32_t injectionResult = waitForFocusedWindowLd(policyFlags, injectorPid, injectorUid, outTargets, /*out*/ focusedWindow); if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { return injectionResult; } windowType = focusedWindow->layoutParamsType; addMonitoringTargetsLd(outTargets); } // release lock pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT); return INPUT_EVENT_INJECTION_SUCCEEDED; } int32_t NativeInputManager::waitForTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector& outTargets) { #if DEBUG_INPUT_DISPATCHER_POLICY LOGD("waitForTouchEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", policyFlags, injectorPid, injectorUid); #endif int32_t windowType; { // acquire lock AutoMutex _l(mDispatchLock); InputWindow* touchedWindow; int32_t injectionResult = waitForTouchedWindowLd(motionEvent, policyFlags, injectorPid, injectorUid, outTargets, /*out*/ touchedWindow); if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { return injectionResult; } windowType = touchedWindow->layoutParamsType; addMonitoringTargetsLd(outTargets); } // release lock int32_t eventType; switch (motionEvent->getAction()) { case AMOTION_EVENT_ACTION_DOWN: eventType = POWER_MANAGER_TOUCH_EVENT; break; case AMOTION_EVENT_ACTION_UP: eventType = POWER_MANAGER_TOUCH_UP_EVENT; break; default: if (motionEvent->getEventTime() - motionEvent->getDownTime() >= EVENT_IGNORE_DURATION) { eventType = POWER_MANAGER_TOUCH_EVENT; } else { eventType = POWER_MANAGER_LONG_TOUCH_EVENT; } break; } pokeUserActivityIfNeeded(windowType, eventType); return INPUT_EVENT_INJECTION_SUCCEEDED; } bool NativeInputManager::interceptKeyBeforeDispatching(const InputTarget& target, const KeyEvent* keyEvent, uint32_t policyFlags) { JNIEnv* env = jniEnv(); jobject inputChannelObj = getInputChannelObjLocal(env, target.inputChannel); if (inputChannelObj) { jboolean consumed = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.interceptKeyBeforeDispatching, inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(), keyEvent->getKeyCode(), keyEvent->getMetaState(), keyEvent->getRepeatCount(), policyFlags); bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching"); env->DeleteLocalRef(inputChannelObj); return consumed && ! error; } else { LOGW("Could not apply key dispatch policy because input channel '%s' is " "no longer valid.", target.inputChannel->getName().string()); return false; } } void NativeInputManager::pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType) { if (windowType != TYPE_KEYGUARD) { nsecs_t eventTime = now(); pokeUserActivity(eventTime, eventType); } } void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) { android_server_PowerManagerService_userActivity(eventTime, eventType); } void NativeInputManager::registerMonitoringChannel(const sp& inputChannel) { { // acquire lock AutoMutex _l(mDispatchLock); mMonitoringChannels.push(inputChannel); } // release lock } void NativeInputManager::unregisterMonitoringChannel(const sp& inputChannel) { { // acquire lock AutoMutex _l(mDispatchLock); for (size_t i = 0; i < mMonitoringChannels.size(); i++) { if (mMonitoringChannels[i] == inputChannel) { mMonitoringChannels.removeAt(i); break; } } } // release lock } void NativeInputManager::addMonitoringTargetsLd(Vector& outTargets) { for (size_t i = 0; i < mMonitoringChannels.size(); i++) { outTargets.push(); InputTarget& target = outTargets.editTop(); target.inputChannel = mMonitoringChannels[i]; target.flags = 0; target.timeout = -1; target.xOffset = 0; target.yOffset = 0; } } static void dumpMotionRange(String8& dump, const char* name, const InputDeviceInfo::MotionRange* range) { if (range) { dump.appendFormat(" %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n", name, range->min, range->max, range->flat, range->fuzz); } } #define DUMP_MOTION_RANGE(range) \ dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range)); void NativeInputManager::dumpDeviceInfo(String8& dump) { Vector deviceIds; mInputManager->getInputDeviceIds(deviceIds); InputDeviceInfo deviceInfo; for (size_t i = 0; i < deviceIds.size(); i++) { int32_t deviceId = deviceIds[i]; status_t result = mInputManager->getInputDeviceInfo(deviceId, & deviceInfo); if (result == NAME_NOT_FOUND) { continue; } else if (result != OK) { dump.appendFormat(" ** Unexpected error %d getting information about input devices.\n", result); continue; } dump.appendFormat(" Device %d: '%s'\n", deviceInfo.getId(), deviceInfo.getName().string()); dump.appendFormat(" sources = 0x%08x\n", deviceInfo.getSources()); dump.appendFormat(" keyboardType = %d\n", deviceInfo.getKeyboardType()); dump.append(" motion ranges:\n"); DUMP_MOTION_RANGE(X); DUMP_MOTION_RANGE(Y); DUMP_MOTION_RANGE(PRESSURE); DUMP_MOTION_RANGE(SIZE); DUMP_MOTION_RANGE(TOUCH_MAJOR); DUMP_MOTION_RANGE(TOUCH_MINOR); DUMP_MOTION_RANGE(TOOL_MAJOR); DUMP_MOTION_RANGE(TOOL_MINOR); DUMP_MOTION_RANGE(ORIENTATION); } } #undef DUMP_MOTION_RANGE void NativeInputManager::logDispatchStateLd() { String8 dump; dumpDispatchStateLd(dump); LOGD("%s", dump.string()); } void NativeInputManager::dumpDispatchStateLd(String8& dump) { dump.appendFormat(" dispatchEnabled: %d\n", mDispatchEnabled); dump.appendFormat(" dispatchFrozen: %d\n", mDispatchFrozen); dump.appendFormat(" windowsReady: %d\n", mWindowsReady); if (mFocusedApplication) { dump.appendFormat(" focusedApplication: name='%s', dispatchingTimeout=%0.3fms\n", mFocusedApplication->name.string(), mFocusedApplication->dispatchingTimeout / 1000000.0); } else { dump.append(" focusedApplication: \n"); } dump.appendFormat(" focusedWindow: '%s'\n", mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : ""); dump.appendFormat(" touchedWindow: '%s', touchDown=%d\n", mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "", mTouchDown); for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) { dump.appendFormat(" touchedWallpaperWindows[%d]: '%s'\n", i, mTouchedWallpaperWindows[i]->inputChannel->getName().string()); } for (size_t i = 0; i < mWindows.size(); i++) { dump.appendFormat(" windows[%d]: '%s', paused=%d, hasFocus=%d, hasWallpaper=%d, " "visible=%d, flags=0x%08x, type=0x%08x, " "frame=[%d,%d], touchableArea=[%d,%d][%d,%d], " "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", i, mWindows[i].inputChannel->getName().string(), mWindows[i].paused, mWindows[i].hasFocus, mWindows[i].hasWallpaper, mWindows[i].visible, mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType, mWindows[i].frameLeft, mWindows[i].frameTop, mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop, mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom, mWindows[i].ownerPid, mWindows[i].ownerUid, mWindows[i].dispatchingTimeout / 1000000.0); } for (size_t i = 0; i < mMonitoringChannels.size(); i++) { dump.appendFormat(" monitoringChannel[%d]: '%s'\n", i, mMonitoringChannels[i]->getName().string()); } } // ---------------------------------------------------------------------------- NativeInputManager::ANRTimer::ANRTimer() : mBudget(APPLICATION), mStartTime(now()), mFrozen(false), mPausedWindow(NULL) { } void NativeInputManager::ANRTimer::dispatchFrozenBySystem() { mFrozen = true; } void NativeInputManager::ANRTimer::dispatchPausedByApplication(InputWindow* pausedWindow) { mPausedWindow = pausedWindow; } bool NativeInputManager::ANRTimer::waitForDispatchStateChangeLd(NativeInputManager* inputManager) { nsecs_t currentTime = now(); Budget newBudget; nsecs_t dispatchingTimeout; sp pausedChannel = NULL; jobject tokenObj = NULL; if (mFrozen) { newBudget = SYSTEM; dispatchingTimeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT; mFrozen = false; } else if (mPausedWindow) { newBudget = APPLICATION; dispatchingTimeout = mPausedWindow->dispatchingTimeout; pausedChannel = mPausedWindow->inputChannel; mPausedWindow = NULL; } else if (inputManager->mFocusedApplication) { newBudget = APPLICATION; dispatchingTimeout = inputManager->mFocusedApplication->dispatchingTimeout; tokenObj = jniEnv()->NewLocalRef(inputManager->mFocusedApplication->tokenObjWeak); } else { newBudget = APPLICATION; dispatchingTimeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT; } if (mBudget != newBudget) { mBudget = newBudget; mStartTime = currentTime; } bool result = false; nsecs_t timeoutRemaining = mStartTime + dispatchingTimeout - currentTime; if (timeoutRemaining > 0 && inputManager->mDispatchStateChanged.waitRelative(inputManager->mDispatchLock, timeoutRemaining) == OK) { result = true; } else { if (pausedChannel != NULL || tokenObj != NULL) { bool resumed; nsecs_t newTimeout = 0; inputManager->mDispatchLock.unlock(); // release lock if (pausedChannel != NULL) { resumed = inputManager->notifyInputChannelANR(pausedChannel, /*out*/ newTimeout); } else { resumed = inputManager->notifyANR(tokenObj, /*out*/ newTimeout); } inputManager->mDispatchLock.lock(); // re-acquire lock if (resumed) { mStartTime = now() - dispatchingTimeout + newTimeout; result = true; } } } if (tokenObj) { jniEnv()->DeleteLocalRef(tokenObj); } return result; } nsecs_t NativeInputManager::ANRTimer::getTimeSpentWaitingForApplication() const { return mBudget == APPLICATION ? now() - mStartTime : 0; } // ---------------------------------------------------------------------------- static sp gNativeInputManager; static bool checkInputManagerUnitialized(JNIEnv* env) { if (gNativeInputManager == NULL) { LOGE("Input manager not initialized."); jniThrowRuntimeException(env, "Input manager not initialized."); return true; } return false; } static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz, jobject callbacks) { if (gNativeInputManager == NULL) { gNativeInputManager = new NativeInputManager(callbacks); } else { LOGE("Input manager already initialized."); jniThrowRuntimeException(env, "Input manager already initialized."); } } static void android_server_InputManager_nativeStart(JNIEnv* env, jclass clazz) { if (checkInputManagerUnitialized(env)) { return; } status_t result = gNativeInputManager->getInputManager()->start(); if (result) { jniThrowRuntimeException(env, "Input manager could not be started."); } } static void android_server_InputManager_nativeSetDisplaySize(JNIEnv* env, jclass clazz, jint displayId, jint width, jint height) { if (checkInputManagerUnitialized(env)) { return; } // XXX we could get this from the SurfaceFlinger directly instead of requiring it // 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 gNativeInputManager->setDisplaySize(displayId, width, height); } static void android_server_InputManager_nativeSetDisplayOrientation(JNIEnv* env, jclass clazz, jint displayId, jint orientation) { if (checkInputManagerUnitialized(env)) { return; } gNativeInputManager->setDisplayOrientation(displayId, orientation); } static jint android_server_InputManager_nativeGetScanCodeState(JNIEnv* env, jclass clazz, jint deviceId, jint sourceMask, jint scanCode) { if (checkInputManagerUnitialized(env)) { return AKEY_STATE_UNKNOWN; } return gNativeInputManager->getInputManager()->getScanCodeState( deviceId, uint32_t(sourceMask), scanCode); } static jint android_server_InputManager_nativeGetKeyCodeState(JNIEnv* env, jclass clazz, jint deviceId, jint sourceMask, jint keyCode) { if (checkInputManagerUnitialized(env)) { return AKEY_STATE_UNKNOWN; } return gNativeInputManager->getInputManager()->getKeyCodeState( deviceId, uint32_t(sourceMask), keyCode); } static jint android_server_InputManager_nativeGetSwitchState(JNIEnv* env, jclass clazz, jint deviceId, jint sourceMask, jint sw) { if (checkInputManagerUnitialized(env)) { return AKEY_STATE_UNKNOWN; } return gNativeInputManager->getInputManager()->getSwitchState( deviceId, uint32_t(sourceMask), sw); } static jboolean android_server_InputManager_nativeHasKeys(JNIEnv* env, jclass clazz, jint deviceId, jint sourceMask, jintArray keyCodes, jbooleanArray outFlags) { if (checkInputManagerUnitialized(env)) { return JNI_FALSE; } int32_t* codes = env->GetIntArrayElements(keyCodes, NULL); uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL); jsize numCodes = env->GetArrayLength(keyCodes); jboolean result; if (numCodes == env->GetArrayLength(keyCodes)) { result = gNativeInputManager->getInputManager()->hasKeys( deviceId, uint32_t(sourceMask), numCodes, codes, flags); } else { result = JNI_FALSE; } env->ReleaseBooleanArrayElements(outFlags, flags, 0); env->ReleaseIntArrayElements(keyCodes, codes, 0); return result; } static void throwInputChannelNotInitialized(JNIEnv* env) { jniThrowException(env, "java/lang/IllegalStateException", "inputChannel is not initialized"); } static void android_server_InputManager_handleInputChannelDisposed(JNIEnv* env, jobject inputChannelObj, const sp& inputChannel, void* data) { LOGW("Input channel object '%s' was disposed without first being unregistered with " "the input manager!", inputChannel->getName().string()); if (gNativeInputManager != NULL) { gNativeInputManager->unregisterInputChannel(env, inputChannel); } } static void android_server_InputManager_nativeRegisterInputChannel(JNIEnv* env, jclass clazz, jobject inputChannelObj, jboolean monitor) { if (checkInputManagerUnitialized(env)) { return; } sp inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); if (inputChannel == NULL) { throwInputChannelNotInitialized(env); return; } status_t status = gNativeInputManager->registerInputChannel( env, inputChannel, inputChannelObj, monitor); if (status) { jniThrowRuntimeException(env, "Failed to register input channel. " "Check logs for details."); return; } if (! monitor) { android_view_InputChannel_setDisposeCallback(env, inputChannelObj, android_server_InputManager_handleInputChannelDisposed, NULL); } } static void android_server_InputManager_nativeUnregisterInputChannel(JNIEnv* env, jclass clazz, jobject inputChannelObj) { if (checkInputManagerUnitialized(env)) { return; } sp inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); if (inputChannel == NULL) { throwInputChannelNotInitialized(env); return; } android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL); status_t status = gNativeInputManager->unregisterInputChannel(env, inputChannel); if (status) { jniThrowRuntimeException(env, "Failed to unregister input channel. " "Check logs for details."); } } static jint android_server_InputManager_nativeInjectInputEvent(JNIEnv* env, jclass clazz, jobject inputEventObj, jint injectorPid, jint injectorUid, jint syncMode, jint timeoutMillis) { if (checkInputManagerUnitialized(env)) { return INPUT_EVENT_INJECTION_FAILED; } if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) { KeyEvent keyEvent; android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent); return gNativeInputManager->getInputManager()->injectInputEvent(& keyEvent, injectorPid, injectorUid, syncMode, timeoutMillis); } else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) { MotionEvent motionEvent; android_view_MotionEvent_toNative(env, inputEventObj, & motionEvent); return gNativeInputManager->getInputManager()->injectInputEvent(& motionEvent, injectorPid, injectorUid, syncMode, timeoutMillis); } else { jniThrowRuntimeException(env, "Invalid input event type."); return INPUT_EVENT_INJECTION_FAILED; } } static void android_server_InputManager_nativeSetInputWindows(JNIEnv* env, jclass clazz, jobjectArray windowObjArray) { if (checkInputManagerUnitialized(env)) { return; } gNativeInputManager->setInputWindows(env, windowObjArray); } static void android_server_InputManager_nativeSetFocusedApplication(JNIEnv* env, jclass clazz, jobject applicationObj) { if (checkInputManagerUnitialized(env)) { return; } gNativeInputManager->setFocusedApplication(env, applicationObj); } static void android_server_InputManager_nativeSetInputDispatchMode(JNIEnv* env, jclass clazz, jboolean enabled, jboolean frozen) { if (checkInputManagerUnitialized(env)) { return; } gNativeInputManager->setInputDispatchMode(enabled, frozen); } static void android_server_InputManager_nativePreemptInputDispatch(JNIEnv* env, jclass clazz) { if (checkInputManagerUnitialized(env)) { return; } 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; } String8 dump(gNativeInputManager->dump()); return env->NewStringUTF(dump.string()); } // ---------------------------------------------------------------------------- static JNINativeMethod gInputManagerMethods[] = { /* name, signature, funcPtr */ { "nativeInit", "(Lcom/android/server/InputManager$Callbacks;)V", (void*) android_server_InputManager_nativeInit }, { "nativeStart", "()V", (void*) android_server_InputManager_nativeStart }, { "nativeSetDisplaySize", "(III)V", (void*) android_server_InputManager_nativeSetDisplaySize }, { "nativeSetDisplayOrientation", "(II)V", (void*) android_server_InputManager_nativeSetDisplayOrientation }, { "nativeGetScanCodeState", "(III)I", (void*) android_server_InputManager_nativeGetScanCodeState }, { "nativeGetKeyCodeState", "(III)I", (void*) android_server_InputManager_nativeGetKeyCodeState }, { "nativeGetSwitchState", "(III)I", (void*) android_server_InputManager_nativeGetSwitchState }, { "nativeHasKeys", "(II[I[Z)Z", (void*) android_server_InputManager_nativeHasKeys }, { "nativeRegisterInputChannel", "(Landroid/view/InputChannel;Z)V", (void*) android_server_InputManager_nativeRegisterInputChannel }, { "nativeUnregisterInputChannel", "(Landroid/view/InputChannel;)V", (void*) android_server_InputManager_nativeUnregisterInputChannel }, { "nativeInjectInputEvent", "(Landroid/view/InputEvent;IIII)I", (void*) android_server_InputManager_nativeInjectInputEvent }, { "nativeSetInputWindows", "([Lcom/android/server/InputWindow;)V", (void*) android_server_InputManager_nativeSetInputWindows }, { "nativeSetFocusedApplication", "(Lcom/android/server/InputApplication;)V", (void*) android_server_InputManager_nativeSetFocusedApplication }, { "nativeSetInputDispatchMode", "(ZZ)V", (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 }, }; #define FIND_CLASS(var, className) \ var = env->FindClass(className); \ LOG_FATAL_IF(! var, "Unable to find class " className); \ var = jclass(env->NewGlobalRef(var)); #define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ var = env->GetMethodID(clazz, methodName, methodDescriptor); \ LOG_FATAL_IF(! var, "Unable to find method " methodName); #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ LOG_FATAL_IF(! var, "Unable to find field " fieldName); int register_android_server_InputManager(JNIEnv* env) { int res = jniRegisterNativeMethods(env, "com/android/server/InputManager", gInputManagerMethods, NELEM(gInputManagerMethods)); LOG_FATAL_IF(res < 0, "Unable to register native methods."); // Callbacks FIND_CLASS(gCallbacksClassInfo.clazz, "com/android/server/InputManager$Callbacks"); GET_METHOD_ID(gCallbacksClassInfo.notifyConfigurationChanged, gCallbacksClassInfo.clazz, "notifyConfigurationChanged", "(JIII)V"); GET_METHOD_ID(gCallbacksClassInfo.notifyLidSwitchChanged, gCallbacksClassInfo.clazz, "notifyLidSwitchChanged", "(JZ)V"); GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelBroken, gCallbacksClassInfo.clazz, "notifyInputChannelBroken", "(Landroid/view/InputChannel;)V"); GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelANR, gCallbacksClassInfo.clazz, "notifyInputChannelANR", "(Landroid/view/InputChannel;)J"); GET_METHOD_ID(gCallbacksClassInfo.notifyInputChannelRecoveredFromANR, gCallbacksClassInfo.clazz, "notifyInputChannelRecoveredFromANR", "(Landroid/view/InputChannel;)V"); GET_METHOD_ID(gCallbacksClassInfo.notifyANR, gCallbacksClassInfo.clazz, "notifyANR", "(Ljava/lang/Object;)J"); GET_METHOD_ID(gCallbacksClassInfo.virtualKeyDownFeedback, gCallbacksClassInfo.clazz, "virtualKeyDownFeedback", "()V"); GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz, "interceptKeyBeforeQueueing", "(JIZIZ)I"); GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz, "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIII)Z"); GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz, "checkInjectEventsPermission", "(II)Z"); GET_METHOD_ID(gCallbacksClassInfo.notifyAppSwitchComing, gCallbacksClassInfo.clazz, "notifyAppSwitchComing", "()V"); GET_METHOD_ID(gCallbacksClassInfo.filterTouchEvents, gCallbacksClassInfo.clazz, "filterTouchEvents", "()Z"); GET_METHOD_ID(gCallbacksClassInfo.filterJumpyTouchEvents, gCallbacksClassInfo.clazz, "filterJumpyTouchEvents", "()Z"); GET_METHOD_ID(gCallbacksClassInfo.getVirtualKeyDefinitions, gCallbacksClassInfo.clazz, "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;"); GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz, "getMaxEventsPerSecond", "()I"); // VirtualKeyDefinition FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz, "com/android/server/InputManager$VirtualKeyDefinition"); GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.scanCode, gVirtualKeyDefinitionClassInfo.clazz, "scanCode", "I"); GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.centerX, gVirtualKeyDefinitionClassInfo.clazz, "centerX", "I"); GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.centerY, gVirtualKeyDefinitionClassInfo.clazz, "centerY", "I"); GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.width, gVirtualKeyDefinitionClassInfo.clazz, "width", "I"); 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"); GET_FIELD_ID(gInputWindowClassInfo.inputChannel, gInputWindowClassInfo.clazz, "inputChannel", "Landroid/view/InputChannel;"); GET_FIELD_ID(gInputWindowClassInfo.layoutParamsFlags, gInputWindowClassInfo.clazz, "layoutParamsFlags", "I"); GET_FIELD_ID(gInputWindowClassInfo.layoutParamsType, gInputWindowClassInfo.clazz, "layoutParamsType", "I"); GET_FIELD_ID(gInputWindowClassInfo.dispatchingTimeoutNanos, gInputWindowClassInfo.clazz, "dispatchingTimeoutNanos", "J"); GET_FIELD_ID(gInputWindowClassInfo.frameLeft, gInputWindowClassInfo.clazz, "frameLeft", "I"); GET_FIELD_ID(gInputWindowClassInfo.frameTop, gInputWindowClassInfo.clazz, "frameTop", "I"); GET_FIELD_ID(gInputWindowClassInfo.touchableAreaLeft, gInputWindowClassInfo.clazz, "touchableAreaLeft", "I"); GET_FIELD_ID(gInputWindowClassInfo.touchableAreaTop, gInputWindowClassInfo.clazz, "touchableAreaTop", "I"); GET_FIELD_ID(gInputWindowClassInfo.touchableAreaRight, gInputWindowClassInfo.clazz, "touchableAreaRight", "I"); GET_FIELD_ID(gInputWindowClassInfo.touchableAreaBottom, gInputWindowClassInfo.clazz, "touchableAreaBottom", "I"); GET_FIELD_ID(gInputWindowClassInfo.visible, gInputWindowClassInfo.clazz, "visible", "Z"); GET_FIELD_ID(gInputWindowClassInfo.hasFocus, gInputWindowClassInfo.clazz, "hasFocus", "Z"); GET_FIELD_ID(gInputWindowClassInfo.hasWallpaper, gInputWindowClassInfo.clazz, "hasWallpaper", "Z"); GET_FIELD_ID(gInputWindowClassInfo.paused, gInputWindowClassInfo.clazz, "paused", "Z"); GET_FIELD_ID(gInputWindowClassInfo.ownerPid, gInputWindowClassInfo.clazz, "ownerPid", "I"); GET_FIELD_ID(gInputWindowClassInfo.ownerUid, gInputWindowClassInfo.clazz, "ownerUid", "I"); // InputApplication FIND_CLASS(gInputApplicationClassInfo.clazz, "com/android/server/InputApplication"); GET_FIELD_ID(gInputApplicationClassInfo.name, gInputApplicationClassInfo.clazz, "name", "Ljava/lang/String;"); GET_FIELD_ID(gInputApplicationClassInfo.dispatchingTimeoutNanos, gInputApplicationClassInfo.clazz, "dispatchingTimeoutNanos", "J"); GET_FIELD_ID(gInputApplicationClassInfo.token, gInputApplicationClassInfo.clazz, "token", "Ljava/lang/Object;"); // KeyEvent FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent"); // 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; } } /* namespace android */