Loading include/ui/InputDispatcher.h +19 −0 Original line number Diff line number Diff line Loading @@ -159,6 +159,12 @@ public: virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) = 0; /* Gets the maximum suggested event delivery rate per second. * This value is used to throttle motion event movement actions on a per-device * basis. It is not intended to be a hard limit. */ virtual int32_t getMaxEventsPerSecond() = 0; }; Loading Loading @@ -332,6 +338,8 @@ private: // Linked list of motion samples associated with this motion event. MotionSample firstSample; MotionSample* lastSample; uint32_t countSamples() const; }; // Tracks the progress of dispatching a particular event to a particular connection. Loading Loading @@ -587,6 +595,17 @@ private: Condition mInjectionSyncFinishedCondition; void decrementPendingSyncDispatchesLocked(EventEntry* entry); // Throttling state. struct ThrottleState { nsecs_t minTimeBetweenEvents; nsecs_t lastEventTime; int32_t lastDeviceId; uint32_t lastSource; uint32_t originalSampleCount; // only collected during debugging } mThrottleState; // Key repeat tracking. // XXX Move this up to the input reader instead. struct KeyRepeatState { Loading libs/ui/InputDispatcher.cpp +77 −5 Original line number Diff line number Diff line Loading @@ -28,6 +28,9 @@ // Log debug messages about input event injection. #define DEBUG_INJECTION 0 // Log debug messages about input event throttling. #define DEBUG_THROTTLING 0 #include <cutils/log.h> #include <ui/InputDispatcher.h> Loading Loading @@ -66,6 +69,15 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mKeyRepeatState.lastKeyEntry = NULL; int32_t maxEventsPerSecond = policy->getMaxEventsPerSecond(); mThrottleState.minTimeBetweenEvents = 1000000000LL / maxEventsPerSecond; mThrottleState.lastDeviceId = -1; #if DEBUG_THROTTLING mThrottleState.originalSampleCount = 0; LOGD("Throttling - Max events per second = %d", maxEventsPerSecond); #endif mCurrentInputTargetsValid = false; } Loading Loading @@ -144,12 +156,60 @@ void InputDispatcher::dispatchOnce() { } } else { // Inbound queue has at least one entry. // Start processing it but leave it on the queue until later so that the EventEntry* entry = mInboundQueue.head.next; // Consider throttling the entry if it is a move event and there are no // other events behind it in the queue. Due to movement batching, additional // samples may be appended to this event by the time the throttling timeout // expires. // TODO Make this smarter and consider throttling per device independently. if (entry->type == EventEntry::TYPE_MOTION) { MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); int32_t deviceId = motionEntry->deviceId; uint32_t source = motionEntry->source; if (motionEntry->next == & mInboundQueue.tail && motionEntry->action == AMOTION_EVENT_ACTION_MOVE && deviceId == mThrottleState.lastDeviceId && source == mThrottleState.lastSource) { nsecs_t nextTime = mThrottleState.lastEventTime + mThrottleState.minTimeBetweenEvents; if (currentTime < nextTime) { // Throttle it! #if DEBUG_THROTTLING LOGD("Throttling - Delaying motion event for " "device 0x%x, source 0x%08x by up to %0.3fms.", deviceId, source, (nextTime - currentTime) * 0.000001); #endif if (nextTime < nextWakeupTime) { nextWakeupTime = nextTime; } if (mThrottleState.originalSampleCount == 0) { mThrottleState.originalSampleCount = motionEntry->countSamples(); } goto Throttle; } } #if DEBUG_THROTTLING if (mThrottleState.originalSampleCount != 0) { uint32_t count = motionEntry->countSamples(); LOGD("Throttling - Motion event sample count grew by %d from %d to %d.", count - mThrottleState.originalSampleCount, mThrottleState.originalSampleCount, count); mThrottleState.originalSampleCount = 0; } #endif mThrottleState.lastEventTime = currentTime; mThrottleState.lastDeviceId = deviceId; mThrottleState.lastSource = source; } // Start processing the entry but leave it on the queue until later so that the // input reader can keep appending samples onto a motion event between the // time we started processing it and the time we finally enqueue dispatch // entries for it. EventEntry* entry = mInboundQueue.head.next; switch (entry->type) { case EventEntry::TYPE_CONFIGURATION_CHANGED: { ConfigurationChangedEntry* typedEntry = Loading Loading @@ -179,6 +239,8 @@ void InputDispatcher::dispatchOnce() { mInboundQueue.dequeue(entry); mAllocator.releaseEventEntry(entry); skipPoll = true; Throttle: ; } } Loading @@ -192,8 +254,8 @@ void InputDispatcher::dispatchOnce() { return; } // Wait for callback or timeout or wake. nsecs_t timeout = nanoseconds_to_milliseconds(nextWakeupTime - currentTime); // Wait for callback or timeout or wake. (make sure we round up, not down) nsecs_t timeout = (nextWakeupTime - currentTime + 999999LL) / 1000000LL; int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0; mPollLoop->pollOnce(timeoutMillis); } Loading Loading @@ -1708,6 +1770,16 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, motionEntry->lastSample = sample; } // --- InputDispatcher::MotionEntry --- uint32_t InputDispatcher::MotionEntry::countSamples() const { uint32_t count = 1; for (MotionSample* sample = firstSample.next; sample != NULL; sample = sample->next) { count += 1; } return count; } // --- InputDispatcher::Connection --- InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) : Loading services/java/com/android/server/InputManager.java +14 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.content.res.Configuration; import android.os.Environment; import android.os.LocalPowerManager; import android.os.PowerManager; import android.os.SystemProperties; import android.util.Slog; import android.util.Xml; import android.view.InputChannel; Loading Loading @@ -507,5 +508,18 @@ public class InputManager { return names.toArray(new String[names.size()]); } @SuppressWarnings("unused") public int getMaxEventsPerSecond() { int result = 0; try { result = Integer.parseInt(SystemProperties.get("windowsmgr.max_events_per_sec")); } catch (NumberFormatException e) { } if (result < 1) { result = 35; } return result; } } } services/jni/com_android_server_InputManager.cpp +24 −0 Original line number Diff line number Diff line Loading @@ -139,6 +139,7 @@ static struct { jmethodID filterJumpyTouchEvents; jmethodID getVirtualKeyDefinitions; jmethodID getExcludedDeviceNames; jmethodID getMaxEventsPerSecond; } gCallbacksClassInfo; static struct { Loading Loading @@ -249,6 +250,7 @@ public: int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); virtual int32_t getMaxEventsPerSecond(); private: struct InputWindow { Loading Loading @@ -310,6 +312,9 @@ private: int32_t mFilterTouchEvents; int32_t mFilterJumpyTouchEvents; // Cached throttling policy. int32_t mMaxEventsPerSecond; // Cached display state. (lock mDisplayLock) Mutex mDisplayLock; int32_t mDisplayWidth, mDisplayHeight; Loading Loading @@ -400,6 +405,7 @@ private: 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), Loading Loading @@ -921,6 +927,21 @@ nsecs_t NativeInputManager::getKeyRepeatTimeout() { } } int32_t NativeInputManager::getMaxEventsPerSecond() { if (mMaxEventsPerSecond < 0) { JNIEnv* env = jniEnv(); jint result = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getMaxEventsPerSecond); if (checkAndClearExceptionFromCallback(env, "getMaxEventsPerSecond")) { result = 35; } mMaxEventsPerSecond = result; } return mMaxEventsPerSecond; } void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) { #if DEBUG_FOCUS LOGD("setInputWindows"); Loading Loading @@ -2293,6 +2314,9 @@ int register_android_server_InputManager(JNIEnv* env) { 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, Loading Loading
include/ui/InputDispatcher.h +19 −0 Original line number Diff line number Diff line Loading @@ -159,6 +159,12 @@ public: virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets) = 0; /* Gets the maximum suggested event delivery rate per second. * This value is used to throttle motion event movement actions on a per-device * basis. It is not intended to be a hard limit. */ virtual int32_t getMaxEventsPerSecond() = 0; }; Loading Loading @@ -332,6 +338,8 @@ private: // Linked list of motion samples associated with this motion event. MotionSample firstSample; MotionSample* lastSample; uint32_t countSamples() const; }; // Tracks the progress of dispatching a particular event to a particular connection. Loading Loading @@ -587,6 +595,17 @@ private: Condition mInjectionSyncFinishedCondition; void decrementPendingSyncDispatchesLocked(EventEntry* entry); // Throttling state. struct ThrottleState { nsecs_t minTimeBetweenEvents; nsecs_t lastEventTime; int32_t lastDeviceId; uint32_t lastSource; uint32_t originalSampleCount; // only collected during debugging } mThrottleState; // Key repeat tracking. // XXX Move this up to the input reader instead. struct KeyRepeatState { Loading
libs/ui/InputDispatcher.cpp +77 −5 Original line number Diff line number Diff line Loading @@ -28,6 +28,9 @@ // Log debug messages about input event injection. #define DEBUG_INJECTION 0 // Log debug messages about input event throttling. #define DEBUG_THROTTLING 0 #include <cutils/log.h> #include <ui/InputDispatcher.h> Loading Loading @@ -66,6 +69,15 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mKeyRepeatState.lastKeyEntry = NULL; int32_t maxEventsPerSecond = policy->getMaxEventsPerSecond(); mThrottleState.minTimeBetweenEvents = 1000000000LL / maxEventsPerSecond; mThrottleState.lastDeviceId = -1; #if DEBUG_THROTTLING mThrottleState.originalSampleCount = 0; LOGD("Throttling - Max events per second = %d", maxEventsPerSecond); #endif mCurrentInputTargetsValid = false; } Loading Loading @@ -144,12 +156,60 @@ void InputDispatcher::dispatchOnce() { } } else { // Inbound queue has at least one entry. // Start processing it but leave it on the queue until later so that the EventEntry* entry = mInboundQueue.head.next; // Consider throttling the entry if it is a move event and there are no // other events behind it in the queue. Due to movement batching, additional // samples may be appended to this event by the time the throttling timeout // expires. // TODO Make this smarter and consider throttling per device independently. if (entry->type == EventEntry::TYPE_MOTION) { MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); int32_t deviceId = motionEntry->deviceId; uint32_t source = motionEntry->source; if (motionEntry->next == & mInboundQueue.tail && motionEntry->action == AMOTION_EVENT_ACTION_MOVE && deviceId == mThrottleState.lastDeviceId && source == mThrottleState.lastSource) { nsecs_t nextTime = mThrottleState.lastEventTime + mThrottleState.minTimeBetweenEvents; if (currentTime < nextTime) { // Throttle it! #if DEBUG_THROTTLING LOGD("Throttling - Delaying motion event for " "device 0x%x, source 0x%08x by up to %0.3fms.", deviceId, source, (nextTime - currentTime) * 0.000001); #endif if (nextTime < nextWakeupTime) { nextWakeupTime = nextTime; } if (mThrottleState.originalSampleCount == 0) { mThrottleState.originalSampleCount = motionEntry->countSamples(); } goto Throttle; } } #if DEBUG_THROTTLING if (mThrottleState.originalSampleCount != 0) { uint32_t count = motionEntry->countSamples(); LOGD("Throttling - Motion event sample count grew by %d from %d to %d.", count - mThrottleState.originalSampleCount, mThrottleState.originalSampleCount, count); mThrottleState.originalSampleCount = 0; } #endif mThrottleState.lastEventTime = currentTime; mThrottleState.lastDeviceId = deviceId; mThrottleState.lastSource = source; } // Start processing the entry but leave it on the queue until later so that the // input reader can keep appending samples onto a motion event between the // time we started processing it and the time we finally enqueue dispatch // entries for it. EventEntry* entry = mInboundQueue.head.next; switch (entry->type) { case EventEntry::TYPE_CONFIGURATION_CHANGED: { ConfigurationChangedEntry* typedEntry = Loading Loading @@ -179,6 +239,8 @@ void InputDispatcher::dispatchOnce() { mInboundQueue.dequeue(entry); mAllocator.releaseEventEntry(entry); skipPoll = true; Throttle: ; } } Loading @@ -192,8 +254,8 @@ void InputDispatcher::dispatchOnce() { return; } // Wait for callback or timeout or wake. nsecs_t timeout = nanoseconds_to_milliseconds(nextWakeupTime - currentTime); // Wait for callback or timeout or wake. (make sure we round up, not down) nsecs_t timeout = (nextWakeupTime - currentTime + 999999LL) / 1000000LL; int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0; mPollLoop->pollOnce(timeoutMillis); } Loading Loading @@ -1708,6 +1770,16 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, motionEntry->lastSample = sample; } // --- InputDispatcher::MotionEntry --- uint32_t InputDispatcher::MotionEntry::countSamples() const { uint32_t count = 1; for (MotionSample* sample = firstSample.next; sample != NULL; sample = sample->next) { count += 1; } return count; } // --- InputDispatcher::Connection --- InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) : Loading
services/java/com/android/server/InputManager.java +14 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.content.res.Configuration; import android.os.Environment; import android.os.LocalPowerManager; import android.os.PowerManager; import android.os.SystemProperties; import android.util.Slog; import android.util.Xml; import android.view.InputChannel; Loading Loading @@ -507,5 +508,18 @@ public class InputManager { return names.toArray(new String[names.size()]); } @SuppressWarnings("unused") public int getMaxEventsPerSecond() { int result = 0; try { result = Integer.parseInt(SystemProperties.get("windowsmgr.max_events_per_sec")); } catch (NumberFormatException e) { } if (result < 1) { result = 35; } return result; } } }
services/jni/com_android_server_InputManager.cpp +24 −0 Original line number Diff line number Diff line Loading @@ -139,6 +139,7 @@ static struct { jmethodID filterJumpyTouchEvents; jmethodID getVirtualKeyDefinitions; jmethodID getExcludedDeviceNames; jmethodID getMaxEventsPerSecond; } gCallbacksClassInfo; static struct { Loading Loading @@ -249,6 +250,7 @@ public: int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); virtual int32_t getMaxEventsPerSecond(); private: struct InputWindow { Loading Loading @@ -310,6 +312,9 @@ private: int32_t mFilterTouchEvents; int32_t mFilterJumpyTouchEvents; // Cached throttling policy. int32_t mMaxEventsPerSecond; // Cached display state. (lock mDisplayLock) Mutex mDisplayLock; int32_t mDisplayWidth, mDisplayHeight; Loading Loading @@ -400,6 +405,7 @@ private: 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), Loading Loading @@ -921,6 +927,21 @@ nsecs_t NativeInputManager::getKeyRepeatTimeout() { } } int32_t NativeInputManager::getMaxEventsPerSecond() { if (mMaxEventsPerSecond < 0) { JNIEnv* env = jniEnv(); jint result = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getMaxEventsPerSecond); if (checkAndClearExceptionFromCallback(env, "getMaxEventsPerSecond")) { result = 35; } mMaxEventsPerSecond = result; } return mMaxEventsPerSecond; } void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) { #if DEBUG_FOCUS LOGD("setInputWindows"); Loading Loading @@ -2293,6 +2314,9 @@ int register_android_server_InputManager(JNIEnv* env) { 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, Loading