diff options
author | Jeff Brown <jeffbrown@google.com> | 2010-06-22 01:27:15 -0700 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2010-06-28 19:10:54 -0700 |
commit | 349703effce5acc53ed96f7ed8556131f0c65e18 (patch) | |
tree | 359217d5076e3005c724b2117a59ffec81e7a83b /services/jni | |
parent | f2b544f5ae7676f7ab4cdf3379b2ed3c60a65def (diff) | |
download | frameworks_base-349703effce5acc53ed96f7ed8556131f0c65e18.zip frameworks_base-349703effce5acc53ed96f7ed8556131f0c65e18.tar.gz frameworks_base-349703effce5acc53ed96f7ed8556131f0c65e18.tar.bz2 |
Native input event dispatching.
Target identification is now fully native.
Fixed a couple of minor issues related to input injection.
Native input enabled by default, can be disabled by setting
WindowManagerPolicy.ENABLE_NATIVE_INPUT_DISPATCH to false.
Change-Id: I7edf66ed3e987cc9306ad4743ac57a116af452ff
Diffstat (limited to 'services/jni')
-rw-r--r-- | services/jni/com_android_server_InputManager.cpp | 1368 |
1 files changed, 1264 insertions, 104 deletions
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 1a6119a..0106e6c 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -19,14 +19,17 @@ //#define LOG_NDEBUG 0 // Log debug messages about InputReaderPolicy -#define DEBUG_INPUT_READER_POLICY 1 +#define DEBUG_INPUT_READER_POLICY 0 // Log debug messages about InputDispatcherPolicy -#define DEBUG_INPUT_DISPATCHER_POLICY 1 +#define DEBUG_INPUT_DISPATCHER_POLICY 0 +// Log debug messages about input focus tracking +#define DEBUG_FOCUS 0 #include "JNIHelp.h" #include "jni.h" +#include <limits.h> #include <android_runtime/AndroidRuntime.h> #include <ui/InputReader.h> #include <ui/InputDispatcher.h> @@ -37,10 +40,94 @@ #include "../../core/jni/android_view_KeyEvent.h" #include "../../core/jni/android_view_MotionEvent.h" #include "../../core/jni/android_view_InputChannel.h" -#include "../../core/jni/android_view_InputTarget.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, +}; + +enum { + POWER_MANAGER_OTHER_EVENT = 0, + POWER_MANAGER_CHEEK_EVENT = 1, + POWER_MANAGER_TOUCH_EVENT = 2, // touch events are TOUCH for 300ms, and then either + // up events or LONG_TOUCH events. + POWER_MANAGER_LONG_TOUCH_EVENT = 3, + POWER_MANAGER_TOUCH_UP_EVENT = 4, + POWER_MANAGER_BUTTON_EVENT = 5, // Button and trackball events. +}; + +// 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 { @@ -53,17 +140,18 @@ static struct { jmethodID notifyInputChannelBroken; jmethodID notifyInputChannelANR; jmethodID notifyInputChannelRecoveredFromANR; + jmethodID notifyANR; jmethodID virtualKeyFeedback; - jmethodID hackInterceptKey; + jmethodID interceptKeyBeforeQueueing; + jmethodID interceptKeyBeforeDispatching; + jmethodID checkInjectEventsPermission; jmethodID goToSleep; - jmethodID pokeUserActivityForKey; + jmethodID pokeUserActivity; jmethodID notifyAppSwitchComing; jmethodID filterTouchEvents; jmethodID filterJumpyTouchEvents; jmethodID getVirtualKeyDefinitions; jmethodID getExcludedDeviceNames; - jmethodID getKeyEventTargets; - jmethodID getMotionEventTargets; } gCallbacksClassInfo; static struct { @@ -79,9 +167,37 @@ static struct { static struct { jclass clazz; - jmethodID ctor; - jfieldID mArray; -} gInputTargetListClassInfo; + 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 inline nsecs_t now() { + return systemTime(SYSTEM_TIME_MONOTONIC); +} // ---------------------------------------------------------------------------- @@ -103,6 +219,11 @@ public: jweak inputChannelObjWeak); status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& 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, @@ -130,16 +251,66 @@ public: nsecs_t& outNewTimeout); virtual void notifyInputChannelRecoveredFromANR(const sp<InputChannel>& inputChannel); virtual nsecs_t getKeyRepeatTimeout(); - virtual int32_t getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, + virtual int32_t waitForKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); - virtual int32_t getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); private: + struct InputWindow { + sp<InputChannel> 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<InputManager> mInputManager; jobject mCallbacksObj; - jobject mReusableInputTargetListObj; // Cached filtering policies. int32_t mFilterTouchEvents; @@ -160,30 +331,78 @@ private: jobject getInputChannelObjLocal(JNIEnv* env, const sp<InputChannel>& inputChannel); + // Input target and focus tracking. (lock mDispatchLock) + Mutex mDispatchLock; + Condition mDispatchStateChanged; + + bool mDispatchEnabled; + bool mDispatchFrozen; + bool mWindowsReady; + Vector<InputWindow> mWindows; + Vector<InputWindow*> mWallpaperWindows; + + // Focus tracking for keys, trackball, etc. + InputWindow* mFocusedWindow; + + // Focus tracking for touch. + bool mTouchDown; + InputWindow* mTouchedWindow; // primary target for current down + Vector<InputWindow*> mTouchedWallpaperWindows; // wallpaper targets + + Vector<InputWindow*> mTempTouchedOutsideWindows; // temporary outside touch targets + Vector<sp<InputChannel> > mTempTouchedWallpaperChannels; // temporary wallpaper targets + + // Focused application. + InputApplication* mFocusedApplication; + InputApplication mFocusedApplicationStorage; // preallocated storage for mFocusedApplication + + void dumpDispatchStateLd(); + + bool notifyANR(jobject tokenObj, nsecs_t& outNewTimeout); + void releaseFocusedApplicationLd(JNIEnv* env); + + int32_t waitForFocusedWindowLd(uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, + Vector<InputTarget>& outTargets, InputWindow*& outFocusedWindow); + int32_t waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, + Vector<InputTarget>& outTargets, InputWindow*& outTouchedWindow); + + void releaseTouchedWindowLd(); + + int32_t identifyTrackballEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); + int32_t identifyTouchEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); + + 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<InputTarget>& outTargets); + static inline JNIEnv* jniEnv() { return AndroidRuntime::getJNIEnv(); } static bool isAppSwitchKey(int32_t keyCode); static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); - static void populateInputTargets(JNIEnv* env, jobject inputTargetListObj, - Vector<InputTarget>& outTargets); }; // ---------------------------------------------------------------------------- NativeInputManager::NativeInputManager(jobject callbacksObj) : mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), - mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1) { + mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(-1), + mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true), + mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL), + mFocusedApplication(NULL) { JNIEnv* env = jniEnv(); mCallbacksObj = env->NewGlobalRef(callbacksObj); - jobject inputTargetListObj = env->NewObject(gInputTargetListClassInfo.clazz, - gInputTargetListClassInfo.ctor); - mReusableInputTargetListObj = env->NewGlobalRef(inputTargetListObj); - env->DeleteLocalRef(inputTargetListObj); - sp<EventHub> eventHub = new EventHub(); mInputManager = new InputManager(eventHub, this, this); } @@ -192,7 +411,8 @@ NativeInputManager::~NativeInputManager() { JNIEnv* env = jniEnv(); env->DeleteGlobalRef(mCallbacksObj); - env->DeleteGlobalRef(mReusableInputTargetListObj); + + releaseFocusedApplicationLd(env); } bool NativeInputManager::isAppSwitchKey(int32_t keyCode) { @@ -362,7 +582,7 @@ 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", + "policyFlags=0x%x", when, deviceId, down, keyCode, scanCode, policyFlags); #endif @@ -375,9 +595,10 @@ int32_t NativeInputManager::interceptKey(nsecs_t when, bool isScreenOn = this->isScreenOn(); bool isScreenBright = this->isScreenBright(); - jint wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.hackInterceptKey, + jint wmActions = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.interceptKeyBeforeQueueing, deviceId, EV_KEY, scanCode, keyCode, policyFlags, down ? 1 : 0, when, isScreenOn); - if (checkAndClearExceptionFromCallback(env, "hackInterceptKey")) { + if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) { wmActions = 0; } @@ -398,8 +619,7 @@ int32_t NativeInputManager::interceptKey(nsecs_t when, } if (wmActions & WM_ACTION_POKE_USER_ACTIVITY) { - env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivityForKey, when); - checkAndClearExceptionFromCallback(env, "pokeUserActivityForKey"); + pokeUserActivity(when, POWER_MANAGER_BUTTON_EVENT); } if (wmActions & WM_ACTION_PASS_TO_USER) { @@ -412,6 +632,9 @@ int32_t NativeInputManager::interceptKey(nsecs_t when, actions |= InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING; } } + + // TODO Be smarter about which keys cause us to request interception during dispatch. + actions |= InputReaderPolicyInterface::ACTION_INTERCEPT_DISPATCH; return actions; } @@ -423,12 +646,13 @@ int32_t NativeInputManager::interceptTouch(nsecs_t when) { int32_t actions = InputReaderPolicyInterface::ACTION_NONE; if (isScreenOn()) { // Only dispatch touch events when the device is awake. + // Do not wake the device. actions |= InputReaderPolicyInterface::ACTION_DISPATCH; - } - if (! isScreenBright()) { - // Brighten the screen if dimmed. - actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE; + if (! isScreenBright()) { + // Brighten the screen if dimmed. + actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE; + } } return actions; @@ -444,12 +668,13 @@ int32_t NativeInputManager::interceptTrackball(nsecs_t when, int32_t actions = InputReaderPolicyInterface::ACTION_NONE; if (isScreenOn()) { // Only dispatch trackball events when the device is awake. + // Do not wake the device. actions |= InputReaderPolicyInterface::ACTION_DISPATCH; - } - if (! isScreenBright()) { - // Brighten the screen if dimmed. - actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE; + if (! isScreenBright()) { + // Brighten the screen if dimmed. + actions |= InputReaderPolicyInterface::ACTION_BRIGHT_HERE; + } } return actions; @@ -639,6 +864,27 @@ void NativeInputManager::notifyInputChannelRecoveredFromANR(const sp<InputChanne } } +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. @@ -649,85 +895,898 @@ nsecs_t NativeInputManager::getKeyRepeatTimeout() { } } -int32_t NativeInputManager::getKeyEventTargets(KeyEvent* keyEvent, uint32_t policyFlags, - int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) { -#if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("getKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", - policyFlags, injectorPid, injectorUid); +void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) { +#if DEBUG_FOCUS + LOGD("setInputWindows"); #endif + { // acquire lock + AutoMutex _l(mDispatchLock); - JNIEnv* env = jniEnv(); + sp<InputChannel> 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(); + } + + 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(); + + mDispatchStateChanged.broadcast(); + +#if DEBUG_FOCUS + dumpDispatchStateLd(); +#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> 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."); + } - jint injectionResult; - jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent); - if (! keyEventObj) { - LOGE("Could not obtain DVM KeyEvent object to get key event targets."); - injectionResult = INPUT_EVENT_INJECTION_FAILED; + env->DeleteLocalRef(inputChannelObj); } else { - jint injectionResult = env->CallIntMethod(mCallbacksObj, - gCallbacksClassInfo.getKeyEventTargets, mReusableInputTargetListObj, - keyEventObj, jint(keyEvent->getNature()), jint(policyFlags), - jint(injectorPid), jint(injectorUid)); - if (checkAndClearExceptionFromCallback(env, "getKeyEventTargets")) { + 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 + dumpDispatchStateLd(); +#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 + dumpDispatchStateLd(); +#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<InputTarget>& 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); + dumpDispatchStateLd(); +#endif + return injectionResult; +} + +int32_t NativeInputManager::waitForTouchedWindowLd(MotionEvent* motionEvent, uint32_t policyFlags, + int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets, + InputWindow*& outTouchedWindow) { + nsecs_t startTime = now(); + + int32_t injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; + int32_t action = motionEvent->getAction(); + + // 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; + 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; // 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 == MOTION_EVENT_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; + break; // failed, exit wait loop + } + + // Check permissions. + if (! checkInjectionPermission(newTouchedWindow, injectorPid, injectorUid)) { + injectionResult = INPUT_EVENT_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); + } + break; // done } else { - populateInputTargets(env, mReusableInputTargetListObj, outTargets); + // Check permissions. + if (! checkInjectionPermission(mTouchedWindow, injectorPid, injectorUid)) { + injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; + break; // failed, exit wait loop + } + + // If there is no currently touched window then fail. + if (! mTouchedWindow || ! mTouchDown) { + LOGI("Dropping event because touched window is no longer valid."); + injectionResult = INPUT_EVENT_INJECTION_FAILED; + 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! + break; // done } - env->DeleteLocalRef(keyEventObj); } + + // Output targets. + bool havePermission; + if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) { + // Injection succeeded so the injector must have permission. + havePermission = true; + + 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 { + if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED + && checkInjectionPermission(NULL, injectorPid, injectorUid)) { + // Injection failed but the injector does have permission to inject events. + // While we might not have found a valid target for the injected event, we + // still want to update the dispatch state to take it into account. + havePermission = true; + } else { + // Injector does not have permission to inject events. + // We make sure to leave the dispatch state unchanged. + havePermission = false; + } + outTouchedWindow = NULL; + } + mTempTouchedOutsideWindows.clear(); + + // Update final pieces of touch state now that we know for sure whether the injector + // had permission to perform the injection. + if (havePermission) { + if (action == MOTION_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 == MOTION_EVENT_ACTION_UP) { + mTouchDown = false; + releaseTouchedWindowLd(); + } + } + +#if DEBUG_FOCUS + LOGD("waitForTouchedWindow finished: injectionResult=%d", + injectionResult); + dumpDispatchStateLd(); +#endif return injectionResult; } -int32_t NativeInputManager::getMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, +void NativeInputManager::releaseTouchedWindowLd() { + mTouchedWindow = NULL; + mTouchedWallpaperWindows.clear(); +} + +void NativeInputManager::addTarget(const InputWindow* window, int32_t targetFlags, + nsecs_t timeSpentWaitingForApplication, Vector<InputTarget>& 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<InputTarget>& outTargets) { #if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("getMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", + LOGD("waitForKeyEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", policyFlags, injectorPid, injectorUid); #endif - JNIEnv* env = jniEnv(); + int32_t windowType; + { // acquire lock + AutoMutex _l(mDispatchLock); - jint injectionResult; - jobject motionEventObj = android_view_MotionEvent_fromNative(env, motionEvent); - if (! motionEventObj) { - LOGE("Could not obtain DVM MotionEvent object to get key event targets."); - injectionResult = INPUT_EVENT_INJECTION_FAILED; - } else { - jint injectionResult = env->CallIntMethod(mCallbacksObj, - gCallbacksClassInfo.getMotionEventTargets, mReusableInputTargetListObj, - motionEventObj, jint(motionEvent->getNature()), jint(policyFlags), - jint(injectorPid), jint(injectorUid)); - if (checkAndClearExceptionFromCallback(env, "getMotionEventTargets")) { - injectionResult = INPUT_EVENT_INJECTION_FAILED; + 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 (policyFlags & POLICY_FLAG_INTERCEPT_DISPATCH) { + const InputTarget& target = outTargets.top(); + + JNIEnv* env = jniEnv(); + + jobject inputChannelObj = getInputChannelObjLocal(env, target.inputChannel); + if (inputChannelObj) { + jboolean consumed = env->CallBooleanMethod(mCallbacksObj, + gCallbacksClassInfo.interceptKeyBeforeDispatching, + inputChannelObj, keyEvent->getKeyCode(), keyEvent->getMetaState(), + keyEvent->getAction() == KEY_EVENT_ACTION_DOWN, + keyEvent->getRepeatCount(), policyFlags); + bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatch"); + + env->DeleteLocalRef(inputChannelObj); + + if (error) { + return INPUT_EVENT_INJECTION_FAILED; + } + + if (consumed) { + outTargets.clear(); + return INPUT_EVENT_INJECTION_SUCCEEDED; + } } else { - populateInputTargets(env, mReusableInputTargetListObj, outTargets); + LOGW("Could not apply key dispatch policy because input channel '%s' is " + "no longer valid.", target.inputChannel->getName().string()); } - android_view_MotionEvent_recycle(env, motionEventObj); - env->DeleteLocalRef(motionEventObj); } - return injectionResult; + + 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<InputTarget>& outTargets) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("waitForMotionEventTargets - policyFlags=%d, injectorPid=%d, injectorUid=%d", + policyFlags, injectorPid, injectorUid); +#endif + + switch (motionEvent->getNature()) { + case INPUT_EVENT_NATURE_TRACKBALL: + return identifyTrackballEventTargets(motionEvent, policyFlags, injectorPid, injectorUid, + outTargets); + + case INPUT_EVENT_NATURE_TOUCH: + return identifyTouchEventTargets(motionEvent, policyFlags, injectorPid, injectorUid, + outTargets); + + default: + assert(false); + return INPUT_EVENT_INJECTION_FAILED; + } +} + +int32_t NativeInputManager::identifyTrackballEventTargets(MotionEvent* motionEvent, + uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, + Vector<InputTarget>& outTargets) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("identifyTrackballEventTargets - 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 + + pokeUserActivityIfNeeded(windowType, POWER_MANAGER_BUTTON_EVENT); + return INPUT_EVENT_INJECTION_SUCCEEDED; } -void NativeInputManager::populateInputTargets(JNIEnv* env, jobject inputTargetListObj, +int32_t NativeInputManager::identifyTouchEventTargets(MotionEvent* motionEvent, + uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) { - jobjectArray inputTargetArray = jobjectArray(env->GetObjectField( - inputTargetListObj, gInputTargetListClassInfo.mArray)); - - jsize length = env->GetArrayLength(inputTargetArray); - for (jsize i = 0; i < length; i++) { - jobject item = env->GetObjectArrayElement(inputTargetArray, i); - if (! item) { - break; // found null element indicating end of used portion of the array +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("identifyTouchEventTargets - 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; + } // release lock + + int32_t eventType; + switch (motionEvent->getAction()) { + case MOTION_EVENT_ACTION_DOWN: + eventType = POWER_MANAGER_TOUCH_EVENT; + break; + case MOTION_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; +} + +void NativeInputManager::pokeUserActivityIfNeeded(int32_t windowType, int32_t eventType) { + if (windowType != TYPE_KEYGUARD) { + nsecs_t eventTime = now(); + pokeUserActivity(eventTime, eventType); + } +} - outTargets.add(); - android_view_InputTarget_toNative(env, item, & outTargets.editTop()); +void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) { + JNIEnv* env = jniEnv(); + env->CallVoidMethod(mCallbacksObj, gCallbacksClassInfo.pokeUserActivity, + eventTime, eventType); + checkAndClearExceptionFromCallback(env, "pokeUserActivity"); +} - env->DeleteLocalRef(item); +void NativeInputManager::dumpDispatchStateLd() { +#if DEBUG_FOCUS + LOGD(" dispatcherState: dispatchEnabled=%d, dispatchFrozen=%d, windowsReady=%d", + mDispatchEnabled, mDispatchFrozen, mWindowsReady); + if (mFocusedApplication) { + LOGD(" focusedApplication: name='%s', dispatchingTimeout=%0.3fms", + mFocusedApplication->name.string(), + mFocusedApplication->dispatchingTimeout / 1000000.0); + } else { + LOGD(" focusedApplication: <null>"); + } + LOGD(" focusedWindow: '%s'", + mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>"); + LOGD(" touchedWindow: '%s', touchDown=%d", + mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>", + mTouchDown); + for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) { + LOGD(" touchedWallpaperWindows[%d]: '%s'", + i, mTouchedWallpaperWindows[i]->inputChannel->getName().string()); } - env->DeleteLocalRef(inputTargetArray); + for (size_t i = 0; i < mWindows.size(); i++) { + LOGD(" 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", + 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); + } +#endif } +// ---------------------------------------------------------------------------- + +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<InputChannel> 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; +} // ---------------------------------------------------------------------------- @@ -926,6 +1985,42 @@ static jint android_server_InputManager_nativeInjectMotionEvent(JNIEnv* env, jcl injectorPid, injectorUid, sync, timeoutMillis); } +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 JNINativeMethod gInputManagerMethods[] = { @@ -953,7 +2048,15 @@ static JNINativeMethod gInputManagerMethods[] = { { "nativeInjectKeyEvent", "(Landroid/view/KeyEvent;IIIZI)I", (void*) android_server_InputManager_nativeInjectKeyEvent }, { "nativeInjectMotionEvent", "(Landroid/view/MotionEvent;IIIZI)I", - (void*) android_server_InputManager_nativeInjectMotionEvent } + (void*) android_server_InputManager_nativeInjectMotionEvent }, + { "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 } }; #define FIND_CLASS(var, className) \ @@ -999,17 +2102,26 @@ int register_android_server_InputManager(JNIEnv* env) { 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.virtualKeyFeedback, gCallbacksClassInfo.clazz, "virtualKeyFeedback", "(JIIIIIIJ)V"); - GET_METHOD_ID(gCallbacksClassInfo.hackInterceptKey, gCallbacksClassInfo.clazz, - "hackInterceptKey", "(IIIIIIJZ)I"); + GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz, + "interceptKeyBeforeQueueing", "(IIIIIIJZ)I"); + + GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz, + "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIZII)Z"); + + GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz, + "checkInjectEventsPermission", "(II)Z"); GET_METHOD_ID(gCallbacksClassInfo.goToSleep, gCallbacksClassInfo.clazz, "goToSleep", "(J)V"); - GET_METHOD_ID(gCallbacksClassInfo.pokeUserActivityForKey, gCallbacksClassInfo.clazz, - "pokeUserActivityForKey", "(J)V"); + GET_METHOD_ID(gCallbacksClassInfo.pokeUserActivity, gCallbacksClassInfo.clazz, + "pokeUserActivity", "(JI)V"); GET_METHOD_ID(gCallbacksClassInfo.notifyAppSwitchComing, gCallbacksClassInfo.clazz, "notifyAppSwitchComing", "()V"); @@ -1027,14 +2139,6 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz, "getExcludedDeviceNames", "()[Ljava/lang/String;"); - GET_METHOD_ID(gCallbacksClassInfo.getKeyEventTargets, gCallbacksClassInfo.clazz, - "getKeyEventTargets", - "(Lcom/android/server/InputTargetList;Landroid/view/KeyEvent;IIII)I"); - - GET_METHOD_ID(gCallbacksClassInfo.getMotionEventTargets, gCallbacksClassInfo.clazz, - "getMotionEventTargets", - "(Lcom/android/server/InputTargetList;Landroid/view/MotionEvent;IIII)I"); - // VirtualKeyDefinition FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz, @@ -1055,15 +2159,71 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gVirtualKeyDefinitionClassInfo.height, gVirtualKeyDefinitionClassInfo.clazz, "height", "I"); - // InputTargetList + // 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"); - FIND_CLASS(gInputTargetListClassInfo.clazz, "com/android/server/InputTargetList"); + GET_FIELD_ID(gInputApplicationClassInfo.name, gInputApplicationClassInfo.clazz, + "name", "Ljava/lang/String;"); - GET_METHOD_ID(gInputTargetListClassInfo.ctor, gInputTargetListClassInfo.clazz, - "<init>", "()V"); + GET_FIELD_ID(gInputApplicationClassInfo.dispatchingTimeoutNanos, + gInputApplicationClassInfo.clazz, + "dispatchingTimeoutNanos", "J"); - GET_FIELD_ID(gInputTargetListClassInfo.mArray, gInputTargetListClassInfo.clazz, - "mArray", "[Landroid/view/InputTarget;"); + GET_FIELD_ID(gInputApplicationClassInfo.token, gInputApplicationClassInfo.clazz, + "token", "Ljava/lang/Object;"); return 0; } |