From f4a4ec2063dfd28e04bbfe712f67acee4bdc8e37 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 16 Jun 2010 01:53:36 -0700 Subject: [PATCH] Even more native input dispatch work in progress. Added more tests. Fixed a regression in Vector. Fixed bugs in pointer tracking. Fixed a starvation issue in PollLoop when setting or removing callbacks. Fixed a couple of policy nits. Modified the internal representation of MotionEvent to be more efficient and more consistent. Added code to skip/cancel virtual key processing when there are multiple pointers down. This helps to better disambiguate virtual key presses from stray touches (such as cheek presses). Change-Id: I2a7d2cce0195afb9125b23378baa94fd2fc6671c --- include/ui/Input.h | 49 ++- include/ui/InputTransport.h | 8 +- include/utils/PollLoop.h | 4 +- include/utils/Vector.h | 14 +- include/utils/VectorImpl.h | 10 +- libs/ui/Input.cpp | 45 ++- libs/ui/InputDispatcher.cpp | 3 +- libs/ui/InputReader.cpp | 113 ++++-- libs/ui/InputTransport.cpp | 38 +- libs/ui/tests/Android.mk | 4 +- libs/ui/tests/InputChannel_test.cpp | 158 ++++++++ libs/ui/tests/InputDispatcher_test.cpp | 3 +- libs/ui/tests/InputPublisherAndConsumer_test.cpp | 449 +++++++++++++++++++++++ libs/utils/PollLoop.cpp | 16 +- libs/utils/VectorImpl.cpp | 22 +- libs/utils/tests/PollLoop_test.cpp | 42 +-- libs/utils/tests/TestHelpers.h | 35 ++ 17 files changed, 881 insertions(+), 132 deletions(-) create mode 100644 libs/ui/tests/InputChannel_test.cpp create mode 100644 libs/ui/tests/InputPublisherAndConsumer_test.cpp diff --git a/include/ui/Input.h b/include/ui/Input.h index d45bfcfda9..92ff87209e 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -148,6 +148,9 @@ private: int32_t mNature; }; +/* + * Key events. + */ class KeyEvent : public InputEvent { public: virtual ~KeyEvent() { } @@ -193,6 +196,9 @@ private: nsecs_t mEventTime; }; +/* + * Motion events. + */ class MotionEvent : public InputEvent { public: virtual ~MotionEvent() { } @@ -205,6 +211,10 @@ public: inline int32_t getMetaState() const { return mMetaState; } + inline float getXOffset() const { return mXOffset; } + + inline float getYOffset() const { return mYOffset; } + inline float getXPrecision() const { return mXPrecision; } inline float getYPrecision() const { return mYPrecision; } @@ -217,16 +227,20 @@ public: inline nsecs_t getEventTime() const { return mSampleEventTimes[getHistorySize()]; } - inline float getRawX() const { return mRawX; } + inline float getRawX(size_t pointerIndex) const { + return getCurrentPointerCoords(pointerIndex).x; + } - inline float getRawY() const { return mRawY; } + inline float getRawY(size_t pointerIndex) const { + return getCurrentPointerCoords(pointerIndex).y; + } inline float getX(size_t pointerIndex) const { - return getCurrentPointerCoords(pointerIndex).x; + return getRawX(pointerIndex) + mXOffset; } inline float getY(size_t pointerIndex) const { - return getCurrentPointerCoords(pointerIndex).y; + return getRawY(pointerIndex) + mYOffset; } inline float getPressure(size_t pointerIndex) const { @@ -243,14 +257,22 @@ public: return mSampleEventTimes[historicalIndex]; } - inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const { + inline float getHistoricalRawX(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalPointerCoords(pointerIndex, historicalIndex).x; } - inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const { + inline float getHistoricalRawY(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalPointerCoords(pointerIndex, historicalIndex).y; } + inline float getHistoricalX(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalRawX(pointerIndex, historicalIndex) + mXOffset; + } + + inline float getHistoricalY(size_t pointerIndex, size_t historicalIndex) const { + return getHistoricalRawY(pointerIndex, historicalIndex) + mYOffset; + } + inline float getHistoricalPressure(size_t pointerIndex, size_t historicalIndex) const { return getHistoricalPointerCoords(pointerIndex, historicalIndex).pressure; } @@ -265,8 +287,8 @@ public: int32_t action, int32_t edgeFlags, int32_t metaState, - float rawX, - float rawY, + float xOffset, + float yOffset, float xPrecision, float yPrecision, nsecs_t downTime, @@ -281,12 +303,19 @@ public: void offsetLocation(float xOffset, float yOffset); + // Low-level accessors. + inline const int32_t* getPointerIds() const { return mPointerIds.array(); } + inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); } + inline const PointerCoords* getSamplePointerCoords() const { + return mSamplePointerCoords.array(); + } + private: int32_t mAction; int32_t mEdgeFlags; int32_t mMetaState; - float mRawX; - float mRawY; + float mXOffset; + float mYOffset; float mXPrecision; float mYPrecision; nsecs_t mDownTime; diff --git a/include/ui/InputTransport.h b/include/ui/InputTransport.h index 9537523832..7b182f384a 100644 --- a/include/ui/InputTransport.h +++ b/include/ui/InputTransport.h @@ -62,7 +62,7 @@ public: * Returns OK on success. */ static status_t openInputChannelPair(const String8& name, - InputChannel** outServerChannel, InputChannel** outClientChannel); + sp& outServerChannel, sp& outClientChannel); inline String8 getName() const { return mName; } inline int32_t getAshmemFd() const { return mAshmemFd; } @@ -72,7 +72,8 @@ public: /* Sends a signal to the other endpoint. * * Returns OK on success. - * Errors probably indicate that the channel is broken. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Other errors probably indicate that the channel is broken. */ status_t sendSignal(char signal); @@ -81,6 +82,7 @@ public: * * Returns OK on success. * Returns WOULD_BLOCK if there is no signal present. + * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ status_t receiveSignal(char* outSignal); @@ -298,7 +300,7 @@ public: * Returns INVALID_OPERATION if there is no currently published event. * Returns NO_MEMORY if the event could not be created. */ - status_t consume(InputEventFactoryInterface* factory, InputEvent** event); + status_t consume(InputEventFactoryInterface* factory, InputEvent** outEvent); /* Sends a finished signal to the publisher to inform it that the current message is * finished processing. diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h index c9d951f18a..a95fb171cf 100644 --- a/include/utils/PollLoop.h +++ b/include/utils/PollLoop.h @@ -114,8 +114,10 @@ private: }; Mutex mLock; - Condition mAwake; bool mPolling; + uint32_t mWaiters; + Condition mAwake; + Condition mResume; int mWakeReadPipeFd; int mWakeWritePipeFd; diff --git a/include/utils/Vector.h b/include/utils/Vector.h index d40ae16648..ec851bd0b5 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -115,10 +115,10 @@ public: //! insert an array at a given index - ssize_t insertArrayAt(const TYPE* array, size_t index, size_t numItems); + ssize_t insertArrayAt(const TYPE* array, size_t index, size_t length); //! append an array at the end of this vector - ssize_t appendArray(const TYPE* array, size_t numItems); + ssize_t appendArray(const TYPE* array, size_t length); /*! * add/insert/replace items @@ -126,7 +126,7 @@ public: //! insert one or several items initialized with their default constructor inline ssize_t insertAt(size_t index, size_t numItems = 1); - //! insert on onr several items initialized from a prototype item + //! insert one or several items initialized from a prototype item ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1); //! pop the top of the stack (removes the last element). No-op if the stack's empty inline void pop(); @@ -265,13 +265,13 @@ ssize_t Vector::appendVector(const Vector& vector) { } template inline -ssize_t Vector::insertArrayAt(const TYPE* array, size_t index, size_t numItems) { - return VectorImpl::insertAt(array, index, numItems); +ssize_t Vector::insertArrayAt(const TYPE* array, size_t index, size_t length) { + return VectorImpl::insertArrayAt(array, index, length); } template inline -ssize_t Vector::appendArray(const TYPE* array, size_t numItems) { - return VectorImpl::add(array, numItems); +ssize_t Vector::appendArray(const TYPE* array, size_t length) { + return VectorImpl::appendArray(array, length); } template inline diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h index 46a7bc2655..c4ec2ff97f 100644 --- a/include/utils/VectorImpl.h +++ b/include/utils/VectorImpl.h @@ -65,9 +65,11 @@ public: size_t capacity() const; ssize_t setCapacity(size_t size); - /*! append/insert another vector */ + /*! append/insert another vector or array */ ssize_t insertVectorAt(const VectorImpl& vector, size_t index); ssize_t appendVector(const VectorImpl& vector); + ssize_t insertArrayAt(const void* array, size_t index, size_t length); + ssize_t appendArray(const void* array, size_t length); /*! add/insert/replace items */ ssize_t insertAt(size_t where, size_t numItems = 1); @@ -76,7 +78,7 @@ public: void push(); void push(const void* item); ssize_t add(); - ssize_t add(const void* item, size_t numItems = 1); + ssize_t add(const void* item); ssize_t replaceAt(size_t index); ssize_t replaceAt(const void* item, size_t index); @@ -184,8 +186,8 @@ private: void push(const void* item); ssize_t insertVectorAt(const VectorImpl& vector, size_t index); ssize_t appendVector(const VectorImpl& vector); - ssize_t insertArrayAt(const void* array, size_t index, size_t numItems); - ssize_t appendArray(const void* array, size_t numItems); + ssize_t insertArrayAt(const void* array, size_t index, size_t length); + ssize_t appendArray(const void* array, size_t length); ssize_t insertAt(size_t where, size_t numItems = 1); ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); ssize_t replaceAt(size_t index); diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index d367708713..0e6f2f53ee 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -50,8 +50,8 @@ void MotionEvent::initialize( int32_t action, int32_t edgeFlags, int32_t metaState, - float rawX, - float rawY, + float xOffset, + float yOffset, float xPrecision, float yPrecision, nsecs_t downTime, @@ -63,8 +63,8 @@ void MotionEvent::initialize( mAction = action; mEdgeFlags = edgeFlags; mMetaState = metaState; - mRawX = rawX; - mRawY = rawY; + mXOffset = xOffset; + mYOffset = yOffset; mXPrecision = xPrecision; mYPrecision = yPrecision; mDownTime = downTime; @@ -83,13 +83,8 @@ void MotionEvent::addSample( } void MotionEvent::offsetLocation(float xOffset, float yOffset) { - if (xOffset != 0 || yOffset != 0) { - for (size_t i = 0; i < mSamplePointerCoords.size(); i++) { - PointerCoords& pointerCoords = mSamplePointerCoords.editItemAt(i); - pointerCoords.x += xOffset; - pointerCoords.y += yOffset; - } - } + mXOffset += xOffset; + mYOffset += yOffset; } } // namespace android @@ -163,6 +158,14 @@ int64_t motion_event_get_event_time(const input_event_t* motion_event) { return reinterpret_cast(motion_event)->getEventTime(); } +float motion_event_get_x_offset(const input_event_t* motion_event) { + return reinterpret_cast(motion_event)->getXOffset(); +} + +float motion_event_get_y_offset(const input_event_t* motion_event) { + return reinterpret_cast(motion_event)->getYOffset(); +} + float motion_event_get_x_precision(const input_event_t* motion_event) { return reinterpret_cast(motion_event)->getXPrecision(); } @@ -179,12 +182,12 @@ int32_t motion_event_get_pointer_id(const input_event_t* motion_event, size_t po return reinterpret_cast(motion_event)->getPointerId(pointer_index); } -float motion_event_get_raw_x(const input_event_t* motion_event) { - return reinterpret_cast(motion_event)->getRawX(); +float motion_event_get_raw_x(const input_event_t* motion_event, size_t pointer_index) { + return reinterpret_cast(motion_event)->getRawX(pointer_index); } -float motion_event_get_raw_y(const input_event_t* motion_event) { - return reinterpret_cast(motion_event)->getRawY(); +float motion_event_get_raw_y(const input_event_t* motion_event, size_t pointer_index) { + return reinterpret_cast(motion_event)->getRawY(pointer_index); } float motion_event_get_x(const input_event_t* motion_event, size_t pointer_index) { @@ -213,6 +216,18 @@ int64_t motion_event_get_historical_event_time(input_event_t* motion_event, history_index); } +float motion_event_get_historical_raw_x(input_event_t* motion_event, size_t pointer_index, + size_t history_index) { + return reinterpret_cast(motion_event)->getHistoricalRawX( + pointer_index, history_index); +} + +float motion_event_get_historical_raw_y(input_event_t* motion_event, size_t pointer_index, + size_t history_index) { + return reinterpret_cast(motion_event)->getHistoricalRawY( + pointer_index, history_index); +} + float motion_event_get_historical_x(input_event_t* motion_event, size_t pointer_index, size_t history_index) { return reinterpret_cast(motion_event)->getHistoricalX( diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index f058271bda..14dcada3b5 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -379,8 +379,7 @@ void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible( mReusableMotionEvent.initialize(entry->deviceId, entry->nature, entry->action, entry->edgeFlags, entry->metaState, - entry->firstSample.pointerCoords[0].x, entry->firstSample.pointerCoords[0].y, - entry->xPrecision, entry->yPrecision, + 0, 0, entry->xPrecision, entry->yPrecision, entry->downTime, entry->eventTime, entry->pointerCount, entry->pointerIds, entry->firstSample.pointerCoords); diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index 62b5f288a3..5a280aed37 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -19,6 +19,9 @@ // Log debug messages about pointers. #define DEBUG_POINTERS 1 +// Log debug messages about pointer assignment calculations. +#define DEBUG_POINTER_ASSIGNMENT 0 + #include #include @@ -57,6 +60,14 @@ inline static T min(const T& a, const T& b) { return a < b ? a : b; } +template +inline static void swap(T& a, T& b) { + T temp = a; + a = b; + b = temp; +} + + int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) { int32_t mask; switch (keyCode) { @@ -188,6 +199,12 @@ void InputDevice::TouchScreenState::reset() { jumpyTouchFilter.jumpyPointsDropped = 0; } +struct PointerDistanceHeapElement { + uint32_t currentPointerIndex : 8; + uint32_t lastPointerIndex : 8; + uint64_t distance : 48; // squared distance +}; + void InputDevice::TouchScreenState::calculatePointerIds() { uint32_t currentPointerCount = currentTouch.pointerCount; uint32_t lastPointerCount = lastTouch.pointerCount; @@ -214,11 +231,7 @@ void InputDevice::TouchScreenState::calculatePointerIds() { // We build a heap of squared euclidean distances between current and last pointers // associated with the current and last pointer indices. Then, we find the best // match (by distance) for each current pointer. - struct { - uint32_t currentPointerIndex : 8; - uint32_t lastPointerIndex : 8; - uint64_t distance : 48; // squared distance - } heap[MAX_POINTERS * MAX_POINTERS]; + PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; uint32_t heapSize = 0; for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; @@ -233,23 +246,45 @@ void InputDevice::TouchScreenState::calculatePointerIds() { uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); // Insert new element into the heap (sift up). + heap[heapSize].currentPointerIndex = currentPointerIndex; + heap[heapSize].lastPointerIndex = lastPointerIndex; + heap[heapSize].distance = distance; heapSize += 1; - uint32_t insertionIndex = heapSize; - while (insertionIndex > 1) { - uint32_t parentIndex = (insertionIndex - 1) / 2; - if (distance < heap[parentIndex].distance) { - heap[insertionIndex] = heap[parentIndex]; - insertionIndex = parentIndex; - } else { - break; - } + } + } + + // Heapify + for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) { + startIndex -= 1; + for (uint32_t parentIndex = startIndex; ;) { + uint32_t childIndex = parentIndex * 2 + 1; + if (childIndex >= heapSize) { + break; + } + + if (childIndex + 1 < heapSize + && heap[childIndex + 1].distance < heap[childIndex].distance) { + childIndex += 1; } - heap[insertionIndex].currentPointerIndex = currentPointerIndex; - heap[insertionIndex].lastPointerIndex = lastPointerIndex; - heap[insertionIndex].distance = distance; + + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } + + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; } } +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld", + i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, + heap[i].distance); + } +#endif + // Pull matches out by increasing order of distance. // To avoid reassigning pointers that have already been matched, the loop keeps track // of which last and current pointers have been matched using the matchedXXXBits variables. @@ -262,7 +297,7 @@ void InputDevice::TouchScreenState::calculatePointerIds() { for (;;) { if (first) { // The first time through the loop, we just consume the root element of - // the heap (the one with smalled distance). + // the heap (the one with smallest distance). first = false; } else { // Previous iterations consumed the root element of the heap. @@ -270,10 +305,10 @@ void InputDevice::TouchScreenState::calculatePointerIds() { heapSize -= 1; assert(heapSize > 0); - // Sift down to find where the element at index heapSize needs to be moved. - uint32_t rootIndex = 0; - for (;;) { - uint32_t childIndex = rootIndex * 2 + 1; + // Sift down. + heap[0] = heap[heapSize]; + for (uint32_t parentIndex = 0; ;) { + uint32_t childIndex = parentIndex * 2 + 1; if (childIndex >= heapSize) { break; } @@ -283,14 +318,22 @@ void InputDevice::TouchScreenState::calculatePointerIds() { childIndex += 1; } - if (heap[heapSize].distance < heap[childIndex].distance) { + if (heap[parentIndex].distance <= heap[childIndex].distance) { break; } - heap[rootIndex] = heap[childIndex]; - rootIndex = childIndex; + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; } - heap[rootIndex] = heap[heapSize]; + +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld", + i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, + heap[i].distance); + } +#endif } uint32_t currentPointerIndex = heap[0].currentPointerIndex; @@ -306,6 +349,11 @@ void InputDevice::TouchScreenState::calculatePointerIds() { currentTouch.pointers[currentPointerIndex].id = id; currentTouch.idToIndex[id] = currentPointerIndex; usedIdBits.markBit(id); + +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld", + lastPointerIndex, currentPointerIndex, id, heap[0].distance); +#endif break; } } @@ -320,6 +368,11 @@ void InputDevice::TouchScreenState::calculatePointerIds() { currentTouch.idToIndex[id] = currentPointerIndex; usedIdBits.markBit(id); +#if DEBUG_POINTER_ASSIGNMENT + LOGD("calculatePointerIds - assigned: cur=%d, id=%d", + currentPointerIndex, id); +#endif + if (--i == 0) break; // done matchedCurrentBits.markBit(currentPointerIndex); } @@ -1208,8 +1261,10 @@ bool InputReader::consumeVirtualKeyTouches(nsecs_t when, int32_t x = device->touchScreen.currentTouch.pointers[0].x; int32_t y = device->touchScreen.currentTouch.pointers[0].y; - if (device->touchScreen.isPointInsideDisplay(x, y)) { - // Pointer moved inside the display area. Send key cancellation. + if (device->touchScreen.isPointInsideDisplay(x, y) + || device->touchScreen.currentTouch.pointerCount != 1) { + // Pointer moved inside the display area or another pointer also went down. + // Send key cancellation. device->touchScreen.currentVirtualKey.down = false; #if DEBUG_VIRTUAL_KEYS @@ -1227,7 +1282,7 @@ bool InputReader::consumeVirtualKeyTouches(nsecs_t when, device->touchScreen.lastTouch.clear(); return false; // not consumed } - } else if (device->touchScreen.currentTouch.pointerCount > 0 + } else if (device->touchScreen.currentTouch.pointerCount == 1 && device->touchScreen.lastTouch.pointerCount == 0) { int32_t x = device->touchScreen.currentTouch.pointers[0].x; int32_t y = device->touchScreen.currentTouch.pointers[0].y; diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp index a24180fca5..86bbd3782d 100644 --- a/libs/ui/InputTransport.cpp +++ b/libs/ui/InputTransport.cpp @@ -8,13 +8,13 @@ //#define LOG_NDEBUG 0 // Log debug messages about channel signalling (send signal, receive signal) -#define DEBUG_CHANNEL_SIGNALS 1 +#define DEBUG_CHANNEL_SIGNALS 0 // Log debug messages whenever InputChannel objects are created/destroyed -#define DEBUG_CHANNEL_LIFECYCLE 1 +#define DEBUG_CHANNEL_LIFECYCLE 0 // Log debug messages about transport actions (initialize, reset, publish, ...) -#define DEBUG_TRANSPORT_ACTIONS 1 +#define DEBUG_TRANSPORT_ACTIONS 0 #include @@ -70,7 +70,7 @@ InputChannel::~InputChannel() { } status_t InputChannel::openInputChannelPair(const String8& name, - InputChannel** outServerChannel, InputChannel** outClientChannel) { + sp& outServerChannel, sp& outClientChannel) { status_t result; int serverAshmemFd = ashmem_create_region(name.string(), DEFAULT_MESSAGE_BUFFER_SIZE); @@ -107,12 +107,12 @@ status_t InputChannel::openInputChannelPair(const String8& name, } else { String8 serverChannelName = name; serverChannelName.append(" (server)"); - *outServerChannel = new InputChannel(serverChannelName, + outServerChannel = new InputChannel(serverChannelName, serverAshmemFd, reverse[0], forward[1]); String8 clientChannelName = name; clientChannelName.append(" (client)"); - *outClientChannel = new InputChannel(clientChannelName, + outClientChannel = new InputChannel(clientChannelName, clientAshmemFd, forward[0], reverse[1]); return OK; } @@ -125,8 +125,8 @@ status_t InputChannel::openInputChannelPair(const String8& name, ::close(serverAshmemFd); } - *outServerChannel = NULL; - *outClientChannel = NULL; + outServerChannel.clear(); + outClientChannel.clear(); return result; } @@ -155,6 +155,13 @@ status_t InputChannel::receiveSignal(char* outSignal) { return OK; } + if (nRead == 0) { // check for EOF +#if DEBUG_CHANNEL_SIGNALS + LOGD("channel '%s' ~ receive signal failed because peer was closed", mName.string()); +#endif + return DEAD_OBJECT; + } + if (errno == EAGAIN) { #if DEBUG_CHANNEL_SIGNALS LOGD("channel '%s' ~ receive signal failed because no signal available", mName.string()); @@ -535,13 +542,13 @@ status_t InputConsumer::initialize() { return OK; } -status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** event) { +status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) { #if DEBUG_TRANSPORT_ACTIONS LOGD("channel '%s' consumer ~ consume", mChannel->getName().string()); #endif - *event = NULL; + *outEvent = NULL; int ashmemFd = mChannel->getAshmemFd(); int result = ashmem_pin_region(ashmemFd, 0, 0); @@ -583,7 +590,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent* populateKeyEvent(keyEvent); - *event = keyEvent; + *outEvent = keyEvent; break; } @@ -593,7 +600,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent* populateMotionEvent(motionEvent); - *event = motionEvent; + *outEvent = motionEvent; break; } @@ -655,8 +662,8 @@ void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const { mSharedMessage->motion.action, mSharedMessage->motion.edgeFlags, mSharedMessage->motion.metaState, - mSharedMessage->motion.sampleData[0].coords[0].x, - mSharedMessage->motion.sampleData[0].coords[0].y, + mSharedMessage->motion.xOffset, + mSharedMessage->motion.yOffset, mSharedMessage->motion.xPrecision, mSharedMessage->motion.yPrecision, mSharedMessage->motion.downTime, @@ -676,9 +683,6 @@ void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const { motionEvent->addSample(sampleData->eventTime, sampleData->coords); } } - - motionEvent->offsetLocation(mSharedMessage->motion.xOffset, - mSharedMessage->motion.yOffset); } } // namespace android diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk index 1ff896baf7..46d74934de 100644 --- a/libs/ui/tests/Android.mk +++ b/libs/ui/tests/Android.mk @@ -3,7 +3,9 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) test_src_files := \ - InputDispatcher_test.cpp + InputChannel_test.cpp \ + InputDispatcher_test.cpp \ + InputPublisherAndConsumer_test.cpp shared_libraries := \ libcutils \ diff --git a/libs/ui/tests/InputChannel_test.cpp b/libs/ui/tests/InputChannel_test.cpp new file mode 100644 index 0000000000..6cec1c02ea --- /dev/null +++ b/libs/ui/tests/InputChannel_test.cpp @@ -0,0 +1,158 @@ +// +// Copyright 2010 The Android Open Source Project +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../utils/tests/TestHelpers.h" + +namespace android { + +class InputChannelTest : public testing::Test { +protected: + virtual void SetUp() { } + virtual void TearDown() { } +}; + + +TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) { + // Our purpose here is to verify that the input channel destructor closes the + // file descriptors provided to it. One easy way is to provide it with one end + // of a pipe and to check for EPIPE on the other end after the channel is destroyed. + Pipe fakeAshmem, sendPipe, receivePipe; + + sp inputChannel = new InputChannel(String8("channel name"), + fakeAshmem.sendFd, receivePipe.receiveFd, sendPipe.sendFd); + + EXPECT_STREQ("channel name", inputChannel->getName().string()) + << "channel should have provided name"; + EXPECT_EQ(fakeAshmem.sendFd, inputChannel->getAshmemFd()) + << "channel should have provided ashmem fd"; + EXPECT_EQ(receivePipe.receiveFd, inputChannel->getReceivePipeFd()) + << "channel should have provided receive pipe fd"; + EXPECT_EQ(sendPipe.sendFd, inputChannel->getSendPipeFd()) + << "channel should have provided send pipe fd"; + + inputChannel.clear(); // destroys input channel + + EXPECT_EQ(-EPIPE, fakeAshmem.readSignal()) + << "channel should have closed ashmem fd when destroyed"; + EXPECT_EQ(-EPIPE, receivePipe.writeSignal()) + << "channel should have closed receive pipe fd when destroyed"; + EXPECT_EQ(-EPIPE, sendPipe.readSignal()) + << "channel should have closed send pipe fd when destroyed"; + + // clean up fds of Pipe endpoints that were closed so we don't try to close them again + fakeAshmem.sendFd = -1; + receivePipe.receiveFd = -1; + sendPipe.sendFd = -1; +} + +TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { + sp serverChannel, clientChannel; + + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + ASSERT_EQ(OK, result) + << "should have successfully opened a channel pair"; + + // Name + EXPECT_STREQ("channel name (server)", serverChannel->getName().string()) + << "server channel should have suffixed name"; + EXPECT_STREQ("channel name (client)", clientChannel->getName().string()) + << "client channel should have suffixed name"; + + // Ashmem uniqueness + EXPECT_NE(serverChannel->getAshmemFd(), clientChannel->getAshmemFd()) + << "server and client channel should have different ashmem fds because it was dup'd"; + + // Ashmem usability + ssize_t serverAshmemSize = ashmem_get_size_region(serverChannel->getAshmemFd()); + ssize_t clientAshmemSize = ashmem_get_size_region(clientChannel->getAshmemFd()); + uint32_t* serverAshmem = static_cast(mmap(NULL, serverAshmemSize, + PROT_READ | PROT_WRITE, MAP_SHARED, serverChannel->getAshmemFd(), 0)); + uint32_t* clientAshmem = static_cast(mmap(NULL, clientAshmemSize, + PROT_READ | PROT_WRITE, MAP_SHARED, clientChannel->getAshmemFd(), 0)); + ASSERT_TRUE(serverAshmem != NULL) + << "server channel ashmem should be mappable"; + ASSERT_TRUE(clientAshmem != NULL) + << "client channel ashmem should be mappable"; + *serverAshmem = 0xf00dd00d; + EXPECT_EQ(0xf00dd00d, *clientAshmem) + << "ashmem buffer should be shared by client and server"; + munmap(serverAshmem, serverAshmemSize); + munmap(clientAshmem, clientAshmemSize); + + // Server->Client communication + EXPECT_EQ(OK, serverChannel->sendSignal('S')) + << "server channel should be able to send signal to client channel"; + char signal; + EXPECT_EQ(OK, clientChannel->receiveSignal(& signal)) + << "client channel should be able to receive signal from server channel"; + EXPECT_EQ('S', signal) + << "client channel should receive the correct signal from server channel"; + + // Client->Server communication + EXPECT_EQ(OK, clientChannel->sendSignal('c')) + << "client channel should be able to send signal to server channel"; + EXPECT_EQ(OK, serverChannel->receiveSignal(& signal)) + << "server channel should be able to receive signal from client channel"; + EXPECT_EQ('c', signal) + << "server channel should receive the correct signal from client channel"; +} + +TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { + sp serverChannel, clientChannel; + + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + ASSERT_EQ(OK, result) + << "should have successfully opened a channel pair"; + + char signal; + EXPECT_EQ(WOULD_BLOCK, clientChannel->receiveSignal(& signal)) + << "receiveSignal should have returned WOULD_BLOCK"; +} + +TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) { + sp serverChannel, clientChannel; + + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + ASSERT_EQ(OK, result) + << "should have successfully opened a channel pair"; + + serverChannel.clear(); // close server channel + + char signal; + EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveSignal(& signal)) + << "receiveSignal should have returned DEAD_OBJECT"; +} + +TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { + sp serverChannel, clientChannel; + + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + ASSERT_EQ(OK, result) + << "should have successfully opened a channel pair"; + + serverChannel.clear(); // close server channel + + EXPECT_EQ(DEAD_OBJECT, clientChannel->sendSignal('S')) + << "sendSignal should have returned DEAD_OBJECT"; +} + + +} // namespace android diff --git a/libs/ui/tests/InputDispatcher_test.cpp b/libs/ui/tests/InputDispatcher_test.cpp index 3d920438a0..1dc6e46a45 100644 --- a/libs/ui/tests/InputDispatcher_test.cpp +++ b/libs/ui/tests/InputDispatcher_test.cpp @@ -12,8 +12,7 @@ public: }; TEST_F(InputDispatcherTest, Dummy) { - SCOPED_TRACE("Trace"); - ASSERT_FALSE(true); + // TODO } } // namespace android diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp new file mode 100644 index 0000000000..2d6b5312ff --- /dev/null +++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp @@ -0,0 +1,449 @@ +// +// Copyright 2010 The Android Open Source Project +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../utils/tests/TestHelpers.h" + +namespace android { + +class InputPublisherAndConsumerTest : public testing::Test { +protected: + sp serverChannel, clientChannel; + InputPublisher* mPublisher; + InputConsumer* mConsumer; + PreallocatedInputEventFactory mEventFactory; + + virtual void SetUp() { + status_t result = InputChannel::openInputChannelPair(String8("channel name"), + serverChannel, clientChannel); + + mPublisher = new InputPublisher(serverChannel); + mConsumer = new InputConsumer(clientChannel); + } + + virtual void TearDown() { + if (mPublisher) { + delete mPublisher; + mPublisher = NULL; + } + + if (mConsumer) { + delete mConsumer; + mConsumer = NULL; + } + + serverChannel.clear(); + clientChannel.clear(); + } + + void Initialize(); + void PublishAndConsumeKeyEvent(); + void PublishAndConsumeMotionEvent( + size_t samplesToAppendBeforeDispatch = 0, + size_t samplesToAppendAfterDispatch = 0); +}; + +TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { + EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get()); + EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get()); +} + +void InputPublisherAndConsumerTest::Initialize() { + status_t status; + + status = mPublisher->initialize(); + ASSERT_EQ(OK, status) + << "publisher initialize should return OK"; + + status = mConsumer->initialize(); + ASSERT_EQ(OK, status) + << "consumer initialize should return OK"; +} + +void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { + status_t status; + + const int32_t deviceId = 1; + const int32_t nature = INPUT_EVENT_NATURE_KEY; + const int32_t action = KEY_EVENT_ACTION_DOWN; + const int32_t flags = KEY_EVENT_FLAG_FROM_SYSTEM; + const int32_t keyCode = KEYCODE_ENTER; + const int32_t scanCode = 13; + const int32_t metaState = META_ALT_LEFT_ON | META_ALT_ON; + const int32_t repeatCount = 1; + const nsecs_t downTime = 3; + const nsecs_t eventTime = 4; + + status = mPublisher->publishKeyEvent(deviceId, nature, action, flags, + keyCode, scanCode, metaState, repeatCount, downTime, eventTime); + ASSERT_EQ(OK, status) + << "publisher publishKeyEvent should return OK"; + + status = mPublisher->sendDispatchSignal(); + ASSERT_EQ(OK, status) + << "publisher sendDispatchSignal should return OK"; + + status = mConsumer->receiveDispatchSignal(); + ASSERT_EQ(OK, status) + << "consumer receiveDispatchSignal should return OK"; + + InputEvent* event; + status = mConsumer->consume(& mEventFactory, & event); + ASSERT_EQ(OK, status) + << "consumer consume should return OK"; + + ASSERT_TRUE(event != NULL) + << "consumer should have returned non-NULL event"; + ASSERT_EQ(INPUT_EVENT_TYPE_KEY, event->getType()) + << "consumer should have returned a key event"; + + KeyEvent* keyEvent = static_cast(event); + EXPECT_EQ(deviceId, keyEvent->getDeviceId()); + EXPECT_EQ(nature, keyEvent->getNature()); + EXPECT_EQ(action, keyEvent->getAction()); + EXPECT_EQ(flags, keyEvent->getFlags()); + EXPECT_EQ(keyCode, keyEvent->getKeyCode()); + EXPECT_EQ(scanCode, keyEvent->getScanCode()); + EXPECT_EQ(metaState, keyEvent->getMetaState()); + EXPECT_EQ(repeatCount, keyEvent->getRepeatCount()); + EXPECT_EQ(downTime, keyEvent->getDownTime()); + EXPECT_EQ(eventTime, keyEvent->getEventTime()); + + status = mConsumer->sendFinishedSignal(); + ASSERT_EQ(OK, status) + << "consumer sendFinishedSignal should return OK"; + + status = mPublisher->receiveFinishedSignal(); + ASSERT_EQ(OK, status) + << "publisher receiveFinishedSignal should return OK"; + + status = mPublisher->reset(); + ASSERT_EQ(OK, status) + << "publisher reset should return OK"; +} + +void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( + size_t samplesToAppendBeforeDispatch, size_t samplesToAppendAfterDispatch) { + status_t status; + + const int32_t deviceId = 1; + const int32_t nature = INPUT_EVENT_NATURE_TOUCH; + const int32_t action = MOTION_EVENT_ACTION_MOVE; + const int32_t edgeFlags = MOTION_EVENT_EDGE_FLAG_TOP; + const int32_t metaState = META_ALT_LEFT_ON | META_ALT_ON; + const float xOffset = -10; + const float yOffset = -20; + const float xPrecision = 0.25; + const float yPrecision = 0.5; + const nsecs_t downTime = 3; + const size_t pointerCount = 3; + const int32_t pointerIds[pointerCount] = { 2, 0, 1 }; + + Vector sampleEventTimes; + Vector samplePointerCoords; + + for (size_t i = 0; i <= samplesToAppendAfterDispatch + samplesToAppendBeforeDispatch; i++) { + sampleEventTimes.push(i + 10); + for (size_t j = 0; j < pointerCount; j++) { + samplePointerCoords.push(); + samplePointerCoords.editTop().x = 100 * i + j; + samplePointerCoords.editTop().y = 200 * i + j; + samplePointerCoords.editTop().pressure = 0.5 * i + j; + samplePointerCoords.editTop().size = 0.7 * i + j; + } + } + + status = mPublisher->publishMotionEvent(deviceId, nature, action, edgeFlags, + metaState, xOffset, yOffset, xPrecision, yPrecision, + downTime, sampleEventTimes[0], pointerCount, pointerIds, samplePointerCoords.array()); + ASSERT_EQ(OK, status) + << "publisher publishMotionEvent should return OK"; + + for (size_t i = 0; i < samplesToAppendBeforeDispatch; i++) { + size_t sampleIndex = i + 1; + status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex], + samplePointerCoords.array() + sampleIndex * pointerCount); + ASSERT_EQ(OK, status) + << "publisher appendMotionEvent should return OK"; + } + + status = mPublisher->sendDispatchSignal(); + ASSERT_EQ(OK, status) + << "publisher sendDispatchSignal should return OK"; + + for (size_t i = 0; i < samplesToAppendAfterDispatch; i++) { + size_t sampleIndex = i + 1 + samplesToAppendBeforeDispatch; + status = mPublisher->appendMotionSample(sampleEventTimes[sampleIndex], + samplePointerCoords.array() + sampleIndex * pointerCount); + ASSERT_EQ(OK, status) + << "publisher appendMotionEvent should return OK"; + } + + status = mConsumer->receiveDispatchSignal(); + ASSERT_EQ(OK, status) + << "consumer receiveDispatchSignal should return OK"; + + InputEvent* event; + status = mConsumer->consume(& mEventFactory, & event); + ASSERT_EQ(OK, status) + << "consumer consume should return OK"; + + ASSERT_TRUE(event != NULL) + << "consumer should have returned non-NULL event"; + ASSERT_EQ(INPUT_EVENT_TYPE_MOTION, event->getType()) + << "consumer should have returned a motion event"; + + size_t lastSampleIndex = samplesToAppendBeforeDispatch + samplesToAppendAfterDispatch; + + MotionEvent* motionEvent = static_cast(event); + EXPECT_EQ(deviceId, motionEvent->getDeviceId()); + EXPECT_EQ(nature, motionEvent->getNature()); + EXPECT_EQ(action, motionEvent->getAction()); + EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); + EXPECT_EQ(metaState, motionEvent->getMetaState()); + EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); + EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); + EXPECT_EQ(downTime, motionEvent->getDownTime()); + EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime()); + EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); + EXPECT_EQ(lastSampleIndex, motionEvent->getHistorySize()); + + for (size_t i = 0; i < pointerCount; i++) { + SCOPED_TRACE(i); + EXPECT_EQ(pointerIds[i], motionEvent->getPointerId(i)); + } + + for (size_t sampleIndex = 0; sampleIndex < lastSampleIndex; sampleIndex++) { + SCOPED_TRACE(sampleIndex); + EXPECT_EQ(sampleEventTimes[sampleIndex], + motionEvent->getHistoricalEventTime(sampleIndex)); + for (size_t i = 0; i < pointerCount; i++) { + SCOPED_TRACE(i); + size_t offset = sampleIndex * pointerCount + i; + EXPECT_EQ(samplePointerCoords[offset].x, + motionEvent->getHistoricalRawX(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].y, + motionEvent->getHistoricalRawY(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].x + xOffset, + motionEvent->getHistoricalX(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].y + yOffset, + motionEvent->getHistoricalY(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].pressure, + motionEvent->getHistoricalPressure(i, sampleIndex)); + EXPECT_EQ(samplePointerCoords[offset].size, + motionEvent->getHistoricalSize(i, sampleIndex)); + } + } + + SCOPED_TRACE(lastSampleIndex); + EXPECT_EQ(sampleEventTimes[lastSampleIndex], motionEvent->getEventTime()); + for (size_t i = 0; i < pointerCount; i++) { + SCOPED_TRACE(i); + size_t offset = lastSampleIndex * pointerCount + i; + EXPECT_EQ(samplePointerCoords[offset].x, motionEvent->getRawX(i)); + EXPECT_EQ(samplePointerCoords[offset].y, motionEvent->getRawY(i)); + EXPECT_EQ(samplePointerCoords[offset].x + xOffset, motionEvent->getX(i)); + EXPECT_EQ(samplePointerCoords[offset].y + yOffset, motionEvent->getY(i)); + EXPECT_EQ(samplePointerCoords[offset].pressure, motionEvent->getPressure(i)); + EXPECT_EQ(samplePointerCoords[offset].size, motionEvent->getSize(i)); + } + + status = mConsumer->sendFinishedSignal(); + ASSERT_EQ(OK, status) + << "consumer sendFinishedSignal should return OK"; + + status = mPublisher->receiveFinishedSignal(); + ASSERT_EQ(OK, status) + << "publisher receiveFinishedSignal should return OK"; + + status = mPublisher->reset(); + ASSERT_EQ(OK, status) + << "publisher reset should return OK"; +} + +TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(Initialize()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); +} + +TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_WhenNotReset_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + ASSERT_EQ(OK, status) + << "publisher publishKeyEvent should return OK first time"; + + status = mPublisher->publishKeyEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + ASSERT_EQ(INVALID_OPERATION, status) + << "publisher publishKeyEvent should return INVALID_OPERATION because " + "the publisher was not reset"; +} + +TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(Initialize()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); +} + +TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenNotReset_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + const size_t pointerCount = 1; + int32_t pointerIds[pointerCount] = { 0 }; + PointerCoords pointerCoords[pointerCount] = { { 0, 0, 0, 0 } }; + + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + pointerCount, pointerIds, pointerCoords); + ASSERT_EQ(OK, status) + << "publisher publishMotionEvent should return OK"; + + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + pointerCount, pointerIds, pointerCoords); + ASSERT_EQ(INVALID_OPERATION, status) + << "publisher publishMotionEvent should return INVALID_OPERATION because "; + "the publisher was not reset"; +} + +TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + const size_t pointerCount = 0; + int32_t pointerIds[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + pointerCount, pointerIds, pointerCoords); + ASSERT_EQ(BAD_VALUE, status) + << "publisher publishMotionEvent should return BAD_VALUE"; +} + +TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + const size_t pointerCount = MAX_POINTERS + 1; + int32_t pointerIds[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + pointerCount, pointerIds, pointerCoords); + ASSERT_EQ(BAD_VALUE, status) + << "publisher publishMotionEvent should return BAD_VALUE"; +} + +TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) { + ASSERT_NO_FATAL_FAILURE(Initialize()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent()); +} + +TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledBeforeDispatchSignal_AppendsSamples) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(3, 0)); +} + +TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenCalledAfterDispatchSignalAndNotConsumed_AppendsSamples) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent(0, 4)); +} + +TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenNoMotionEventPublished_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + PointerCoords pointerCoords[1]; + status = mPublisher->appendMotionSample(0, pointerCoords); + ASSERT_EQ(INVALID_OPERATION, status) + << "publisher appendMotionSample should return INVALID_OPERATION"; +} + +TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenPublishedMotionEventIsNotAMove_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + const size_t pointerCount = MAX_POINTERS; + int32_t pointerIds[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + status = mPublisher->publishMotionEvent(0, 0, MOTION_EVENT_ACTION_DOWN, + 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords); + ASSERT_EQ(OK, status); + + status = mPublisher->appendMotionSample(0, pointerCoords); + ASSERT_EQ(INVALID_OPERATION, status) + << "publisher appendMotionSample should return INVALID_OPERATION"; +} + +TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenAlreadyConsumed_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + const size_t pointerCount = MAX_POINTERS; + int32_t pointerIds[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + status = mPublisher->publishMotionEvent(0, 0, MOTION_EVENT_ACTION_MOVE, + 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords); + ASSERT_EQ(OK, status); + + status = mPublisher->sendDispatchSignal(); + ASSERT_EQ(OK, status); + + status = mConsumer->receiveDispatchSignal(); + ASSERT_EQ(OK, status); + + InputEvent* event; + status = mConsumer->consume(& mEventFactory, & event); + ASSERT_EQ(OK, status); + + status = mPublisher->appendMotionSample(0, pointerCoords); + ASSERT_EQ(status_t(FAILED_TRANSACTION), status) + << "publisher appendMotionSample should return FAILED_TRANSACTION"; +} + +TEST_F(InputPublisherAndConsumerTest, AppendMotionSample_WhenBufferFull_ReturnsError) { + status_t status; + ASSERT_NO_FATAL_FAILURE(Initialize()); + + const size_t pointerCount = MAX_POINTERS; + int32_t pointerIds[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + status = mPublisher->publishMotionEvent(0, 0, MOTION_EVENT_ACTION_MOVE, + 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords); + ASSERT_EQ(OK, status); + + for (int count = 1;; count++) { + ASSERT_LT(count, 100000) << "should eventually reach OOM"; + + status = mPublisher->appendMotionSample(0, pointerCoords); + if (status != OK) { + ASSERT_GT(count, 12) << "should be able to add at least a dozen samples"; + ASSERT_EQ(NO_MEMORY, status) + << "publisher appendMotionSample should return NO_MEMORY when buffer is full"; + break; + } + } + + status = mPublisher->appendMotionSample(0, pointerCoords); + ASSERT_EQ(NO_MEMORY, status) + << "publisher appendMotionSample should return NO_MEMORY persistently until reset"; +} + +} // namespace android diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp index 90a3e8b353..20a4d1385b 100644 --- a/libs/utils/PollLoop.cpp +++ b/libs/utils/PollLoop.cpp @@ -11,7 +11,7 @@ #define DEBUG_POLL_AND_WAKE 0 // Debugs callback registration and invocation. -#define DEBUG_CALLBACKS 1 +#define DEBUG_CALLBACKS 0 #include #include @@ -22,7 +22,7 @@ namespace android { PollLoop::PollLoop() : - mPolling(false) { + mPolling(false), mWaiters(0) { openWakePipe(); } @@ -68,6 +68,9 @@ void PollLoop::closeWakePipe() { bool PollLoop::pollOnce(int timeoutMillis) { mLock.lock(); + while (mWaiters != 0) { + mResume.wait(mLock); + } mPolling = true; mLock.unlock(); @@ -156,7 +159,9 @@ bool PollLoop::pollOnce(int timeoutMillis) { Done: mLock.lock(); mPolling = false; - mAwake.broadcast(); + if (mWaiters != 0) { + mAwake.broadcast(); + } mLock.unlock(); if (result) { @@ -258,10 +263,15 @@ ssize_t PollLoop::getRequestIndexLocked(int fd) { void PollLoop::wakeAndLock() { mLock.lock(); + mWaiters += 1; while (mPolling) { wake(); mAwake.wait(mLock); } + mWaiters -= 1; + if (mWaiters == 0) { + mResume.signal(); + } } } // namespace android diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index b09c6cae70..289c826d3e 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -108,7 +108,7 @@ size_t VectorImpl::capacity() const ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index) { - return insertAt(vector.arrayImpl(), index, vector.size()); + return insertArrayAt(vector.arrayImpl(), index, vector.size()); } ssize_t VectorImpl::appendVector(const VectorImpl& vector) @@ -116,6 +116,22 @@ ssize_t VectorImpl::appendVector(const VectorImpl& vector) return insertVectorAt(vector, size()); } +ssize_t VectorImpl::insertArrayAt(const void* array, size_t index, size_t length) +{ + if (index > size()) + return BAD_INDEX; + void* where = _grow(index, length); + if (where) { + _do_copy(where, array, length); + } + return where ? index : (ssize_t)NO_MEMORY; +} + +ssize_t VectorImpl::appendArray(const void* array, size_t length) +{ + return insertArrayAt(array, size(), length); +} + ssize_t VectorImpl::insertAt(size_t index, size_t numItems) { return insertAt(0, index, numItems); @@ -220,9 +236,9 @@ ssize_t VectorImpl::add() return add(0); } -ssize_t VectorImpl::add(const void* item, size_t numItems) +ssize_t VectorImpl::add(const void* item) { - return insertAt(item, size(), numItems); + return insertAt(item, size()); } ssize_t VectorImpl::replaceAt(size_t index) diff --git a/libs/utils/tests/PollLoop_test.cpp b/libs/utils/tests/PollLoop_test.cpp index 6c719c85c7..4848c0fa66 100644 --- a/libs/utils/tests/PollLoop_test.cpp +++ b/libs/utils/tests/PollLoop_test.cpp @@ -16,34 +16,6 @@ namespace android { -class Pipe { -public: - int sendFd; - int receiveFd; - - Pipe() { - int fds[2]; - ::pipe(fds); - - receiveFd = fds[0]; - sendFd = fds[1]; - } - - ~Pipe() { - ::close(sendFd); - ::close(receiveFd); - } - - bool writeSignal() { - return ::write(sendFd, "*", 1) == 1; - } - - bool readSignal() { - char buf[1]; - return ::read(receiveFd, buf, 1) == 1; - } -}; - class DelayedWake : public DelayedTask { sp mPollLoop; @@ -195,7 +167,7 @@ TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCa Pipe pipe; StubCallbackHandler handler(true); - ASSERT_TRUE(pipe.writeSignal()); + ASSERT_EQ(OK, pipe.writeSignal()); handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); StopWatch stopWatch("pollOnce"); @@ -243,7 +215,7 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_Imme bool result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - ASSERT_TRUE(pipe.readSignal()) + ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; @@ -269,7 +241,7 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_Promp bool result = mPollLoop->pollOnce(1000); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - ASSERT_TRUE(pipe.readSignal()) + ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal signal delay"; @@ -295,7 +267,7 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeIn bool result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - ASSERT_TRUE(pipe.readSignal()) + ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal timeout because FD was no longer registered"; @@ -318,7 +290,7 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvoke bool result = mPollLoop->pollOnce(0); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - ASSERT_TRUE(pipe.readSignal()) + ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal zero because FD was already signalled"; @@ -334,7 +306,7 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvoke result = mPollLoop->pollOnce(0); elapsedMillis = ns2ms(stopWatch.elapsedTime()); - ASSERT_TRUE(pipe.readSignal()) + ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal zero because timeout was zero"; @@ -382,7 +354,7 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeI bool result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - ASSERT_TRUE(pipe.readSignal()) + ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because FD was already signalled"; diff --git a/libs/utils/tests/TestHelpers.h b/libs/utils/tests/TestHelpers.h index e55af3ccd8..d8e985e9ce 100644 --- a/libs/utils/tests/TestHelpers.h +++ b/libs/utils/tests/TestHelpers.h @@ -21,6 +21,41 @@ namespace android { +class Pipe { +public: + int sendFd; + int receiveFd; + + Pipe() { + int fds[2]; + ::pipe(fds); + + receiveFd = fds[0]; + sendFd = fds[1]; + } + + ~Pipe() { + if (sendFd != -1) { + ::close(sendFd); + } + + if (receiveFd != -1) { + ::close(receiveFd); + } + } + + status_t writeSignal() { + ssize_t nWritten = ::write(sendFd, "*", 1); + return nWritten == 1 ? 0 : -errno; + } + + status_t readSignal() { + char buf[1]; + ssize_t nRead = ::read(receiveFd, buf, 1); + return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno; + } +}; + class DelayedTask : public Thread { int mDelayMillis; -- 2.11.0