Merge "Add unit tests for native input and fix bugs identified." into gingerbread

This commit is contained in:
Jeff Brown 2010-10-24 14:18:19 -07:00 committed by Android (Google) Code Review
commit f35c873501
7 changed files with 3670 additions and 26 deletions

View File

@ -170,11 +170,10 @@ public:
* and parameters maintained by the input reader.
*/
class InputReaderContext {
protected:
public:
InputReaderContext() { }
virtual ~InputReaderContext() { }
public:
virtual void updateGlobalMetaState() = 0;
virtual int32_t getGlobalMetaState() = 0;
@ -193,7 +192,7 @@ public:
* the input reader, the input reader never calls into other components while holding
* an exclusive internal lock whenever re-entrance can happen.
*/
class InputReader : public InputReaderInterface, private InputReaderContext {
class InputReader : public InputReaderInterface, protected InputReaderContext {
public:
InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
@ -219,6 +218,11 @@ public:
virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask,
size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags);
protected:
// These methods are protected virtual so they can be overridden and instrumented
// by test cases.
virtual InputDevice* createDevice(int32_t deviceId, const String8& name, uint32_t classes);
private:
sp<EventHubInterface> mEventHub;
sp<InputReaderPolicyInterface> mPolicy;
@ -244,12 +248,11 @@ private:
void addDevice(int32_t deviceId);
void removeDevice(int32_t deviceId);
InputDevice* createDevice(int32_t deviceId, const String8& name, uint32_t classes);
void configureExcludedDevices();
void consumeEvent(const RawEvent* rawEvent);
void handleConfigurationChanged();
void handleConfigurationChanged(nsecs_t when);
// state management for all devices
Mutex mStateLock;
@ -533,6 +536,21 @@ protected:
int32_t toolMajor;
int32_t toolMinor;
int32_t orientation;
inline bool operator== (const PointerData& other) const {
return id == other.id
&& x == other.x
&& y == other.y
&& pressure == other.pressure
&& touchMajor == other.touchMajor
&& touchMinor == other.touchMinor
&& toolMajor == other.toolMajor
&& toolMinor == other.toolMinor
&& orientation == other.orientation;
}
inline bool operator!= (const PointerData& other) const {
return !(*this == other);
}
};
// Raw data for a collection of pointers including a pointer id mapping table.

View File

@ -370,6 +370,7 @@ bool EventHub::getEvent(RawEvent* outEvent)
outEvent->deviceId = device->id;
}
outEvent->type = DEVICE_REMOVED;
outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
delete device;
mNeedToSendFinishedDeviceScan = true;
return true;
@ -386,6 +387,7 @@ bool EventHub::getEvent(RawEvent* outEvent)
outEvent->deviceId = device->id;
}
outEvent->type = DEVICE_ADDED;
outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
mNeedToSendFinishedDeviceScan = true;
return true;
}
@ -393,6 +395,7 @@ bool EventHub::getEvent(RawEvent* outEvent)
if (mNeedToSendFinishedDeviceScan) {
mNeedToSendFinishedDeviceScan = false;
outEvent->type = FINISHED_DEVICE_SCAN;
outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC);
return true;
}

View File

@ -124,12 +124,19 @@ static bool validateMotionEvent(int32_t action, size_t pointerCount,
pointerCount, MAX_POINTERS);
return false;
}
BitSet32 pointerIdBits;
for (size_t i = 0; i < pointerCount; i++) {
if (pointerIds[i] < 0 || pointerIds[i] > MAX_POINTER_ID) {
int32_t id = pointerIds[i];
if (id < 0 || id > MAX_POINTER_ID) {
LOGE("Motion event has invalid pointer id %d; value must be between 0 and %d",
pointerIds[i], MAX_POINTER_ID);
id, MAX_POINTER_ID);
return false;
}
if (pointerIdBits.hasBit(id)) {
LOGE("Motion event has duplicate pointer id %d", id);
return false;
}
pointerIdBits.markBit(id);
}
return true;
}

View File

@ -234,7 +234,7 @@ void InputReader::process(const RawEvent* rawEvent) {
break;
case EventHubInterface::FINISHED_DEVICE_SCAN:
handleConfigurationChanged();
handleConfigurationChanged(rawEvent->when);
break;
default:
@ -372,7 +372,7 @@ void InputReader::consumeEvent(const RawEvent* rawEvent) {
} // release device registry reader lock
}
void InputReader::handleConfigurationChanged() {
void InputReader::handleConfigurationChanged(nsecs_t when) {
// Reset global meta state because it depends on the list of all configured devices.
updateGlobalMetaState();
@ -380,7 +380,6 @@ void InputReader::handleConfigurationChanged() {
updateInputConfiguration();
// Enqueue configuration changed.
nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
mDispatcher->notifyConfigurationChanged(when);
}
@ -2094,7 +2093,7 @@ TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches(
mLocked.currentVirtualKey.down = false;
#if DEBUG_VIRTUAL_KEYS
LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
mLocked.currentVirtualKey.keyCode, mLocked.currentVirtualKey.scanCode);
#endif
keyEventAction = AKEY_EVENT_ACTION_UP;
keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;
@ -2119,13 +2118,22 @@ TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches(
mLocked.currentVirtualKey.down = false;
#if DEBUG_VIRTUAL_KEYS
LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
mLocked.currentVirtualKey.keyCode, mLocked.currentVirtualKey.scanCode);
#endif
keyEventAction = AKEY_EVENT_ACTION_UP;
keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY
| AKEY_EVENT_FLAG_CANCELED;
touchResult = DROP_STROKE;
goto DispatchVirtualKey;
// Check whether the pointer moved inside the display area where we should
// start a new stroke.
int32_t x = mCurrentTouch.pointers[0].x;
int32_t y = mCurrentTouch.pointers[0].y;
if (isPointInsideSurfaceLocked(x, y)) {
mLastTouch.clear();
touchResult = DISPATCH_TOUCH;
} else {
touchResult = DROP_STROKE;
}
} else {
if (mCurrentTouch.pointerCount >= 1 && mLastTouch.pointerCount == 0) {
// Pointer just went down. Handle off-screen touches, if needed.
@ -2143,7 +2151,8 @@ TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches(
mLocked.currentVirtualKey.scanCode = virtualKey->scanCode;
#if DEBUG_VIRTUAL_KEYS
LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
mLocked.currentVirtualKey.keyCode,
mLocked.currentVirtualKey.scanCode);
#endif
keyEventAction = AKEY_EVENT_ACTION_DOWN;
keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM
@ -2190,14 +2199,35 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
dispatchTouch(when, policyFlags, & mCurrentTouch,
currentIdBits, -1, currentPointerCount, motionEventAction);
} else {
// There may be pointers going up and pointers going down at the same time when pointer
// ids are reported by the device driver.
// There may be pointers going up and pointers going down and pointers moving
// all at the same time.
BitSet32 upIdBits(lastIdBits.value & ~ currentIdBits.value);
BitSet32 downIdBits(currentIdBits.value & ~ lastIdBits.value);
BitSet32 activeIdBits(lastIdBits.value);
uint32_t pointerCount = lastPointerCount;
while (! upIdBits.isEmpty()) {
// Produce an intermediate representation of the touch data that consists of the
// old location of pointers that have just gone up and the new location of pointers that
// have just moved but omits the location of pointers that have just gone down.
TouchData interimTouch;
interimTouch.copyFrom(mLastTouch);
BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);
bool moveNeeded = false;
while (!moveIdBits.isEmpty()) {
uint32_t moveId = moveIdBits.firstMarkedBit();
moveIdBits.clearBit(moveId);
int32_t oldIndex = mLastTouch.idToIndex[moveId];
int32_t newIndex = mCurrentTouch.idToIndex[moveId];
if (mLastTouch.pointers[oldIndex] != mCurrentTouch.pointers[newIndex]) {
interimTouch.pointers[oldIndex] = mCurrentTouch.pointers[newIndex];
moveNeeded = true;
}
}
// Dispatch pointer up events using the interim pointer locations.
while (!upIdBits.isEmpty()) {
uint32_t upId = upIdBits.firstMarkedBit();
upIdBits.clearBit(upId);
BitSet32 oldActiveIdBits = activeIdBits;
@ -2210,12 +2240,21 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
motionEventAction = AMOTION_EVENT_ACTION_POINTER_UP;
}
dispatchTouch(when, policyFlags, & mLastTouch,
dispatchTouch(when, policyFlags, &interimTouch,
oldActiveIdBits, upId, pointerCount, motionEventAction);
pointerCount -= 1;
}
while (! downIdBits.isEmpty()) {
// Dispatch move events if any of the remaining pointers moved from their old locations.
// Although applications receive new locations as part of individual pointer up
// events, they do not generally handle them except when presented in a move event.
if (moveNeeded) {
dispatchTouch(when, policyFlags, &mCurrentTouch,
activeIdBits, -1, pointerCount, AMOTION_EVENT_ACTION_MOVE);
}
// Dispatch pointer down events using the new pointer locations.
while (!downIdBits.isEmpty()) {
uint32_t downId = downIdBits.firstMarkedBit();
downIdBits.clearBit(downId);
BitSet32 oldActiveIdBits = activeIdBits;
@ -2230,7 +2269,7 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {
}
pointerCount += 1;
dispatchTouch(when, policyFlags, & mCurrentTouch,
dispatchTouch(when, policyFlags, &mCurrentTouch,
activeIdBits, downId, pointerCount, motionEventAction);
}
}
@ -3339,8 +3378,8 @@ void MultiTouchInputMapper::sync(nsecs_t when) {
if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) {
if (inPointer.absMTPressure <= 0) {
// Some devices send sync packets with X / Y but with a 0 presure to indicate
// a pointer up. Drop this finger.
// Some devices send sync packets with X / Y but with a 0 pressure to indicate
// a pointer going up. Drop this finger.
continue;
}
outPointer.pressure = inPointer.absMTPressure;

View File

@ -7,6 +7,7 @@ ifneq ($(TARGET_SIMULATOR),true)
# Build the unit tests.
test_src_files := \
InputChannel_test.cpp \
InputReader_test.cpp \
InputDispatcher_test.cpp \
InputPublisherAndConsumer_test.cpp

View File

@ -4,15 +4,223 @@
#include <ui/InputDispatcher.h>
#include <gtest/gtest.h>
#include <linux/input.h>
namespace android {
class InputDispatcherTest : public testing::Test {
// An arbitrary time value.
static const nsecs_t ARBITRARY_TIME = 1234;
// An arbitrary device id.
static const int32_t DEVICE_ID = 1;
// An arbitrary injector pid / uid pair that has permission to inject events.
static const int32_t INJECTOR_PID = 999;
static const int32_t INJECTOR_UID = 1001;
// --- FakeInputDispatcherPolicy ---
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
protected:
virtual ~FakeInputDispatcherPolicy() {
}
public:
FakeInputDispatcherPolicy() {
}
private:
virtual void notifyConfigurationChanged(nsecs_t when) {
}
virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
const sp<InputChannel>& inputChannel) {
return 0;
}
virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) {
}
virtual nsecs_t getKeyRepeatTimeout() {
return 500 * 1000000LL;
}
virtual nsecs_t getKeyRepeatDelay() {
return 50 * 1000000LL;
}
virtual int32_t getMaxEventsPerSecond() {
return 60;
}
virtual void interceptKeyBeforeQueueing(nsecs_t when, int32_t deviceId,
int32_t action, int32_t& flags, int32_t keyCode, int32_t scanCode,
uint32_t& policyFlags) {
}
virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
}
virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel,
const KeyEvent* keyEvent, uint32_t policyFlags) {
return false;
}
virtual void notifySwitch(nsecs_t when,
int32_t switchCode, int32_t switchValue, uint32_t policyFlags) {
}
virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) {
}
virtual bool checkInjectEventsPermissionNonReentrant(
int32_t injectorPid, int32_t injectorUid) {
return false;
}
};
TEST_F(InputDispatcherTest, Dummy) {
// TODO
// --- InputDispatcherTest ---
class InputDispatcherTest : public testing::Test {
protected:
sp<FakeInputDispatcherPolicy> mFakePolicy;
sp<InputDispatcher> mDispatcher;
virtual void SetUp() {
mFakePolicy = new FakeInputDispatcherPolicy();
mDispatcher = new InputDispatcher(mFakePolicy);
}
virtual void TearDown() {
mFakePolicy.clear();
mDispatcher.clear();
}
};
TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) {
KeyEvent event;
// Rejects undefined key actions.
event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
/*action*/ -1, 0,
AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
<< "Should reject key events with undefined action.";
// Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
AKEY_EVENT_ACTION_MULTIPLE, 0,
AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
<< "Should reject key events with ACTION_MULTIPLE.";
}
TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) {
MotionEvent event;
int32_t pointerIds[MAX_POINTERS + 1];
PointerCoords pointerCoords[MAX_POINTERS + 1];
for (int i = 0; i <= MAX_POINTERS; i++) {
pointerIds[i] = i;
}
// Rejects undefined motion actions.
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
/*action*/ -1, 0, 0, AMETA_NONE, 0, 0, 0, 0,
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
<< "Should reject motion events with undefined action.";
// Rejects pointer down with invalid index.
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, AMETA_NONE, 0, 0, 0, 0,
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
<< "Should reject motion events with pointer down index too large.";
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
AMOTION_EVENT_ACTION_POINTER_DOWN | (-1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, AMETA_NONE, 0, 0, 0, 0,
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
<< "Should reject motion events with pointer down index too small.";
// Rejects pointer up with invalid index.
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, AMETA_NONE, 0, 0, 0, 0,
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
<< "Should reject motion events with pointer up index too large.";
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
AMOTION_EVENT_ACTION_POINTER_UP | (-1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
0, 0, AMETA_NONE, 0, 0, 0, 0,
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
<< "Should reject motion events with pointer up index too small.";
// Rejects motion events with invalid number of pointers.
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0,
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 0, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
<< "Should reject motion events with 0 pointers.";
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0,
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ MAX_POINTERS + 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
<< "Should reject motion events with more than MAX_POINTERS pointers.";
// Rejects motion events with invalid pointer ids.
pointerIds[0] = -1;
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0,
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
<< "Should reject motion events with pointer ids less than 0.";
pointerIds[0] = MAX_POINTER_ID + 1;
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0,
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 1, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
<< "Should reject motion events with pointer ids greater than MAX_POINTER_ID.";
// Rejects motion events with duplicate pointer ids.
pointerIds[0] = 1;
pointerIds[1] = 1;
event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0,
ARBITRARY_TIME, ARBITRARY_TIME,
/*pointerCount*/ 2, pointerIds, pointerCoords);
ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event,
INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0))
<< "Should reject motion events with duplicate pointer ids.";
}
} // namespace android

File diff suppressed because it is too large Load Diff