am 99401b29: Merge "Add support for throttling motion events." into gingerbread

Merge commit '99401b29c3f39488a058d3eb616009b751879a77' into gingerbread-plus-aosp

* commit '99401b29c3f39488a058d3eb616009b751879a77':
  Add support for throttling motion events.
This commit is contained in:
Jeff Brown
2010-08-18 17:42:47 -07:00
committed by Android Git Automerger
4 changed files with 134 additions and 5 deletions

View File

@@ -159,6 +159,12 @@ public:
virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid, int32_t injectorPid, int32_t injectorUid,
Vector<InputTarget>& outTargets) = 0; 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;
}; };
@@ -332,6 +338,8 @@ private:
// Linked list of motion samples associated with this motion event. // Linked list of motion samples associated with this motion event.
MotionSample firstSample; MotionSample firstSample;
MotionSample* lastSample; MotionSample* lastSample;
uint32_t countSamples() const;
}; };
// Tracks the progress of dispatching a particular event to a particular connection. // Tracks the progress of dispatching a particular event to a particular connection.
@@ -587,6 +595,17 @@ private:
Condition mInjectionSyncFinishedCondition; Condition mInjectionSyncFinishedCondition;
void decrementPendingSyncDispatchesLocked(EventEntry* entry); 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. // Key repeat tracking.
// XXX Move this up to the input reader instead. // XXX Move this up to the input reader instead.
struct KeyRepeatState { struct KeyRepeatState {

View File

@@ -28,6 +28,9 @@
// Log debug messages about input event injection. // Log debug messages about input event injection.
#define DEBUG_INJECTION 0 #define DEBUG_INJECTION 0
// Log debug messages about input event throttling.
#define DEBUG_THROTTLING 0
#include <cutils/log.h> #include <cutils/log.h>
#include <ui/InputDispatcher.h> #include <ui/InputDispatcher.h>
@@ -66,6 +69,15 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic
mKeyRepeatState.lastKeyEntry = NULL; 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; mCurrentInputTargetsValid = false;
} }
@@ -144,12 +156,60 @@ void InputDispatcher::dispatchOnce() {
} }
} else { } else {
// Inbound queue has at least one entry. // 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 // input reader can keep appending samples onto a motion event between the
// time we started processing it and the time we finally enqueue dispatch // time we started processing it and the time we finally enqueue dispatch
// entries for it. // entries for it.
EventEntry* entry = mInboundQueue.head.next;
switch (entry->type) { switch (entry->type) {
case EventEntry::TYPE_CONFIGURATION_CHANGED: { case EventEntry::TYPE_CONFIGURATION_CHANGED: {
ConfigurationChangedEntry* typedEntry = ConfigurationChangedEntry* typedEntry =
@@ -179,6 +239,8 @@ void InputDispatcher::dispatchOnce() {
mInboundQueue.dequeue(entry); mInboundQueue.dequeue(entry);
mAllocator.releaseEventEntry(entry); mAllocator.releaseEventEntry(entry);
skipPoll = true; skipPoll = true;
Throttle: ;
} }
} }
@@ -192,8 +254,8 @@ void InputDispatcher::dispatchOnce() {
return; return;
} }
// Wait for callback or timeout or wake. // Wait for callback or timeout or wake. (make sure we round up, not down)
nsecs_t timeout = nanoseconds_to_milliseconds(nextWakeupTime - currentTime); nsecs_t timeout = (nextWakeupTime - currentTime + 999999LL) / 1000000LL;
int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0; int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0;
mPollLoop->pollOnce(timeoutMillis); mPollLoop->pollOnce(timeoutMillis);
} }
@@ -1708,6 +1770,16 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry,
motionEntry->lastSample = sample; 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 ---
InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) : InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :

View File

@@ -26,6 +26,7 @@ import android.content.res.Configuration;
import android.os.Environment; import android.os.Environment;
import android.os.LocalPowerManager; import android.os.LocalPowerManager;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.SystemProperties;
import android.util.Slog; import android.util.Slog;
import android.util.Xml; import android.util.Xml;
import android.view.InputChannel; import android.view.InputChannel;
@@ -507,5 +508,18 @@ public class InputManager {
return names.toArray(new String[names.size()]); 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;
}
} }
} }

View File

@@ -139,6 +139,7 @@ static struct {
jmethodID filterJumpyTouchEvents; jmethodID filterJumpyTouchEvents;
jmethodID getVirtualKeyDefinitions; jmethodID getVirtualKeyDefinitions;
jmethodID getExcludedDeviceNames; jmethodID getExcludedDeviceNames;
jmethodID getMaxEventsPerSecond;
} gCallbacksClassInfo; } gCallbacksClassInfo;
static struct { static struct {
@@ -249,6 +250,7 @@ public:
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags, virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets); int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
virtual int32_t getMaxEventsPerSecond();
private: private:
struct InputWindow { struct InputWindow {
@@ -310,6 +312,9 @@ private:
int32_t mFilterTouchEvents; int32_t mFilterTouchEvents;
int32_t mFilterJumpyTouchEvents; int32_t mFilterJumpyTouchEvents;
// Cached throttling policy.
int32_t mMaxEventsPerSecond;
// Cached display state. (lock mDisplayLock) // Cached display state. (lock mDisplayLock)
Mutex mDisplayLock; Mutex mDisplayLock;
int32_t mDisplayWidth, mDisplayHeight; int32_t mDisplayWidth, mDisplayHeight;
@@ -400,6 +405,7 @@ private:
NativeInputManager::NativeInputManager(jobject callbacksObj) : NativeInputManager::NativeInputManager(jobject callbacksObj) :
mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
mMaxEventsPerSecond(-1),
mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0), mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0),
mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true), mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true),
mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL), mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
@@ -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) { void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
#if DEBUG_FOCUS #if DEBUG_FOCUS
LOGD("setInputWindows"); LOGD("setInputWindows");
@@ -2293,6 +2314,9 @@ int register_android_server_InputManager(JNIEnv* env) {
GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz, GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz,
"getExcludedDeviceNames", "()[Ljava/lang/String;"); "getExcludedDeviceNames", "()[Ljava/lang/String;");
GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz,
"getMaxEventsPerSecond", "()I");
// VirtualKeyDefinition // VirtualKeyDefinition
FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz, FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz,